summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk6
-rw-r--r--api/current.txt14
-rw-r--r--cmds/dumpstate/Android.mk19
-rw-r--r--cmds/dumpstate/dumpstate.c452
-rw-r--r--cmds/dumpstate/dumpstate.h62
-rw-r--r--cmds/dumpstate/utils.c470
-rw-r--r--cmds/dumpsys/Android.mk19
-rw-r--r--cmds/dumpsys/MODULE_LICENSE_APACHE20
-rw-r--r--cmds/dumpsys/NOTICE190
-rw-r--r--cmds/dumpsys/dumpsys.cpp85
-rw-r--r--cmds/installd/installd.c5
-rw-r--r--cmds/stagefright/Android.mk6
-rw-r--r--cmds/stagefright/codec.cpp58
-rw-r--r--cmds/stagefright/jpeg.cpp91
-rw-r--r--cmds/stagefright/jpeg.h6
-rw-r--r--cmds/stagefright/sf2.cpp5
-rw-r--r--cmds/stagefright/stagefright.cpp16
-rw-r--r--core/java/android/accounts/AccountManagerService.java30
-rw-r--r--core/java/android/app/ApplicationPackageManager.java17
-rw-r--r--core/java/android/content/Intent.java45
-rw-r--r--core/java/android/content/SyncManager.java23
-rw-r--r--core/java/android/content/SyncQueue.java13
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl2
-rw-r--r--core/java/android/content/pm/PackageManager.java18
-rw-r--r--core/java/android/content/pm/PackageParser.java97
-rw-r--r--core/java/android/database/sqlite/SQLiteSession.java8
-rw-r--r--core/java/android/net/SSLCertificateSocketFactory.java64
-rw-r--r--core/java/android/view/DisplayList.java24
-rw-r--r--core/java/android/view/FocusFinder.java82
-rw-r--r--core/java/android/view/GLES20Canvas.java8
-rw-r--r--core/java/android/view/HardwareCanvas.java16
-rw-r--r--core/java/android/view/HardwareRenderer.java10
-rw-r--r--core/java/android/view/View.java81
-rw-r--r--core/java/android/view/ViewGroup.java15
-rw-r--r--core/java/android/webkit/DebugFlags.java7
-rw-r--r--core/java/android/webkit/WebView.java24
-rw-r--r--core/java/android/webkit/WebViewClassic.java33
-rw-r--r--core/java/android/webkit/WebViewCore.java1
-rw-r--r--core/java/android/webkit/WebViewProvider.java2
-rw-r--r--core/java/android/widget/AdapterViewFlipper.java26
-rw-r--r--core/java/android/widget/ImageView.java133
-rw-r--r--core/java/android/widget/RadioGroup.java2
-rw-r--r--core/java/android/widget/RatingBar.java4
-rw-r--r--core/java/android/widget/TextView.java6
-rw-r--r--core/java/com/android/internal/util/StateMachine.java61
-rw-r--r--core/java/com/google/android/mms/pdu/PduPersister.java124
-rw-r--r--core/java/com/google/android/mms/util/DownloadDrmHelper.java111
-rw-r--r--core/java/com/google/android/mms/util/DrmConvertSession.java172
-rw-r--r--core/jni/android_view_GLES20Canvas.cpp22
-rw-r--r--core/res/AndroidManifest.xml21
-rw-r--r--core/res/res/values-am/strings.xml4
-rw-r--r--core/res/res/values-in/strings.xml6
-rw-r--r--core/res/res/values-zh-rTW/strings.xml3
-rw-r--r--core/tests/coretests/src/android/net/SSLTest.java40
-rw-r--r--data/etc/android.hardware.audio.low_latency.xml22
-rw-r--r--data/etc/android.hardware.bluetooth.xml19
-rw-r--r--data/etc/android.hardware.camera.autofocus.xml21
-rw-r--r--data/etc/android.hardware.camera.flash-autofocus.xml23
-rw-r--r--data/etc/android.hardware.camera.front.xml20
-rw-r--r--data/etc/android.hardware.camera.xml20
-rw-r--r--data/etc/android.hardware.faketouch.multitouch.distinct.xml23
-rw-r--r--data/etc/android.hardware.faketouch.xml22
-rw-r--r--data/etc/android.hardware.location.gps.xml22
-rw-r--r--data/etc/android.hardware.location.xml22
-rw-r--r--data/etc/android.hardware.nfc.xml21
-rw-r--r--data/etc/android.hardware.sensor.accelerometer.xml20
-rw-r--r--data/etc/android.hardware.sensor.barometer.xml20
-rw-r--r--data/etc/android.hardware.sensor.compass.xml20
-rw-r--r--data/etc/android.hardware.sensor.gyroscope.xml20
-rw-r--r--data/etc/android.hardware.sensor.light.xml20
-rw-r--r--data/etc/android.hardware.sensor.proximity.xml20
-rw-r--r--data/etc/android.hardware.telephony.cdma.xml21
-rw-r--r--data/etc/android.hardware.telephony.gsm.xml21
-rw-r--r--data/etc/android.hardware.touchscreen.multitouch.distinct.xml24
-rw-r--r--data/etc/android.hardware.touchscreen.multitouch.jazzhand.xml25
-rw-r--r--data/etc/android.hardware.touchscreen.multitouch.xml24
-rw-r--r--data/etc/android.hardware.touchscreen.xml22
-rw-r--r--data/etc/android.hardware.usb.accessory.xml22
-rw-r--r--data/etc/android.hardware.usb.host.xml21
-rw-r--r--data/etc/android.hardware.wifi.direct.xml20
-rw-r--r--data/etc/android.hardware.wifi.xml20
-rw-r--r--data/etc/android.software.sip.voip.xml21
-rw-r--r--data/etc/android.software.sip.xml20
-rw-r--r--data/etc/handheld_core_hardware.xml65
-rw-r--r--data/etc/tablet_core_hardware.xml55
-rw-r--r--docs/html/guide/appendix/api-levels.jd6
-rw-r--r--docs/html/guide/developing/building/building-cmdline.jd10
-rw-r--r--docs/html/guide/developing/devices/index.jd6
-rw-r--r--docs/html/guide/developing/devices/managing-avds.jd6
-rw-r--r--docs/html/guide/developing/projects/index.jd8
-rw-r--r--docs/html/guide/developing/tools/adt.jd5
-rw-r--r--docs/html/guide/developing/tools/android.jd29
-rw-r--r--docs/html/guide/developing/tools/index.jd4
-rwxr-xr-xdocs/html/guide/market/billing/billing_integrate.jd6
-rw-r--r--docs/html/guide/market/licensing/overview.jd11
-rw-r--r--docs/html/guide/market/licensing/setting-up.jd36
-rw-r--r--docs/html/guide/practices/optimizing-for-3.0.jd8
-rw-r--r--docs/html/guide/practices/screens_support.jd14
-rw-r--r--docs/html/guide/topics/location/index.jd2
-rw-r--r--docs/html/guide/topics/manifest/uses-feature-element.jd2
-rw-r--r--docs/html/guide/tutorials/views/hello-mapview.jd4
-rw-r--r--docs/html/images/efficient-downloads/DDMS.pngbin0 -> 27283 bytes
-rw-r--r--docs/html/images/efficient-downloads/graphs.pngbin0 -> 16550 bytes
-rw-r--r--docs/html/images/efficient-downloads/mobile_radio_state_machine.pngbin0 -> 34338 bytes
-rw-r--r--docs/html/images/screens_support/avds-config.pngbin57962 -> 66660 bytes
-rw-r--r--docs/html/index.jd20
-rw-r--r--docs/html/offline.jd2
-rw-r--r--docs/html/resources/articles/creating-input-method.jd742
-rw-r--r--docs/html/resources/articles/images/inputmethod_composing_text_1.pngbin0 -> 5551 bytes
-rw-r--r--docs/html/resources/articles/images/inputmethod_composing_text_2.pngbin0 -> 5458 bytes
-rw-r--r--docs/html/resources/articles/images/inputmethod_composing_text_3.pngbin0 -> 1888 bytes
-rw-r--r--docs/html/resources/articles/images/inputmethod_lifecycle_image.pngbin0 -> 88902 bytes
-rw-r--r--docs/html/resources/articles/images/inputmethod_numeric_type_screenshot.pngbin0 -> 11814 bytes
-rw-r--r--docs/html/resources/articles/images/inputmethod_subtype_notification.pngbin0 -> 11404 bytes
-rw-r--r--docs/html/resources/articles/images/inputmethod_subtype_preferences.pngbin0 -> 18451 bytes
-rw-r--r--docs/html/resources/articles/images/inputmethod_subtype_settings.pngbin0 -> 16355 bytes
-rw-r--r--docs/html/resources/articles/images/inputmethod_text_type_screenshot.pngbin0 -> 13017 bytes
-rw-r--r--docs/html/resources/resources_toc.cs24
-rw-r--r--docs/html/resources/samples/get.jd8
-rw-r--r--docs/html/resources/tutorials/hello-world.jd4
-rw-r--r--docs/html/resources/tutorials/testing/activity_test.jd2
-rw-r--r--docs/html/sdk/adding-components.jd114
-rw-r--r--docs/html/sdk/android-3.0.jd9
-rw-r--r--docs/html/sdk/compatibility-library.jd6
-rw-r--r--docs/html/sdk/eclipse-adt.jd44
-rw-r--r--docs/html/sdk/index.jd2
-rw-r--r--docs/html/sdk/installing.jd107
-rw-r--r--docs/html/sdk/oem-usb.jd4
-rw-r--r--docs/html/sdk/sdk_toc.cs4
-rw-r--r--docs/html/sdk/win-usb.jd10
-rw-r--r--docs/html/training/efficient-downloads/connectivity_patterns.jd76
-rw-r--r--docs/html/training/efficient-downloads/efficient-network-access.jd170
-rw-r--r--docs/html/training/efficient-downloads/index.jd51
-rw-r--r--docs/html/training/efficient-downloads/redundant_redundant.jd87
-rw-r--r--docs/html/training/efficient-downloads/regular_updates.jd102
-rw-r--r--include/cpustats/CentralTendencyStatistics.h75
-rw-r--r--include/cpustats/README.txt6
-rw-r--r--include/cpustats/ThreadCpuUsage.h139
-rw-r--r--include/media/ICrypto.h63
-rw-r--r--include/media/IMediaPlayerService.h2
-rw-r--r--include/media/JetPlayer.h5
-rw-r--r--include/media/stagefright/ACodec.h8
-rw-r--r--include/media/stagefright/MediaCodec.h7
-rw-r--r--include/media/stagefright/MetaData.h6
-rw-r--r--include/media/stagefright/NuMediaExtractor.h10
-rw-r--r--include/media/stagefright/OMXCodec.h6
-rw-r--r--include/media/stagefright/foundation/AString.h1
-rw-r--r--include/private/hwui/DrawGlInfo.h17
-rw-r--r--libs/cpustats/Android.mk21
-rw-r--r--libs/cpustats/CentralTendencyStatistics.cpp81
-rw-r--r--libs/cpustats/ThreadCpuUsage.cpp252
-rw-r--r--libs/hwui/DisplayListRenderer.cpp81
-rw-r--r--libs/hwui/DisplayListRenderer.h10
-rw-r--r--libs/hwui/FontRenderer.cpp4
-rw-r--r--libs/hwui/OpenGLRenderer.cpp23
-rw-r--r--libs/hwui/OpenGLRenderer.h4
-rw-r--r--libs/rs/driver/rsdGL.cpp8
-rw-r--r--libs/rs/driver/rsdProgram.cpp12
-rw-r--r--libs/rs/driver/rsdShader.cpp88
-rw-r--r--libs/rs/driver/rsdShader.h28
-rw-r--r--libs/rs/driver/rsdShaderCache.cpp66
-rw-r--r--libs/rs/driver/rsdShaderCache.h4
-rw-r--r--media/java/android/media/MediaCodec.java2
-rw-r--r--media/java/android/media/MediaExtractor.java7
-rw-r--r--media/jni/android_media_MediaExtractor.cpp29
-rw-r--r--media/jni/android_media_MediaExtractor.h1
-rw-r--r--media/jni/android_media_Utils.cpp2
-rw-r--r--media/jni/soundpool/Android.mk8
-rw-r--r--media/jni/soundpool/android_media_SoundPool.cpp2
-rw-r--r--media/libeffects/visualizer/EffectVisualizer.cpp33
-rw-r--r--media/libmedia/Android.mk5
-rw-r--r--media/libmedia/ICrypto.cpp293
-rw-r--r--media/libmedia/IMediaMetadataRetriever.cpp1
-rw-r--r--media/libmedia/IMediaPlayerService.cpp15
-rw-r--r--media/libmedia/JetPlayer.cpp2
-rw-r--r--media/libmedia/SoundPool.cpp (renamed from media/jni/soundpool/SoundPool.cpp)2
-rw-r--r--media/libmedia/SoundPool.h (renamed from media/jni/soundpool/SoundPool.h)0
-rw-r--r--media/libmedia/SoundPoolThread.cpp (renamed from media/jni/soundpool/SoundPoolThread.cpp)0
-rw-r--r--media/libmedia/SoundPoolThread.h (renamed from media/jni/soundpool/SoundPoolThread.h)0
-rw-r--r--media/libmediaplayerservice/Android.mk3
-rw-r--r--media/libmediaplayerservice/Crypto.cpp65
-rw-r--r--media/libmediaplayerservice/Crypto.h57
-rw-r--r--media/libmediaplayerservice/MediaPlayerService.cpp12
-rw-r--r--media/libmediaplayerservice/MediaPlayerService.h2
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp5
-rw-r--r--media/libstagefright/ACodec.cpp53
-rw-r--r--media/libstagefright/Android.mk13
-rw-r--r--media/libstagefright/MediaCodec.cpp112
-rw-r--r--media/libstagefright/NuMediaExtractor.cpp57
-rwxr-xr-xmedia/libstagefright/OMXCodec.cpp31
-rw-r--r--media/libstagefright/TimedEventQueue.cpp21
-rw-r--r--media/libstagefright/chromium_http/Android.mk2
-rw-r--r--media/libstagefright/codecs/aacdec/SoftAAC.cpp49
-rw-r--r--media/libstagefright/codecs/aacdec/SoftAAC.h1
-rw-r--r--media/libstagefright/foundation/AString.cpp10
-rw-r--r--media/libstagefright/mpeg2ts/AnotherPacketSource.cpp6
-rw-r--r--media/mca/Android.mk21
-rw-r--r--media/mca/effect/java/android/media/effect/Effect.java111
-rw-r--r--media/mca/effect/java/android/media/effect/EffectContext.java131
-rw-r--r--media/mca/effect/java/android/media/effect/EffectFactory.java517
-rw-r--r--media/mca/effect/java/android/media/effect/EffectUpdateListener.java36
-rw-r--r--media/mca/effect/java/android/media/effect/FilterEffect.java101
-rw-r--r--media/mca/effect/java/android/media/effect/FilterGraphEffect.java120
-rw-r--r--media/mca/effect/java/android/media/effect/SingleFilterEffect.java98
-rw-r--r--media/mca/effect/java/android/media/effect/SizeChangeEffect.java65
-rw-r--r--media/mca/effect/java/android/media/effect/effects/AutoFixEffect.java (renamed from core/java/android/webkit/FindListener.java)23
-rw-r--r--media/mca/effect/java/android/media/effect/effects/BackDropperEffect.java102
-rw-r--r--media/mca/effect/java/android/media/effect/effects/BitmapOverlayEffect.java32
-rw-r--r--media/mca/effect/java/android/media/effect/effects/BlackWhiteEffect.java31
-rw-r--r--media/mca/effect/java/android/media/effect/effects/BrightnessEffect.java32
-rw-r--r--media/mca/effect/java/android/media/effect/effects/ColorTemperatureEffect.java31
-rw-r--r--media/mca/effect/java/android/media/effect/effects/ContrastEffect.java32
-rw-r--r--media/mca/effect/java/android/media/effect/effects/CropEffect.java33
-rw-r--r--media/mca/effect/java/android/media/effect/effects/CrossProcessEffect.java31
-rw-r--r--media/mca/effect/java/android/media/effect/effects/DocumentaryEffect.java30
-rw-r--r--media/mca/effect/java/android/media/effect/effects/DuotoneEffect.java31
-rw-r--r--media/mca/effect/java/android/media/effect/effects/FillLightEffect.java31
-rw-r--r--media/mca/effect/java/android/media/effect/effects/FisheyeEffect.java32
-rw-r--r--media/mca/effect/java/android/media/effect/effects/FlipEffect.java31
-rw-r--r--media/mca/effect/java/android/media/effect/effects/GrainEffect.java31
-rw-r--r--media/mca/effect/java/android/media/effect/effects/GrayscaleEffect.java31
-rw-r--r--media/mca/effect/java/android/media/effect/effects/IdentityEffect.java58
-rw-r--r--media/mca/effect/java/android/media/effect/effects/LomoishEffect.java30
-rw-r--r--media/mca/effect/java/android/media/effect/effects/NegativeEffect.java31
-rw-r--r--media/mca/effect/java/android/media/effect/effects/PosterizeEffect.java31
-rw-r--r--media/mca/effect/java/android/media/effect/effects/RedEyeEffect.java32
-rw-r--r--media/mca/effect/java/android/media/effect/effects/RotateEffect.java31
-rw-r--r--media/mca/effect/java/android/media/effect/effects/SaturateEffect.java31
-rw-r--r--media/mca/effect/java/android/media/effect/effects/SepiaEffect.java31
-rw-r--r--media/mca/effect/java/android/media/effect/effects/SharpenEffect.java31
-rw-r--r--media/mca/effect/java/android/media/effect/effects/StraightenEffect.java31
-rw-r--r--media/mca/effect/java/android/media/effect/effects/TintEffect.java31
-rw-r--r--media/mca/effect/java/android/media/effect/effects/VignetteEffect.java31
-rw-r--r--media/mca/effect/java/android/media/effect/package-info.java28
-rw-r--r--media/mca/effect/java/android/media/effect/package.html54
-rw-r--r--media/mca/filterfw/Android.mk53
-rw-r--r--media/mca/filterfw/java/android/filterfw/FilterFunctionEnvironment.java99
-rw-r--r--media/mca/filterfw/java/android/filterfw/GraphEnvironment.java197
-rw-r--r--media/mca/filterfw/java/android/filterfw/MffEnvironment.java106
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/AsyncRunner.java247
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/CachedFrameManager.java155
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/FieldPort.java111
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/Filter.java709
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/FilterContext.java126
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/FilterFactory.java145
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/FilterFunction.java139
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/FilterGraph.java363
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/FilterPort.java134
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/FilterSurfaceView.java157
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/FinalPort.java48
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/Frame.java236
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/FrameFormat.java439
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/FrameManager.java67
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/GLEnvironment.java180
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/GLFrame.java417
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/GenerateFieldPort.java30
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/GenerateFinalPort.java30
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/GenerateProgramPort.java32
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/GenerateProgramPorts.java29
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/GraphRunner.java100
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/InputPort.java85
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/KeyValueMap.java82
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/MutableFrameFormat.java96
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/NativeAllocatorTag.java28
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/NativeBuffer.java129
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/NativeFrame.java265
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/NativeProgram.java176
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/OneShotScheduler.java78
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/OutputPort.java122
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/Program.java41
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/ProgramPort.java62
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/ProgramVariable.java57
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/ProtocolException.java33
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/RandomScheduler.java54
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/RoundRobinScheduler.java73
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/Scheduler.java47
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/SerializedFrame.java287
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/ShaderProgram.java301
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/SimpleFrame.java161
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/SimpleFrameManager.java107
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/SimpleScheduler.java45
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/StopWatchMap.java101
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/StreamPort.java100
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/SyncRunner.java227
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/VertexFrame.java143
-rw-r--r--media/mca/filterfw/java/android/filterfw/core/package-info.java4
-rw-r--r--media/mca/filterfw/java/android/filterfw/format/ImageFormat.java92
-rw-r--r--media/mca/filterfw/java/android/filterfw/format/ObjectFormat.java105
-rw-r--r--media/mca/filterfw/java/android/filterfw/format/PrimitiveFormat.java79
-rw-r--r--media/mca/filterfw/java/android/filterfw/format/package-info.java4
-rw-r--r--media/mca/filterfw/java/android/filterfw/geometry/Point.java113
-rw-r--r--media/mca/filterfw/java/android/filterfw/geometry/Quad.java94
-rw-r--r--media/mca/filterfw/java/android/filterfw/geometry/Rectangle.java95
-rw-r--r--media/mca/filterfw/java/android/filterfw/geometry/package-info.java4
-rw-r--r--media/mca/filterfw/java/android/filterfw/io/GraphIOException.java33
-rw-r--r--media/mca/filterfw/java/android/filterfw/io/GraphReader.java69
-rw-r--r--media/mca/filterfw/java/android/filterfw/io/PatternScanner.java123
-rw-r--r--media/mca/filterfw/java/android/filterfw/io/TextGraphReader.java489
-rw-r--r--media/mca/filterfw/java/android/filterfw/io/package-info.java4
-rw-r--r--media/mca/filterfw/jni/Android.mk51
-rw-r--r--media/mca/filterfw/jni/jni_gl_environment.cpp377
-rw-r--r--media/mca/filterfw/jni/jni_gl_environment.h97
-rw-r--r--media/mca/filterfw/jni/jni_gl_frame.cpp322
-rw-r--r--media/mca/filterfw/jni/jni_gl_frame.h137
-rw-r--r--media/mca/filterfw/jni/jni_init.cpp43
-rw-r--r--media/mca/filterfw/jni/jni_native_buffer.cpp84
-rw-r--r--media/mca/filterfw/jni/jni_native_buffer.h55
-rw-r--r--media/mca/filterfw/jni/jni_native_frame.cpp293
-rw-r--r--media/mca/filterfw/jni/jni_native_frame.h94
-rw-r--r--media/mca/filterfw/jni/jni_native_program.cpp187
-rw-r--r--media/mca/filterfw/jni/jni_native_program.h100
-rw-r--r--media/mca/filterfw/jni/jni_shader_program.cpp326
-rw-r--r--media/mca/filterfw/jni/jni_shader_program.h150
-rw-r--r--media/mca/filterfw/jni/jni_util.cpp188
-rw-r--r--media/mca/filterfw/jni/jni_util.h283
-rw-r--r--media/mca/filterfw/jni/jni_vertex_frame.cpp90
-rw-r--r--media/mca/filterfw/jni/jni_vertex_frame.h54
-rw-r--r--media/mca/filterfw/native/Android.mk44
-rw-r--r--media/mca/filterfw/native/base/logging.h27
-rw-r--r--media/mca/filterfw/native/base/utilities.h160
-rw-r--r--media/mca/filterfw/native/core/geometry.cpp137
-rw-r--r--media/mca/filterfw/native/core/geometry.h94
-rw-r--r--media/mca/filterfw/native/core/gl_buffer_interface.h71
-rw-r--r--media/mca/filterfw/native/core/gl_env.cpp408
-rw-r--r--media/mca/filterfw/native/core/gl_env.h261
-rw-r--r--media/mca/filterfw/native/core/gl_frame.cpp467
-rw-r--r--media/mca/filterfw/native/core/gl_frame.h217
-rw-r--r--media/mca/filterfw/native/core/native_frame.cpp61
-rw-r--r--media/mca/filterfw/native/core/native_frame.h85
-rw-r--r--media/mca/filterfw/native/core/native_program.cpp162
-rw-r--r--media/mca/filterfw/native/core/native_program.h84
-rw-r--r--media/mca/filterfw/native/core/shader_program.cpp1122
-rw-r--r--media/mca/filterfw/native/core/shader_program.h553
-rw-r--r--media/mca/filterfw/native/core/statistics.cpp57
-rw-r--r--media/mca/filterfw/native/core/statistics.h71
-rw-r--r--media/mca/filterfw/native/core/time_util.cpp92
-rw-r--r--media/mca/filterfw/native/core/time_util.h69
-rw-r--r--media/mca/filterfw/native/core/value.cpp236
-rw-r--r--media/mca/filterfw/native/core/value.h80
-rw-r--r--media/mca/filterfw/native/core/vertex_frame.cpp84
-rw-r--r--media/mca/filterfw/native/core/vertex_frame.h77
-rw-r--r--media/mca/filterfw/native/libfilterfw.mk33
-rw-r--r--media/mca/filterpacks/Android.mk55
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/base/CallbackFilter.java103
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/base/FrameBranch.java62
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/base/FrameFetch.java64
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/base/FrameSource.java63
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/base/FrameStore.java51
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/base/GLTextureSource.java106
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/base/GLTextureTarget.java64
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/base/InputStreamSource.java96
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/base/NullFilter.java44
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/base/ObjectSource.java93
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/base/OutputStreamTarget.java66
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/base/RetargetFilter.java76
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/AlphaBlendFilter.java66
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/AutoFixFilter.java309
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/BitmapOverlayFilter.java153
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/BitmapSource.java118
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/BlackWhiteFilter.java176
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/BlendFilter.java65
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/BrightnessFilter.java59
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/ColorTemperatureFilter.java128
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/ContrastFilter.java64
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/CropFilter.java147
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/CropRectFilter.java141
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/CrossProcessFilter.java129
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/DocumentaryFilter.java175
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/DrawOverlayFilter.java92
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/DrawRectFilter.java132
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/DuotoneFilter.java125
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/FillLightFilter.java140
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/FisheyeFilter.java178
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/FixedRotationFilter.java112
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/FlipFilter.java120
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/GrainFilter.java187
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/ImageCombineFilter.java139
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/ImageEncoder.java64
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/ImageSlicer.java142
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/ImageStitcher.java144
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/Invert.java60
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/LomoishFilter.java218
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/NegativeFilter.java103
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/PosterizeFilter.java106
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/RedEyeFilter.java211
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/ResizeFilter.java124
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/RotateFilter.java156
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/SaturateFilter.java170
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/SepiaFilter.java111
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/SharpenFilter.java153
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/SimpleImageFilter.java116
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/StraightenFilter.java154
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/TintFilter.java133
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/ToGrayFilter.java90
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/ToPackedGrayFilter.java143
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/ToRGBAFilter.java103
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/ToRGBFilter.java103
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/VignetteFilter.java153
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/package-info.java4
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/numeric/SinWaveFilter.java66
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/numeric/package-info.java4
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/performance/Throughput.java61
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/performance/ThroughputFilter.java103
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/performance/package-info.java4
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/text/StringLogger.java49
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/text/StringSource.java59
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/text/ToUpperCase.java55
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/text/package-info.java4
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/ui/SurfaceRenderFilter.java275
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/ui/SurfaceTargetFilter.java257
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/ui/package-info.java4
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/videoproc/BackDropperFilter.java976
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/videosink/MediaEncoderFilter.java469
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/videosink/MediaRecorderStopException.java44
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/videosrc/CameraSource.java358
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/videosrc/MediaSource.java567
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java265
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureTarget.java282
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/videosrc/package-info.java4
-rw-r--r--media/mca/filterpacks/native/base/geometry.cpp160
-rw-r--r--media/mca/filterpacks/native/base/geometry.h111
-rw-r--r--media/mca/filterpacks/native/base/time_util.cpp90
-rw-r--r--media/mca/filterpacks/native/base/time_util.h69
-rw-r--r--media/mca/filterpacks/native/base/utilities.h160
-rw-r--r--media/mca/filterpacks/native/base/vec_types.h177
-rw-r--r--media/mca/filterpacks/native/imageproc/brightness.c97
-rw-r--r--media/mca/filterpacks/native/imageproc/contrast.c85
-rw-r--r--media/mca/filterpacks/native/imageproc/invert.c46
-rw-r--r--media/mca/filterpacks/native/imageproc/to_rgba.c145
-rw-r--r--media/mca/samples/Android.mk21
-rw-r--r--media/mca/samples/CameraEffectsRecordingSample/Android.mk34
-rw-r--r--media/mca/samples/CameraEffectsRecordingSample/AndroidManifest.xml37
-rw-r--r--media/mca/samples/CameraEffectsRecordingSample/java/android/media/filterfw/samples/CameraEffectsRecordingSample.java101
-rw-r--r--media/mca/samples/CameraEffectsRecordingSample/res/drawable/android.jpgbin0 -> 3710 bytes
-rw-r--r--media/mca/samples/CameraEffectsRecordingSample/res/layout/main.xml32
-rw-r--r--media/mca/samples/CameraEffectsRecordingSample/res/raw/cameraeffectsrecordingsample.graph57
-rw-r--r--media/mca/samples/CameraEffectsRecordingSample/res/values/strings.xml20
-rw-r--r--media/mca/structgen.py367
-rw-r--r--media/mca/tests/Android.mk18
-rw-r--r--media/mca/tests/AndroidManifest.xml (renamed from data/etc/android.hardware.faketouch.multitouch.jazzhand.xml)23
-rw-r--r--media/mca/tests/src/android/camera/mediaeffects/tests/functional/EffectsVideoCapture.java90
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java2
-rw-r--r--policy/src/com/android/internal/policy/impl/GlobalActions.java28
-rw-r--r--services/java/com/android/server/AppWidgetService.java33
-rw-r--r--services/java/com/android/server/AppWidgetServiceImpl.java16
-rw-r--r--services/java/com/android/server/BackupManagerService.java220
-rw-r--r--services/java/com/android/server/ClipboardService.java3
-rw-r--r--services/java/com/android/server/WallpaperManagerService.java52
-rw-r--r--services/java/com/android/server/WiredAccessoryObserver.java23
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java96
-rw-r--r--services/java/com/android/server/pm/PackageManagerService.java294
-rw-r--r--services/java/com/android/server/pm/PackageSetting.java6
-rw-r--r--services/java/com/android/server/pm/PackageSettingBase.java9
-rw-r--r--services/java/com/android/server/pm/Settings.java63
-rw-r--r--services/java/com/android/server/pm/UserManager.java107
-rw-r--r--services/java/com/android/server/wm/AppWindowToken.java18
-rw-r--r--services/java/com/android/server/wm/DimAnimator.java8
-rw-r--r--services/java/com/android/server/wm/WindowAnimator.java27
-rw-r--r--services/java/com/android/server/wm/WindowManagerService.java54
-rw-r--r--services/java/com/android/server/wm/WindowState.java332
-rw-r--r--services/java/com/android/server/wm/WindowStateAnimator.java298
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java8
-rw-r--r--telephony/java/com/android/internal/telephony/PhoneBase.java14
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java5
-rwxr-xr-xtelephony/java/com/android/internal/telephony/cdma/CDMAPhone.java13
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java2
-rwxr-xr-xtelephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java5
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GSMPhone.java13
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java2
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java4
-rw-r--r--test-runner/src/android/test/mock/MockPackageManager.java9
-rw-r--r--voip/java/android/net/rtp/AudioStream.java2
472 files changed, 35683 insertions, 4341 deletions
diff --git a/Android.mk b/Android.mk
index 5572c50..15a4196 100644
--- a/Android.mk
+++ b/Android.mk
@@ -324,9 +324,6 @@ fwbase_dirs_to_document := \
# include definition of libcore_to_document
include $(LOCAL_PATH)/../../libcore/Docs.mk
-# include definition of libfilterfw_to_document
-include $(LOCAL_PATH)/../../system/media/mca/Docs.mk
-
non_base_dirs := \
../../external/apache-http/src/org/apache/http
@@ -349,8 +346,7 @@ html_dirs := \
# Common sources for doc check and api check
common_src_files := \
$(call find-other-html-files, $(html_dirs)) \
- $(addprefix ../../libcore/, $(call libcore_to_document, $(LOCAL_PATH)/../../libcore)) \
- $(addprefix ../../system/media/mca/, $(call libfilterfw_to_document, $(LOCAL_PATH)/../../system/media/mca)) \
+ $(addprefix ../../libcore/, $(call libcore_to_document, $(LOCAL_PATH)/../../libcore))
# These are relative to frameworks/base
framework_docs_LOCAL_SRC_FILES := \
diff --git a/api/current.txt b/api/current.txt
index 86cf9e1..4cdd30b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11926,8 +11926,10 @@ package android.net {
method public java.lang.String[] getDefaultCipherSuites();
method public static org.apache.http.conn.ssl.SSLSocketFactory getHttpSocketFactory(int, android.net.SSLSessionCache);
method public static javax.net.ssl.SSLSocketFactory getInsecure(int, android.net.SSLSessionCache);
+ method public byte[] getNpnSelectedProtocol(java.net.Socket);
method public java.lang.String[] getSupportedCipherSuites();
method public void setKeyManagers(javax.net.ssl.KeyManager[]);
+ method public void setNpnProtocols(byte[][]);
method public void setTrustManagers(javax.net.ssl.TrustManager[]);
}
@@ -23474,6 +23476,7 @@ package android.view {
method public boolean requestRectangleOnScreen(android.graphics.Rect, boolean);
method public void resetResolvedLayoutDirection();
method public void resetResolvedTextDirection();
+ method public void resolveLayoutDirection();
method public void resolvePadding();
method public static int resolveSize(int, int);
method public static int resolveSizeAndState(int, int, int);
@@ -26054,6 +26057,7 @@ package android.widget {
public class AdapterViewFlipper extends android.widget.AdapterViewAnimator {
ctor public AdapterViewFlipper(android.content.Context);
ctor public AdapterViewFlipper(android.content.Context, android.util.AttributeSet);
+ method public int getFlipInterval();
method public boolean isAutoStart();
method public boolean isFlipping();
method public void setAutoStart(boolean);
@@ -26729,19 +26733,27 @@ package android.widget {
ctor public ImageView(android.content.Context, android.util.AttributeSet);
ctor public ImageView(android.content.Context, android.util.AttributeSet, int);
method public final void clearColorFilter();
+ method public boolean getAdjustViewBounds();
method public boolean getBaselineAlignBottom();
+ method public android.graphics.ColorFilter getColorFilter();
+ method public boolean getCropToPadding();
method public android.graphics.drawable.Drawable getDrawable();
+ method public int getImageAlpha();
method public android.graphics.Matrix getImageMatrix();
+ method public int getMaxHeight();
+ method public int getMaxWidth();
method public android.widget.ImageView.ScaleType getScaleType();
method public int[] onCreateDrawableState(int);
method public void setAdjustViewBounds(boolean);
- method public void setAlpha(int);
+ method public deprecated void setAlpha(int);
method public void setBaseline(int);
method public void setBaselineAlignBottom(boolean);
method public final void setColorFilter(int, android.graphics.PorterDuff.Mode);
method public final void setColorFilter(int);
method public void setColorFilter(android.graphics.ColorFilter);
+ method public void setCropToPadding(boolean);
method protected boolean setFrame(int, int, int, int);
+ method public void setImageAlpha(int);
method public void setImageBitmap(android.graphics.Bitmap);
method public void setImageDrawable(android.graphics.drawable.Drawable);
method public void setImageLevel(int);
diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk
deleted file mode 100644
index d602500..0000000
--- a/cmds/dumpstate/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-ifdef BOARD_WLAN_DEVICE
-LOCAL_CFLAGS := -DFWDUMP_$(BOARD_WLAN_DEVICE)
-endif
-
-LOCAL_SRC_FILES := dumpstate.c utils.c
-
-LOCAL_MODULE := dumpstate
-
-LOCAL_SHARED_LIBRARIES := libcutils
-
-ifdef BOARD_LIB_DUMPSTATE
-LOCAL_STATIC_LIBRARIES := $(BOARD_LIB_DUMPSTATE)
-LOCAL_CFLAGS += -DBOARD_HAS_DUMPSTATE
-endif
-
-include $(BUILD_EXECUTABLE)
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c
deleted file mode 100644
index aa95b35..0000000
--- a/cmds/dumpstate/dumpstate.c
+++ /dev/null
@@ -1,452 +0,0 @@
-/*
- * Copyright (C) 2008 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.
- */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/resource.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#include <linux/capability.h>
-#include <linux/prctl.h>
-
-#include <cutils/properties.h>
-
-#include "private/android_filesystem_config.h"
-
-#define LOG_TAG "dumpstate"
-#include <utils/Log.h>
-
-#include "dumpstate.h"
-
-/* read before root is shed */
-static char cmdline_buf[16384] = "(unknown)";
-static const char *dump_traces_path = NULL;
-
-static char screenshot_path[PATH_MAX] = "";
-
-/* dumps the current system state to stdout */
-static void dumpstate() {
- time_t now = time(NULL);
- char build[PROPERTY_VALUE_MAX], fingerprint[PROPERTY_VALUE_MAX];
- char radio[PROPERTY_VALUE_MAX], bootloader[PROPERTY_VALUE_MAX];
- char network[PROPERTY_VALUE_MAX], date[80];
- char build_type[PROPERTY_VALUE_MAX];
-
- property_get("ro.build.display.id", build, "(unknown)");
- property_get("ro.build.fingerprint", fingerprint, "(unknown)");
- property_get("ro.build.type", build_type, "(unknown)");
- property_get("ro.baseband", radio, "(unknown)");
- property_get("ro.bootloader", bootloader, "(unknown)");
- property_get("gsm.operator.alpha", network, "(unknown)");
- strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", localtime(&now));
-
- printf("========================================================\n");
- printf("== dumpstate: %s\n", date);
- printf("========================================================\n");
-
- printf("\n");
- printf("Build: %s\n", build);
- printf("Build fingerprint: '%s'\n", fingerprint); /* format is important for other tools */
- printf("Bootloader: %s\n", bootloader);
- printf("Radio: %s\n", radio);
- printf("Network: %s\n", network);
-
- printf("Kernel: ");
- dump_file(NULL, "/proc/version");
- printf("Command line: %s\n", strtok(cmdline_buf, "\n"));
- printf("\n");
-
- run_command("UPTIME", 10, "uptime", NULL);
- dump_file("MEMORY INFO", "/proc/meminfo");
- run_command("CPU INFO", 10, "top", "-n", "1", "-d", "1", "-m", "30", "-t", NULL);
- run_command("PROCRANK", 20, "procrank", NULL);
- dump_file("VIRTUAL MEMORY STATS", "/proc/vmstat");
- dump_file("VMALLOC INFO", "/proc/vmallocinfo");
- dump_file("SLAB INFO", "/proc/slabinfo");
- dump_file("ZONEINFO", "/proc/zoneinfo");
- dump_file("PAGETYPEINFO", "/proc/pagetypeinfo");
- dump_file("BUDDYINFO", "/proc/buddyinfo");
-
-
- dump_file("KERNEL WAKELOCKS", "/proc/wakelocks");
- dump_file("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state");
-
- run_command("PROCESSES", 10, "ps", "-P", NULL);
- run_command("PROCESSES AND THREADS", 10, "ps", "-t", "-p", "-P", NULL);
- run_command("LIBRANK", 10, "librank", NULL);
-
- do_dmesg();
-
- run_command("LIST OF OPEN FILES", 10, SU_PATH, "root", "lsof", NULL);
-
- for_each_pid(do_showmap, "SMAPS OF ALL PROCESSES");
- for_each_pid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS");
-
- // dump_file("EVENT LOG TAGS", "/etc/event-log-tags");
- run_command("SYSTEM LOG", 20, "logcat", "-v", "threadtime", "-d", "*:v", NULL);
- run_command("EVENT LOG", 20, "logcat", "-b", "events", "-v", "threadtime", "-d", "*:v", NULL);
- run_command("RADIO LOG", 20, "logcat", "-b", "radio", "-v", "threadtime", "-d", "*:v", NULL);
-
-
- /* show the traces we collected in main(), if that was done */
- if (dump_traces_path != NULL) {
- dump_file("VM TRACES JUST NOW", dump_traces_path);
- }
-
- /* only show ANR traces if they're less than 15 minutes old */
- struct stat st;
- char anr_traces_path[PATH_MAX];
- property_get("dalvik.vm.stack-trace-file", anr_traces_path, "");
- if (!anr_traces_path[0]) {
- printf("*** NO VM TRACES FILE DEFINED (dalvik.vm.stack-trace-file)\n\n");
- } else if (stat(anr_traces_path, &st)) {
- printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anr_traces_path, strerror(errno));
- } else {
- dump_file("VM TRACES AT LAST ANR", anr_traces_path);
- }
-
- /* slow traces for slow operations */
- if (anr_traces_path[0] != 0) {
- int tail = strlen(anr_traces_path)-1;
- while (tail > 0 && anr_traces_path[tail] != '/') {
- tail--;
- }
- int i = 0;
- while (1) {
- sprintf(anr_traces_path+tail+1, "slow%02d.txt", i);
- if (stat(anr_traces_path, &st)) {
- // No traces file at this index, done with the files.
- break;
- }
- dump_file("VM TRACES WHEN SLOW", anr_traces_path);
- i++;
- }
- }
-
- dump_file("NETWORK DEV INFO", "/proc/net/dev");
- dump_file("QTAGUID NETWORK INTERFACES INFO", "/proc/net/xt_qtaguid/iface_stat_all");
- dump_file("QTAGUID CTRL INFO", "/proc/net/xt_qtaguid/ctrl");
- dump_file("QTAGUID STATS INFO", "/proc/net/xt_qtaguid/stats");
-
- dump_file("NETWORK ROUTES", "/proc/net/route");
- dump_file("NETWORK ROUTES IPV6", "/proc/net/ipv6_route");
-
- /* TODO: Make last_kmsg CAP_SYSLOG protected. b/5555691 */
- dump_file("LAST KMSG", "/proc/last_kmsg");
- dump_file("LAST PANIC CONSOLE", "/data/dontpanic/apanic_console");
- dump_file("LAST PANIC THREADS", "/data/dontpanic/apanic_threads");
-
- if (screenshot_path[0]) {
- ALOGI("taking screenshot\n");
- run_command(NULL, 5, SU_PATH, "root", "screenshot", screenshot_path, NULL);
- ALOGI("wrote screenshot: %s\n", screenshot_path);
- }
-
- run_command("SYSTEM SETTINGS", 20, SU_PATH, "root", "sqlite3",
- "/data/data/com.android.providers.settings/databases/settings.db",
- "pragma user_version; select * from system; select * from secure;", NULL);
-
- /* The following have a tendency to get wedged when wifi drivers/fw goes belly-up. */
- run_command("NETWORK INTERFACES", 10, SU_PATH, "root", "netcfg", NULL);
- run_command("IP RULES", 10, "ip", "rule", "show", NULL);
- run_command("IP RULES v6", 10, "ip", "-6", "rule", "show", NULL);
- run_command("ROUTE TABLE 60", 10, "ip", "route", "show", "table", "60", NULL);
- run_command("ROUTE TABLE 61 v6", 10, "ip", "-6", "route", "show", "table", "60", NULL);
- run_command("ROUTE TABLE 61", 10, "ip", "route", "show", "table", "61", NULL);
- run_command("ROUTE TABLE 61 v6", 10, "ip", "-6", "route", "show", "table", "61", NULL);
- dump_file("ARP CACHE", "/proc/net/arp");
- run_command("IPTABLES", 10, SU_PATH, "root", "iptables", "-L", "-nvx", NULL);
- run_command("IP6TABLES", 10, SU_PATH, "root", "ip6tables", "-L", "-nvx", NULL);
- run_command("IPTABLE NAT", 10, SU_PATH, "root", "iptables", "-t", "nat", "-L", "-n", NULL);
- run_command("IPT6ABLE NAT", 10, SU_PATH, "root", "ip6tables", "-t", "nat", "-L", "-n", NULL);
-
- run_command("WIFI NETWORKS", 20,
- SU_PATH, "root", "wpa_cli", "list_networks", NULL);
-
- property_get("dhcp.wlan0.gateway", network, "");
- if (network[0])
- run_command("PING GATEWAY", 10, SU_PATH, "root", "ping", "-c", "3", "-i", ".5", network, NULL);
- property_get("dhcp.wlan0.dns1", network, "");
- if (network[0])
- run_command("PING DNS1", 10, SU_PATH, "root", "ping", "-c", "3", "-i", ".5", network, NULL);
- property_get("dhcp.wlan0.dns2", network, "");
- if (network[0])
- run_command("PING DNS2", 10, SU_PATH, "root", "ping", "-c", "3", "-i", ".5", network, NULL);
-#ifdef FWDUMP_bcm4329
- run_command("DUMP WIFI STATUS", 20,
- SU_PATH, "root", "dhdutil", "-i", "wlan0", "dump", NULL);
- run_command("DUMP WIFI INTERNAL COUNTERS", 20,
- SU_PATH, "root", "wlutil", "counters", NULL);
-#endif
-
- print_properties();
-
- run_command("VOLD DUMP", 10, "vdc", "dump", NULL);
- run_command("SECURE CONTAINERS", 10, "vdc", "asec", "list", NULL);
-
- run_command("FILESYSTEMS & FREE SPACE", 10, SU_PATH, "root", "df", NULL);
-
- dump_file("PACKAGE SETTINGS", "/data/system/packages.xml");
- dump_file("PACKAGE UID ERRORS", "/data/system/uiderrors.txt");
-
- run_command("LAST RADIO LOG", 10, "parse_radio_log", "/proc/last_radio_log", NULL);
-
- printf("------ BACKLIGHTS ------\n");
- printf("LCD brightness=");
- dump_file(NULL, "/sys/class/leds/lcd-backlight/brightness");
- printf("Button brightness=");
- dump_file(NULL, "/sys/class/leds/button-backlight/brightness");
- printf("Keyboard brightness=");
- dump_file(NULL, "/sys/class/leds/keyboard-backlight/brightness");
- printf("ALS mode=");
- dump_file(NULL, "/sys/class/leds/lcd-backlight/als");
- printf("LCD driver registers:\n");
- dump_file(NULL, "/sys/class/leds/lcd-backlight/registers");
- printf("\n");
-
- /* Binder state is expensive to look at as it uses a lot of memory. */
- dump_file("BINDER FAILED TRANSACTION LOG", "/sys/kernel/debug/binder/failed_transaction_log");
- dump_file("BINDER TRANSACTION LOG", "/sys/kernel/debug/binder/transaction_log");
- dump_file("BINDER TRANSACTIONS", "/sys/kernel/debug/binder/transactions");
- dump_file("BINDER STATS", "/sys/kernel/debug/binder/stats");
- dump_file("BINDER STATE", "/sys/kernel/debug/binder/state");
-
-#ifdef BOARD_HAS_DUMPSTATE
- printf("========================================================\n");
- printf("== Board\n");
- printf("========================================================\n");
-
- dumpstate_board();
- printf("\n");
-#endif
-
- /* Migrate the ril_dumpstate to a dumpstate_board()? */
- char ril_dumpstate_timeout[PROPERTY_VALUE_MAX] = {0};
- property_get("ril.dumpstate.timeout", ril_dumpstate_timeout, "30");
- if (strnlen(ril_dumpstate_timeout, PROPERTY_VALUE_MAX - 1) > 0) {
- if (0 == strncmp(build_type, "user", PROPERTY_VALUE_MAX - 1)) {
- // su does not exist on user builds, so try running without it.
- // This way any implementations of vril-dump that do not require
- // root can run on user builds.
- run_command("DUMP VENDOR RIL LOGS", atoi(ril_dumpstate_timeout),
- "vril-dump", NULL);
- } else {
- run_command("DUMP VENDOR RIL LOGS", atoi(ril_dumpstate_timeout),
- SU_PATH, "root", "vril-dump", NULL);
- }
- }
-
- printf("========================================================\n");
- printf("== Android Framework Services\n");
- printf("========================================================\n");
-
- /* the full dumpsys is starting to take a long time, so we need
- to increase its timeout. we really need to do the timeouts in
- dumpsys itself... */
- run_command("DUMPSYS", 60, "dumpsys", NULL);
-
- printf("========================================================\n");
- printf("== Running Application Activities\n");
- printf("========================================================\n");
-
- run_command("APP ACTIVITIES", 30, "dumpsys", "activity", "all", NULL);
-
- printf("========================================================\n");
- printf("== Running Application Services\n");
- printf("========================================================\n");
-
- run_command("APP SERVICES", 30, "dumpsys", "activity", "service", "all", NULL);
-
- printf("========================================================\n");
- printf("== Running Application Providers\n");
- printf("========================================================\n");
-
- run_command("APP SERVICES", 30, "dumpsys", "activity", "provider", "all", NULL);
-
-
- printf("========================================================\n");
- printf("== dumpstate: done\n");
- printf("========================================================\n");
-}
-
-static void usage() {
- fprintf(stderr, "usage: dumpstate [-b soundfile] [-e soundfile] [-o file [-d] [-p] [-z]] [-s]\n"
- " -o: write to file (instead of stdout)\n"
- " -d: append date to filename (requires -o)\n"
- " -z: gzip output (requires -o)\n"
- " -p: capture screenshot to filename.png (requires -o)\n"
- " -s: write output to control socket (for init)\n"
- " -b: play sound file instead of vibrate, at beginning of job\n"
- " -e: play sound file instead of vibrate, at end of job\n"
- );
-}
-
-int main(int argc, char *argv[]) {
- int do_add_date = 0;
- int do_compress = 0;
- char* use_outfile = 0;
- char* begin_sound = 0;
- char* end_sound = 0;
- int use_socket = 0;
- int do_fb = 0;
-
- ALOGI("begin\n");
-
- /* set as high priority, and protect from OOM killer */
- setpriority(PRIO_PROCESS, 0, -20);
- FILE *oom_adj = fopen("/proc/self/oom_adj", "w");
- if (oom_adj) {
- fputs("-17", oom_adj);
- fclose(oom_adj);
- }
-
- /* very first thing, collect VM traces from Dalvik (needs root) */
- dump_traces_path = dump_vm_traces();
-
- int c;
- while ((c = getopt(argc, argv, "b:de:ho:svzp")) != -1) {
- switch (c) {
- case 'b': begin_sound = optarg; break;
- case 'd': do_add_date = 1; break;
- case 'e': end_sound = optarg; break;
- case 'o': use_outfile = optarg; break;
- case 's': use_socket = 1; break;
- case 'v': break; // compatibility no-op
- case 'z': do_compress = 6; break;
- case 'p': do_fb = 1; break;
- case '?': printf("\n");
- case 'h':
- usage();
- exit(1);
- }
- }
-
- /* open the vibrator before dropping root */
- FILE *vibrator = fopen("/sys/class/timed_output/vibrator/enable", "w");
- if (vibrator) fcntl(fileno(vibrator), F_SETFD, FD_CLOEXEC);
-
- /* read /proc/cmdline before dropping root */
- FILE *cmdline = fopen("/proc/cmdline", "r");
- if (cmdline != NULL) {
- fgets(cmdline_buf, sizeof(cmdline_buf), cmdline);
- fclose(cmdline);
- }
-
- if (getuid() == 0) {
- if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
- ALOGE("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno));
- return -1;
- }
-
- /* switch to non-root user and group */
- gid_t groups[] = { AID_LOG, AID_SDCARD_RW, AID_MOUNT, AID_INET, AID_NET_BW_STATS };
- if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) {
- ALOGE("Unable to setgroups, aborting: %s\n", strerror(errno));
- return -1;
- }
- if (setgid(AID_SHELL) != 0) {
- ALOGE("Unable to setgid, aborting: %s\n", strerror(errno));
- return -1;
- }
- if (setuid(AID_SHELL) != 0) {
- ALOGE("Unable to setuid, aborting: %s\n", strerror(errno));
- return -1;
- }
-
- struct __user_cap_header_struct capheader;
- struct __user_cap_data_struct capdata[2];
- memset(&capheader, 0, sizeof(capheader));
- memset(&capdata, 0, sizeof(capdata));
- capheader.version = _LINUX_CAPABILITY_VERSION_3;
- capheader.pid = 0;
-
- capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = CAP_TO_MASK(CAP_SYSLOG);
- capdata[CAP_TO_INDEX(CAP_SYSLOG)].effective = CAP_TO_MASK(CAP_SYSLOG);
- capdata[0].inheritable = 0;
- capdata[1].inheritable = 0;
-
- if (capset(&capheader, &capdata[0]) < 0) {
- ALOGE("capset failed: %s\n", strerror(errno));
- return -1;
- }
- }
-
- char path[PATH_MAX], tmp_path[PATH_MAX];
- pid_t gzip_pid = -1;
-
- if (use_socket) {
- redirect_to_socket(stdout, "dumpstate");
- } else if (use_outfile) {
- strlcpy(path, use_outfile, sizeof(path));
- if (do_add_date) {
- char date[80];
- time_t now = time(NULL);
- strftime(date, sizeof(date), "-%Y-%m-%d-%H-%M-%S", localtime(&now));
- strlcat(path, date, sizeof(path));
- }
- if (do_fb) {
- strlcpy(screenshot_path, path, sizeof(screenshot_path));
- strlcat(screenshot_path, ".png", sizeof(screenshot_path));
- }
- strlcat(path, ".txt", sizeof(path));
- if (do_compress) strlcat(path, ".gz", sizeof(path));
- strlcpy(tmp_path, path, sizeof(tmp_path));
- strlcat(tmp_path, ".tmp", sizeof(tmp_path));
- gzip_pid = redirect_to_file(stdout, tmp_path, do_compress);
- }
-
- if (begin_sound) {
- play_sound(begin_sound);
- } else if (vibrator) {
- fputs("150", vibrator);
- fflush(vibrator);
- }
-
- dumpstate();
-
- if (end_sound) {
- play_sound(end_sound);
- } else if (vibrator) {
- int i;
- for (i = 0; i < 3; i++) {
- fputs("75\n", vibrator);
- fflush(vibrator);
- usleep((75 + 50) * 1000);
- }
- fclose(vibrator);
- }
-
- /* wait for gzip to finish, otherwise it might get killed when we exit */
- if (gzip_pid > 0) {
- fclose(stdout);
- waitpid(gzip_pid, NULL, 0);
- }
-
- /* rename the (now complete) .tmp file to its final location */
- if (use_outfile && rename(tmp_path, path)) {
- fprintf(stderr, "rename(%s, %s): %s\n", tmp_path, path, strerror(errno));
- }
-
- ALOGI("done\n");
-
- return 0;
-}
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
deleted file mode 100644
index c1c2ad8..0000000
--- a/cmds/dumpstate/dumpstate.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2008 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.
- */
-
-#ifndef _DUMPSTATE_H_
-#define _DUMPSTATE_H_
-
-#include <time.h>
-#include <unistd.h>
-#include <stdio.h>
-
-#define SU_PATH "/system/xbin/su"
-
-/* prints the contents of a file */
-int dump_file(const char *title, const char* path);
-
-/* forks a command and waits for it to finish -- terminate args with NULL */
-int run_command(const char *title, int timeout_seconds, const char *command, ...);
-
-/* prints all the system properties */
-void print_properties();
-
-/* redirect output to a service control socket */
-void redirect_to_socket(FILE *redirect, const char *service);
-
-/* redirect output to a file, optionally gzipping; returns gzip pid */
-pid_t redirect_to_file(FILE *redirect, char *path, int gzip_level);
-
-/* dump Dalvik stack traces, return the trace file location (NULL if none) */
-const char *dump_vm_traces();
-
-/* for each process in the system, run the specified function */
-void for_each_pid(void (*func)(int, const char *), const char *header);
-
-/* Displays a blocked processes in-kernel wait channel */
-void show_wchan(int pid, const char *name);
-
-/* Runs "showmap" for a process */
-void do_showmap(int pid, const char *name);
-
-/* Gets the dmesg output for the kernel */
-void do_dmesg();
-
-/* Play a sound via Stagefright */
-void play_sound(const char* path);
-
-/* Implemented by libdumpstate_board to dump board-specific info */
-void dumpstate_board();
-
-#endif /* _DUMPSTATE_H_ */
diff --git a/cmds/dumpstate/utils.c b/cmds/dumpstate/utils.c
deleted file mode 100644
index 0d5ab90..0000000
--- a/cmds/dumpstate/utils.c
+++ /dev/null
@@ -1,470 +0,0 @@
-/*
- * Copyright (C) 2008 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.
- */
-
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <poll.h>
-#include <signal.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/inotify.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/wait.h>
-#include <sys/klog.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <cutils/properties.h>
-#include <cutils/sockets.h>
-#include <private/android_filesystem_config.h>
-
-#include "dumpstate.h"
-
-void for_each_pid(void (*func)(int, const char *), const char *header) {
- DIR *d;
- struct dirent *de;
-
- if (!(d = opendir("/proc"))) {
- printf("Failed to open /proc (%s)\n", strerror(errno));
- return;
- }
-
- printf("\n------ %s ------\n", header);
- while ((de = readdir(d))) {
- int pid;
- int fd;
- char cmdpath[255];
- char cmdline[255];
-
- if (!(pid = atoi(de->d_name))) {
- continue;
- }
-
- sprintf(cmdpath,"/proc/%d/cmdline", pid);
- memset(cmdline, 0, sizeof(cmdline));
- if ((fd = open(cmdpath, O_RDONLY)) < 0) {
- strcpy(cmdline, "N/A");
- } else {
- read(fd, cmdline, sizeof(cmdline));
- close(fd);
- }
- func(pid, cmdline);
- }
-
- closedir(d);
-}
-
-void show_wchan(int pid, const char *name) {
- char path[255];
- char buffer[255];
- int fd;
-
- memset(buffer, 0, sizeof(buffer));
-
- sprintf(path, "/proc/%d/wchan", pid);
- if ((fd = open(path, O_RDONLY)) < 0) {
- printf("Failed to open '%s' (%s)\n", path, strerror(errno));
- return;
- }
-
- if (read(fd, buffer, sizeof(buffer)) < 0) {
- printf("Failed to read '%s' (%s)\n", path, strerror(errno));
- goto out_close;
- }
-
- printf("%-7d %-32s %s\n", pid, name, buffer);
-
-out_close:
- close(fd);
- return;
-}
-
-void do_dmesg() {
- printf("------ KERNEL LOG (dmesg) ------\n");
- int size = klogctl(10, NULL, 0); /* Get size of kernel buffer */
- if (size <= 0) {
- printf("Unexpected klogctl return value: %d\n\n", size);
- return;
- }
- char *buf = (char *) malloc(size + 1);
- if (buf == NULL) {
- printf("memory allocation failed\n\n");
- return;
- }
- int retval = klogctl(KLOG_READ_ALL, buf, size);
- if (retval < 0) {
- printf("klogctl failure\n\n");
- free(buf);
- return;
- }
- buf[retval] = '\0';
- printf("%s\n\n", buf);
- free(buf);
- return;
-}
-
-void do_showmap(int pid, const char *name) {
- char title[255];
- char arg[255];
-
- sprintf(title, "SHOW MAP %d (%s)", pid, name);
- sprintf(arg, "%d", pid);
- run_command(title, 10, SU_PATH, "root", "showmap", arg, NULL);
-}
-
-/* prints the contents of a file */
-int dump_file(const char *title, const char* path) {
- char buffer[32768];
- int fd = open(path, O_RDONLY);
- if (fd < 0) {
- int err = errno;
- if (title) printf("------ %s (%s) ------\n", title, path);
- printf("*** %s: %s\n", path, strerror(err));
- if (title) printf("\n");
- return -1;
- }
-
- if (title) printf("------ %s (%s", title, path);
-
- if (title) {
- struct stat st;
- if (memcmp(path, "/proc/", 6) && memcmp(path, "/sys/", 5) && !fstat(fd, &st)) {
- char stamp[80];
- time_t mtime = st.st_mtime;
- strftime(stamp, sizeof(stamp), "%Y-%m-%d %H:%M:%S", localtime(&mtime));
- printf(": %s", stamp);
- }
- printf(") ------\n");
- }
-
- int newline = 0;
- for (;;) {
- int ret = read(fd, buffer, sizeof(buffer));
- if (ret > 0) {
- newline = (buffer[ret - 1] == '\n');
- ret = fwrite(buffer, ret, 1, stdout);
- }
- if (ret <= 0) break;
- }
-
- close(fd);
- if (!newline) printf("\n");
- if (title) printf("\n");
- return 0;
-}
-
-/* forks a command and waits for it to finish */
-int run_command(const char *title, int timeout_seconds, const char *command, ...) {
- fflush(stdout);
- clock_t start = clock();
- pid_t pid = fork();
-
- /* handle error case */
- if (pid < 0) {
- printf("*** fork: %s\n", strerror(errno));
- return pid;
- }
-
- /* handle child case */
- if (pid == 0) {
- const char *args[1024] = {command};
- size_t arg;
-
- va_list ap;
- va_start(ap, command);
- if (title) printf("------ %s (%s", title, command);
- for (arg = 1; arg < sizeof(args) / sizeof(args[0]); ++arg) {
- args[arg] = va_arg(ap, const char *);
- if (args[arg] == NULL) break;
- if (title) printf(" %s", args[arg]);
- }
- if (title) printf(") ------\n");
- fflush(stdout);
-
- execvp(command, (char**) args);
- printf("*** exec(%s): %s\n", command, strerror(errno));
- fflush(stdout);
- _exit(-1);
- }
-
- /* handle parent case */
- for (;;) {
- int status;
- pid_t p = waitpid(pid, &status, WNOHANG);
- float elapsed = (float) (clock() - start) / CLOCKS_PER_SEC;
- if (p == pid) {
- if (WIFSIGNALED(status)) {
- printf("*** %s: Killed by signal %d\n", command, WTERMSIG(status));
- } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) {
- printf("*** %s: Exit code %d\n", command, WEXITSTATUS(status));
- }
- if (title) printf("[%s: %.1fs elapsed]\n\n", command, elapsed);
- return status;
- }
-
- if (timeout_seconds && elapsed > timeout_seconds) {
- printf("*** %s: Timed out after %.1fs (killing pid %d)\n", command, elapsed, pid);
- kill(pid, SIGTERM);
- return -1;
- }
-
- usleep(100000); // poll every 0.1 sec
- }
-}
-
-size_t num_props = 0;
-static char* props[2000];
-
-static void print_prop(const char *key, const char *name, void *user) {
- (void) user;
- if (num_props < sizeof(props) / sizeof(props[0])) {
- char buf[PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX + 10];
- snprintf(buf, sizeof(buf), "[%s]: [%s]\n", key, name);
- props[num_props++] = strdup(buf);
- }
-}
-
-static int compare_prop(const void *a, const void *b) {
- return strcmp(*(char * const *) a, *(char * const *) b);
-}
-
-/* prints all the system properties */
-void print_properties() {
- size_t i;
- num_props = 0;
- property_list(print_prop, NULL);
- qsort(&props, num_props, sizeof(props[0]), compare_prop);
-
- printf("------ SYSTEM PROPERTIES ------\n");
- for (i = 0; i < num_props; ++i) {
- fputs(props[i], stdout);
- free(props[i]);
- }
- printf("\n");
-}
-
-/* redirect output to a service control socket */
-void redirect_to_socket(FILE *redirect, const char *service) {
- int s = android_get_control_socket(service);
- if (s < 0) {
- fprintf(stderr, "android_get_control_socket(%s): %s\n", service, strerror(errno));
- exit(1);
- }
- if (listen(s, 4) < 0) {
- fprintf(stderr, "listen(control socket): %s\n", strerror(errno));
- exit(1);
- }
-
- struct sockaddr addr;
- socklen_t alen = sizeof(addr);
- int fd = accept(s, &addr, &alen);
- if (fd < 0) {
- fprintf(stderr, "accept(control socket): %s\n", strerror(errno));
- exit(1);
- }
-
- fflush(redirect);
- dup2(fd, fileno(redirect));
- close(fd);
-}
-
-/* redirect output to a file, optionally gzipping; returns gzip pid (or -1) */
-pid_t redirect_to_file(FILE *redirect, char *path, int gzip_level) {
- char *chp = path;
-
- /* skip initial slash */
- if (chp[0] == '/')
- chp++;
-
- /* create leading directories, if necessary */
- while (chp && chp[0]) {
- chp = strchr(chp, '/');
- if (chp) {
- *chp = 0;
- mkdir(path, 0775); /* drwxrwxr-x */
- *chp++ = '/';
- }
- }
-
- int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
- if (fd < 0) {
- fprintf(stderr, "%s: %s\n", path, strerror(errno));
- exit(1);
- }
-
- pid_t gzip_pid = -1;
- if (gzip_level > 0) {
- int fds[2];
- if (pipe(fds)) {
- fprintf(stderr, "pipe: %s\n", strerror(errno));
- exit(1);
- }
-
- fflush(redirect);
- fflush(stdout);
-
- gzip_pid = fork();
- if (gzip_pid < 0) {
- fprintf(stderr, "fork: %s\n", strerror(errno));
- exit(1);
- }
-
- if (gzip_pid == 0) {
- dup2(fds[0], STDIN_FILENO);
- dup2(fd, STDOUT_FILENO);
-
- close(fd);
- close(fds[0]);
- close(fds[1]);
-
- char level[10];
- snprintf(level, sizeof(level), "-%d", gzip_level);
- execlp("gzip", "gzip", level, NULL);
- fprintf(stderr, "exec(gzip): %s\n", strerror(errno));
- _exit(-1);
- }
-
- close(fd);
- close(fds[0]);
- fd = fds[1];
- }
-
- dup2(fd, fileno(redirect));
- close(fd);
- return gzip_pid;
-}
-
-/* dump Dalvik stack traces, return the trace file location (NULL if none) */
-const char *dump_vm_traces() {
- char traces_path[PROPERTY_VALUE_MAX] = "";
- property_get("dalvik.vm.stack-trace-file", traces_path, "");
- if (!traces_path[0]) return NULL;
-
- /* move the old traces.txt (if any) out of the way temporarily */
- char anr_traces_path[PATH_MAX];
- strlcpy(anr_traces_path, traces_path, sizeof(anr_traces_path));
- strlcat(anr_traces_path, ".anr", sizeof(anr_traces_path));
- if (rename(traces_path, anr_traces_path) && errno != ENOENT) {
- fprintf(stderr, "rename(%s, %s): %s\n", traces_path, anr_traces_path, strerror(errno));
- return NULL; // Can't rename old traces.txt -- no permission? -- leave it alone instead
- }
-
- /* make the directory if necessary */
- char anr_traces_dir[PATH_MAX];
- strlcpy(anr_traces_dir, traces_path, sizeof(anr_traces_dir));
- char *slash = strrchr(anr_traces_dir, '/');
- if (slash != NULL) {
- *slash = '\0';
- if (!mkdir(anr_traces_dir, 0775)) {
- chown(anr_traces_dir, AID_SYSTEM, AID_SYSTEM);
- } else if (errno != EEXIST) {
- fprintf(stderr, "mkdir(%s): %s\n", anr_traces_dir, strerror(errno));
- return NULL;
- }
- }
-
- /* create a new, empty traces.txt file to receive stack dumps */
- int fd = open(traces_path, O_CREAT | O_WRONLY | O_TRUNC, 0666); /* -rw-rw-rw- */
- if (fd < 0) {
- fprintf(stderr, "%s: %s\n", traces_path, strerror(errno));
- return NULL;
- }
- close(fd);
-
- /* walk /proc and kill -QUIT all Dalvik processes */
- DIR *proc = opendir("/proc");
- if (proc == NULL) {
- fprintf(stderr, "/proc: %s\n", strerror(errno));
- return NULL;
- }
-
- /* use inotify to find when processes are done dumping */
- int ifd = inotify_init();
- if (ifd < 0) {
- fprintf(stderr, "inotify_init: %s\n", strerror(errno));
- return NULL;
- }
-
- int wfd = inotify_add_watch(ifd, traces_path, IN_CLOSE_WRITE);
- if (wfd < 0) {
- fprintf(stderr, "inotify_add_watch(%s): %s\n", traces_path, strerror(errno));
- return NULL;
- }
-
- struct dirent *d;
- int dalvik_found = 0;
- while ((d = readdir(proc))) {
- int pid = atoi(d->d_name);
- if (pid <= 0) continue;
-
- /* identify Dalvik: /proc/(pid)/exe = /system/bin/app_process */
- char path[PATH_MAX], data[PATH_MAX];
- snprintf(path, sizeof(path), "/proc/%d/exe", pid);
- size_t len = readlink(path, data, sizeof(data) - 1);
- if (len <= 0 || memcmp(data, "/system/bin/app_process", 23)) continue;
-
- /* skip zygote -- it won't dump its stack anyway */
- snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
- int fd = open(path, O_RDONLY);
- len = read(fd, data, sizeof(data) - 1);
- close(fd);
- if (len <= 0 || !memcmp(data, "zygote", 6)) continue;
-
- ++dalvik_found;
- if (kill(pid, SIGQUIT)) {
- fprintf(stderr, "kill(%d, SIGQUIT): %s\n", pid, strerror(errno));
- continue;
- }
-
- /* wait for the writable-close notification from inotify */
- struct pollfd pfd = { ifd, POLLIN, 0 };
- int ret = poll(&pfd, 1, 200); /* 200 msec timeout */
- if (ret < 0) {
- fprintf(stderr, "poll: %s\n", strerror(errno));
- } else if (ret == 0) {
- fprintf(stderr, "warning: timed out dumping pid %d\n", pid);
- } else {
- struct inotify_event ie;
- read(ifd, &ie, sizeof(ie));
- }
- }
-
- close(ifd);
- if (dalvik_found == 0) {
- fprintf(stderr, "Warning: no Dalvik processes found to dump stacks\n");
- }
-
- static char dump_traces_path[PATH_MAX];
- strlcpy(dump_traces_path, traces_path, sizeof(dump_traces_path));
- strlcat(dump_traces_path, ".bugreport", sizeof(dump_traces_path));
- if (rename(traces_path, dump_traces_path)) {
- fprintf(stderr, "rename(%s, %s): %s\n", traces_path, dump_traces_path, strerror(errno));
- return NULL;
- }
-
- /* replace the saved [ANR] traces.txt file */
- rename(anr_traces_path, traces_path);
- return dump_traces_path;
-}
-
-void play_sound(const char* path) {
- run_command(NULL, 5, "/system/bin/stagefright", "-o", "-a", path, NULL);
-}
diff --git a/cmds/dumpsys/Android.mk b/cmds/dumpsys/Android.mk
deleted file mode 100644
index 42b1b73..0000000
--- a/cmds/dumpsys/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- dumpsys.cpp
-
-LOCAL_SHARED_LIBRARIES := \
- libutils \
- libbinder
-
-
-ifeq ($(TARGET_OS),linux)
- LOCAL_CFLAGS += -DXP_UNIX
- #LOCAL_SHARED_LIBRARIES += librt
-endif
-
-LOCAL_MODULE:= dumpsys
-
-include $(BUILD_EXECUTABLE)
diff --git a/cmds/dumpsys/MODULE_LICENSE_APACHE2 b/cmds/dumpsys/MODULE_LICENSE_APACHE2
deleted file mode 100644
index e69de29..0000000
--- a/cmds/dumpsys/MODULE_LICENSE_APACHE2
+++ /dev/null
diff --git a/cmds/dumpsys/NOTICE b/cmds/dumpsys/NOTICE
deleted file mode 100644
index c5b1efa..0000000
--- a/cmds/dumpsys/NOTICE
+++ /dev/null
@@ -1,190 +0,0 @@
-
- Copyright (c) 2005-2008, 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.
-
- 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.
-
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
deleted file mode 100644
index 7dad6b6..0000000
--- a/cmds/dumpsys/dumpsys.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Command that dumps interesting system state to the log.
- *
- */
-
-#define LOG_TAG "dumpsys"
-
-#include <utils/Log.h>
-#include <binder/Parcel.h>
-#include <binder/ProcessState.h>
-#include <binder/IServiceManager.h>
-#include <utils/TextOutput.h>
-#include <utils/Vector.h>
-
-#include <getopt.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/time.h>
-
-using namespace android;
-
-static int sort_func(const String16* lhs, const String16* rhs)
-{
- return lhs->compare(*rhs);
-}
-
-int main(int argc, char* const argv[])
-{
- sp<IServiceManager> sm = defaultServiceManager();
- fflush(stdout);
- if (sm == NULL) {
- ALOGE("Unable to get default service manager!");
- aerr << "dumpsys: Unable to get default service manager!" << endl;
- return 20;
- }
-
- Vector<String16> services;
- Vector<String16> args;
- if (argc == 1) {
- services = sm->listServices();
- services.sort(sort_func);
- args.add(String16("-a"));
- } else {
- services.add(String16(argv[1]));
- for (int i=2; i<argc; i++) {
- args.add(String16(argv[i]));
- }
- }
-
- const size_t N = services.size();
-
- if (N > 1) {
- // first print a list of the current services
- aout << "Currently running services:" << endl;
-
- for (size_t i=0; i<N; i++) {
- sp<IBinder> service = sm->checkService(services[i]);
- if (service != NULL) {
- aout << " " << services[i] << endl;
- }
- }
- }
-
- for (size_t i=0; i<N; i++) {
- sp<IBinder> service = sm->checkService(services[i]);
- if (service != NULL) {
- if (N > 1) {
- aout << "------------------------------------------------------------"
- "-------------------" << endl;
- aout << "DUMP OF SERVICE " << services[i] << ":" << endl;
- }
- int err = service->dump(STDOUT_FILENO, args);
- if (err != 0) {
- aerr << "Error dumping service info: (" << strerror(err)
- << ") " << services[i] << endl;
- }
- } else {
- aerr << "Can't find service: " << services[i] << endl;
- }
- }
-
- return 0;
-}
diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c
index 7f94a96..c2c749a 100644
--- a/cmds/installd/installd.c
+++ b/cmds/installd/installd.c
@@ -332,12 +332,15 @@ int initialize_directories() {
ret = 0;
// Make the /data/user directory if necessary
if (access(user_data_dir, R_OK) < 0) {
- if (mkdir(user_data_dir, 0755) < 0) {
+ if (mkdir(user_data_dir, 0711) < 0) {
return -1;
}
if (chown(user_data_dir, AID_SYSTEM, AID_SYSTEM) < 0) {
return -1;
}
+ if (chmod(user_data_dir, 0711) < 0) {
+ return -1;
+ }
}
// Make the /data/user/0 symlink to /data/data if necessary
if (access(primary_data_dir, R_OK) < 0) {
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
index 8b55c8f..52a9293 100644
--- a/cmds/stagefright/Android.mk
+++ b/cmds/stagefright/Android.mk
@@ -4,18 +4,18 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
stagefright.cpp \
+ jpeg.cpp \
SineSource.cpp
LOCAL_SHARED_LIBRARIES := \
libstagefright libmedia libmedia_native libutils libbinder libstagefright_foundation \
- libskia libgui
+ libjpeg libgui
LOCAL_C_INCLUDES:= \
frameworks/base/media/libstagefright \
frameworks/base/media/libstagefright/include \
$(TOP)/frameworks/native/include/media/openmax \
- external/skia/include/core \
- external/skia/include/images \
+ external/jpeg \
LOCAL_CFLAGS += -Wno-multichar
diff --git a/cmds/stagefright/codec.cpp b/cmds/stagefright/codec.cpp
index fea62cc..cf2909e 100644
--- a/cmds/stagefright/codec.cpp
+++ b/cmds/stagefright/codec.cpp
@@ -28,6 +28,7 @@
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaCodec.h>
+#include <media/stagefright/MediaCodecList.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/NuMediaExtractor.h>
#include <gui/SurfaceComposerClient.h>
@@ -36,7 +37,9 @@ static void usage(const char *me) {
fprintf(stderr, "usage: %s [-a] use audio\n"
"\t\t[-v] use video\n"
"\t\t[-p] playback\n"
- "\t\t[-S] allocate buffers from a surface\n", me);
+ "\t\t[-S] allocate buffers from a surface\n"
+ "\t\t[-D] decrypt input buffers\n",
+ me);
exit(1);
}
@@ -63,7 +66,8 @@ static int decode(
const char *path,
bool useAudio,
bool useVideo,
- const android::sp<android::Surface> &surface) {
+ const android::sp<android::Surface> &surface,
+ bool decryptInputBuffers) {
using namespace android;
static int64_t kTimeout = 500ll;
@@ -109,13 +113,31 @@ static int decode(
state->mNumBuffersDecoded = 0;
state->mIsAudio = isAudio;
- state->mCodec = MediaCodec::CreateByType(
- looper, mime.c_str(), false /* encoder */);
+ if (decryptInputBuffers && !isAudio) {
+ static const MediaCodecList *list = MediaCodecList::getInstance();
+
+ ssize_t index =
+ list->findCodecByType(mime.c_str(), false /* encoder */);
+
+ CHECK_GE(index, 0);
+
+ const char *componentName = list->getCodecName(index);
+
+ AString fullName = componentName;
+ fullName.append(".secure");
+
+ state->mCodec = MediaCodec::CreateByComponentName(
+ looper, fullName.c_str());
+ } else {
+ state->mCodec = MediaCodec::CreateByType(
+ looper, mime.c_str(), false /* encoder */);
+ }
CHECK(state->mCodec != NULL);
err = state->mCodec->configure(
- format, isVideo ? surface : NULL, 0 /* flags */);
+ format, isVideo ? surface : NULL,
+ decryptInputBuffers ? MediaCodec::CONFIGURE_FLAG_SECURE : 0);
CHECK_EQ(err, (status_t)OK);
@@ -202,12 +224,24 @@ static int decode(
err = extractor->getSampleTime(&timeUs);
CHECK_EQ(err, (status_t)OK);
+ uint32_t bufferFlags = 0;
+
+ uint32_t sampleFlags;
+ err = extractor->getSampleFlags(&sampleFlags);
+ CHECK_EQ(err, (status_t)OK);
+
+ if (sampleFlags & NuMediaExtractor::SAMPLE_FLAG_ENCRYPTED) {
+ CHECK(decryptInputBuffers);
+
+ bufferFlags |= MediaCodec::BUFFER_FLAG_ENCRYPTED;
+ }
+
err = state->mCodec->queueInputBuffer(
index,
0 /* offset */,
buffer->size(),
timeUs,
- 0 /* flags */);
+ bufferFlags);
CHECK_EQ(err, (status_t)OK);
@@ -341,9 +375,10 @@ int main(int argc, char **argv) {
bool useVideo = false;
bool playback = false;
bool useSurface = false;
+ bool decryptInputBuffers = false;
int res;
- while ((res = getopt(argc, argv, "havpS")) >= 0) {
+ while ((res = getopt(argc, argv, "havpSD")) >= 0) {
switch (res) {
case 'a':
{
@@ -369,6 +404,12 @@ int main(int argc, char **argv) {
break;
}
+ case 'D':
+ {
+ decryptInputBuffers = true;
+ break;
+ }
+
case '?':
case 'h':
default:
@@ -440,7 +481,8 @@ int main(int argc, char **argv) {
player->stop();
player->reset();
} else {
- decode(looper, argv[0], useAudio, useVideo, surface);
+ decode(looper, argv[0],
+ useAudio, useVideo, surface, decryptInputBuffers);
}
if (playback || (useSurface && useVideo)) {
diff --git a/cmds/stagefright/jpeg.cpp b/cmds/stagefright/jpeg.cpp
new file mode 100644
index 0000000..7e859c3
--- /dev/null
+++ b/cmds/stagefright/jpeg.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <setjmp.h>
+#include <stdio.h>
+
+extern "C" {
+#include "jpeglib.h"
+}
+
+static inline uint8_t from565to8(uint16_t p, int start, int bits) {
+ uint8_t c = (p >> start) & ((1 << bits) - 1);
+ return (c << (8 - bits)) | (c >> (bits - (8 - bits)));
+}
+
+struct sf_jpeg_error_mgr {
+ struct jpeg_error_mgr jerr;
+ jmp_buf longjmp_buffer;
+};
+
+void sf_jpeg_error_exit(j_common_ptr cinfo) {
+ struct sf_jpeg_error_mgr *sf_err = (struct sf_jpeg_error_mgr *)cinfo->err;
+ longjmp(sf_err->longjmp_buffer, 0);
+}
+
+int writeJpegFile(const char *filename, uint8_t *frame, int width, int height) {
+ struct sf_jpeg_error_mgr sf_err;
+ struct jpeg_compress_struct cinfo;
+ uint8_t row_data[width * 3];
+ JSAMPROW row_pointer = row_data;
+ FILE *f;
+
+ f = fopen(filename, "w");
+ if (!f) {
+ return -errno;
+ }
+
+ cinfo.err = jpeg_std_error(&sf_err.jerr);
+ sf_err.jerr.error_exit = sf_jpeg_error_exit;
+ if (setjmp(sf_err.longjmp_buffer)) {
+ jpeg_destroy_compress(&cinfo);
+ fclose(f);
+ return -1;
+ }
+
+ jpeg_create_compress(&cinfo);
+ jpeg_stdio_dest(&cinfo, f);
+
+ cinfo.image_width = width;
+ cinfo.image_height = height;
+ cinfo.input_components = 3;
+ cinfo.in_color_space = JCS_RGB;
+
+ jpeg_set_defaults(&cinfo);
+ jpeg_set_quality(&cinfo, 80, TRUE);
+
+ jpeg_start_compress(&cinfo, TRUE);
+
+ for (int row = 0; row < height; row++) {
+ uint16_t *src = (uint16_t *)(frame + row * width * 2);
+ uint8_t *dst = row_data;
+ for (int col = 0; col < width; col++) {
+ dst[0] = from565to8(*src, 11, 5);
+ dst[1] = from565to8(*src, 5, 6);
+ dst[2] = from565to8(*src, 0, 5);
+ dst += 3;
+ src++;
+ }
+ jpeg_write_scanlines(&cinfo, &row_pointer, 1);
+ }
+
+ jpeg_finish_compress(&cinfo);
+ jpeg_destroy_compress(&cinfo);
+
+ fclose(f);
+ return 0;
+}
diff --git a/cmds/stagefright/jpeg.h b/cmds/stagefright/jpeg.h
new file mode 100644
index 0000000..ce86cf2
--- /dev/null
+++ b/cmds/stagefright/jpeg.h
@@ -0,0 +1,6 @@
+#ifndef _STAGEFRIGHT_JPEG_H_
+#define _STAGEFRIGHT_JPEG_H_
+
+int writeJpegFile(const char *filename, uint8_t *frame, int width, int height);
+
+#endif
diff --git a/cmds/stagefright/sf2.cpp b/cmds/stagefright/sf2.cpp
index 64df5d1..3bbfbdc 100644
--- a/cmds/stagefright/sf2.cpp
+++ b/cmds/stagefright/sf2.cpp
@@ -287,6 +287,11 @@ private:
msg->setInt32("channel-count", numChannels);
msg->setInt32("sample-rate", sampleRate);
+
+ int32_t isADTS;
+ if (meta->findInt32(kKeyIsADTS, &isADTS) && isADTS != 0) {
+ msg->setInt32("is-adts", true);
+ }
}
uint32_t type;
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index dab2e0f..d70c862 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -24,6 +24,7 @@
#include <string.h>
#include <unistd.h>
+#include "jpeg.h"
#include "SineSource.h"
#include <binder/IServiceManager.h>
@@ -49,8 +50,6 @@
#include <media/stagefright/MPEG4Writer.h>
#include <private/media/VideoFrame.h>
-#include <SkBitmap.h>
-#include <SkImageEncoder.h>
#include <fcntl.h>
@@ -787,16 +786,9 @@ int main(int argc, char **argv) {
VideoFrame *frame = (VideoFrame *)mem->pointer();
- SkBitmap bitmap;
- bitmap.setConfig(
- SkBitmap::kRGB_565_Config, frame->mWidth, frame->mHeight);
-
- bitmap.setPixels((uint8_t *)frame + sizeof(VideoFrame));
-
- CHECK(SkImageEncoder::EncodeFile(
- "/sdcard/out.jpg", bitmap,
- SkImageEncoder::kJPEG_Type,
- SkImageEncoder::kDefaultQuality));
+ CHECK_EQ(writeJpegFile("/sdcard/out.jpg",
+ (uint8_t *)frame + sizeof(VideoFrame),
+ frame->mWidth, frame->mHeight), 0);
}
{
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index 27c9c8b..197c1bd 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -231,6 +231,14 @@ public class AccountManagerService
}
}, intentFilter);
+ IntentFilter userFilter = new IntentFilter();
+ userFilter.addAction(Intent.ACTION_USER_REMOVED);
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ onUserRemoved(intent);
+ }
+ }, userFilter);
}
private UserAccounts initUser(int userId) {
@@ -347,6 +355,28 @@ public class AccountManagerService
}
}
+ private void onUserRemoved(Intent intent) {
+ int userId = intent.getIntExtra(Intent.EXTRA_USERID, -1);
+ if (userId < 1) return;
+
+ UserAccounts accounts;
+ synchronized (mUsers) {
+ accounts = mUsers.get(userId);
+ mUsers.remove(userId);
+ }
+ if (accounts == null) {
+ File dbFile = new File(getDatabaseName(userId));
+ dbFile.delete();
+ return;
+ }
+
+ synchronized (accounts.cacheLock) {
+ accounts.openHelper.close();
+ File dbFile = new File(getDatabaseName(userId));
+ dbFile.delete();
+ }
+ }
+
private List<UserInfo> getAllUsers() {
try {
return AppGlobals.getPackageManager().getUsers();
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index f38540c..0510de1 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1215,6 +1215,18 @@ final class ApplicationPackageManager extends PackageManager {
* @hide
*/
@Override
+ public UserInfo getUser(int userId) {
+ try {
+ return mPM.getUser(userId);
+ } catch (RemoteException re) {
+ return null;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
public boolean removeUser(int id) {
try {
return mPM.removeUser(id);
@@ -1228,7 +1240,10 @@ final class ApplicationPackageManager extends PackageManager {
*/
@Override
public void updateUserName(int id, String name) {
- // TODO:
+ try {
+ mPM.updateUserName(id, name);
+ } catch (RemoteException re) {
+ }
}
/**
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 1c9ef38..2a9f1af 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2141,6 +2141,30 @@ public class Intent implements Parcelable, Cloneable {
public static final String ACTION_PRE_BOOT_COMPLETED =
"android.intent.action.PRE_BOOT_COMPLETED";
+ /**
+ * Broadcast sent to the system when a user is added. Carries an extra EXTRA_USERID that has the
+ * userid of the new user.
+ * @hide
+ */
+ public static final String ACTION_USER_ADDED =
+ "android.intent.action.USER_ADDED";
+
+ /**
+ * Broadcast sent to the system when a user is removed. Carries an extra EXTRA_USERID that has
+ * the userid of the user.
+ * @hide
+ */
+ public static final String ACTION_USER_REMOVED =
+ "android.intent.action.USER_REMOVED";
+
+ /**
+ * Broadcast sent to the system when the user switches. Carries an extra EXTRA_USERID that has
+ * the userid of the user to become the current one.
+ * @hide
+ */
+ public static final String ACTION_USER_SWITCHED =
+ "android.intent.action.USER_SWITCHED";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Standard intent categories (see addCategory()).
@@ -2682,6 +2706,13 @@ public class Intent implements Parcelable, Cloneable {
public static final String EXTRA_LOCAL_ONLY =
"android.intent.extra.LOCAL_ONLY";
+ /**
+ * The userid carried with broadcast intents related to addition, removal and switching of users
+ * - {@link #ACTION_USER_ADDED}, {@link #ACTION_USER_REMOVED} and {@link #ACTION_USER_SWITCHED}.
+ * @hide
+ */
+ public static final String EXTRA_USERID =
+ "android.intent.extra.user_id";
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Intent flags (see mFlags variable).
@@ -6483,7 +6514,12 @@ public class Intent implements Parcelable, Cloneable {
final String action = getAction();
if (ACTION_SEND.equals(action)) {
- final Uri stream = getParcelableExtra(EXTRA_STREAM);
+ final Uri stream;
+ try {
+ stream = getParcelableExtra(EXTRA_STREAM);
+ } catch (ClassCastException e) {
+ return;
+ }
if (stream != null) {
final ClipData clipData = new ClipData(
null, new String[] { getType() }, new ClipData.Item(stream));
@@ -6493,7 +6529,12 @@ public class Intent implements Parcelable, Cloneable {
}
} else if (ACTION_SEND_MULTIPLE.equals(action)) {
- final ArrayList<Uri> streams = getParcelableArrayListExtra(EXTRA_STREAM);
+ final ArrayList<Uri> streams;
+ try {
+ streams = getParcelableArrayListExtra(EXTRA_STREAM);
+ } catch (ClassCastException e) {
+ return;
+ }
if (streams != null && streams.size() > 0) {
final Uri firstStream = streams.get(0);
final ClipData clipData = new ClipData(
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index b7dfe92..06dfe90 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -326,6 +326,13 @@ public class SyncManager implements OnAccountsUpdateListener {
}
};
+ private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ onUserRemoved(intent);
+ }
+ };
+
private static final String ACTION_SYNC_ALARM = "android.content.syncmanager.SYNC_ALARM";
private final SyncHandler mSyncHandler;
@@ -420,6 +427,10 @@ public class SyncManager implements OnAccountsUpdateListener {
intentFilter.setPriority(100);
context.registerReceiver(mShutdownIntentReceiver, intentFilter);
+ intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_USER_REMOVED);
+ mContext.registerReceiver(mUserIntentReceiver, intentFilter);
+
if (!factoryTest) {
mNotificationMgr = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
@@ -905,6 +916,18 @@ public class SyncManager implements OnAccountsUpdateListener {
}
}
+ private void onUserRemoved(Intent intent) {
+ int userId = intent.getIntExtra(Intent.EXTRA_USERID, -1);
+ if (userId == -1) return;
+
+ // Clean up the storage engine database
+ mSyncStorageEngine.doDatabaseCleanup(new Account[0], userId);
+ onAccountsUpdated(null);
+ synchronized (mSyncQueue) {
+ mSyncQueue.removeUser(userId);
+ }
+ }
+
/**
* @hide
*/
diff --git a/core/java/android/content/SyncQueue.java b/core/java/android/content/SyncQueue.java
index 06da6fa..c18c86b 100644
--- a/core/java/android/content/SyncQueue.java
+++ b/core/java/android/content/SyncQueue.java
@@ -117,6 +117,19 @@ public class SyncQueue {
return true;
}
+ public void removeUser(int userId) {
+ ArrayList<SyncOperation> opsToRemove = new ArrayList<SyncOperation>();
+ for (SyncOperation op : mOperationsMap.values()) {
+ if (op.userId == userId) {
+ opsToRemove.add(op);
+ }
+ }
+
+ for (SyncOperation op : opsToRemove) {
+ remove(op);
+ }
+ }
+
/**
* Remove the specified operation if it is in the queue.
* @param operation the operation to remove
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index d89d2de..56fd5f8 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -358,6 +358,7 @@ interface IPackageManager {
UserInfo createUser(in String name, int flags);
boolean removeUser(int userId);
+ void updateUserName(int userId, String name);
void installPackageWithVerification(in Uri packageURI, in IPackageInstallObserver observer,
int flags, in String installerPackageName, in Uri verificationURI,
@@ -370,6 +371,7 @@ interface IPackageManager {
boolean isFirstBoot();
List<UserInfo> getUsers();
+ UserInfo getUser(int userId);
void setPermissionEnforcement(String permission, int enforcement);
int getPermissionEnforcement(String permission);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 55426b8..b06b4a5 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2153,7 +2153,8 @@ public abstract class PackageManager {
if ((flags & GET_SIGNATURES) != 0) {
packageParser.collectCertificates(pkg, 0);
}
- return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null);
+ return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, false,
+ COMPONENT_ENABLED_STATE_DEFAULT);
}
/**
@@ -2637,10 +2638,17 @@ public abstract class PackageManager {
public abstract void updateUserFlags(int id, int flags);
/**
- * Returns the device identity that verifiers can use to associate their
- * scheme to a particular device. This should not be used by anything other
- * than a package verifier.
- *
+ * Returns the details for the user specified by userId.
+ * @param userId the user id of the user
+ * @return UserInfo for the specified user, or null if no such user exists.
+ * @hide
+ */
+ public abstract UserInfo getUser(int userId);
+
+ /**
+ * Returns the device identity that verifiers can use to associate their scheme to a particular
+ * device. This should not be used by anything other than a package verifier.
+ *
* @return identity that uniquely identifies current device
* @hide
*/
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 07d231a..eb8536f 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -230,6 +230,15 @@ public class PackageParser {
return name.endsWith(".apk");
}
+ public static PackageInfo generatePackageInfo(PackageParser.Package p,
+ int gids[], int flags, long firstInstallTime, long lastUpdateTime,
+ HashSet<String> grantedPermissions) {
+
+ return generatePackageInfo(p, gids, flags, firstInstallTime, lastUpdateTime,
+ grantedPermissions, false, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
+ UserId.getCallingUserId());
+ }
+
/**
* Generate and return the {@link PackageInfo} for a parsed package.
*
@@ -238,15 +247,15 @@ public class PackageParser {
*/
public static PackageInfo generatePackageInfo(PackageParser.Package p,
int gids[], int flags, long firstInstallTime, long lastUpdateTime,
- HashSet<String> grantedPermissions) {
+ HashSet<String> grantedPermissions, boolean stopped, int enabledState) {
return generatePackageInfo(p, gids, flags, firstInstallTime, lastUpdateTime,
- grantedPermissions, UserId.getCallingUserId());
+ grantedPermissions, stopped, enabledState, UserId.getCallingUserId());
}
- static PackageInfo generatePackageInfo(PackageParser.Package p,
+ public static PackageInfo generatePackageInfo(PackageParser.Package p,
int gids[], int flags, long firstInstallTime, long lastUpdateTime,
- HashSet<String> grantedPermissions, int userId) {
+ HashSet<String> grantedPermissions, boolean stopped, int enabledState, int userId) {
PackageInfo pi = new PackageInfo();
pi.packageName = p.packageName;
@@ -254,7 +263,7 @@ public class PackageParser {
pi.versionName = p.mVersionName;
pi.sharedUserId = p.mSharedUserId;
pi.sharedUserLabel = p.mSharedUserLabel;
- pi.applicationInfo = generateApplicationInfo(p, flags);
+ pi.applicationInfo = generateApplicationInfo(p, flags, stopped, enabledState, userId);
pi.installLocation = p.installLocation;
pi.firstInstallTime = firstInstallTime;
pi.lastUpdateTime = lastUpdateTime;
@@ -290,7 +299,7 @@ public class PackageParser {
if (activity.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
pi.activities[j++] = generateActivityInfo(p.activities.get(i), flags,
- userId);
+ stopped, enabledState, userId);
}
}
}
@@ -311,7 +320,8 @@ public class PackageParser {
final Activity activity = p.receivers.get(i);
if (activity.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
- pi.receivers[j++] = generateActivityInfo(p.receivers.get(i), flags, userId);
+ pi.receivers[j++] = generateActivityInfo(p.receivers.get(i), flags,
+ stopped, enabledState, userId);
}
}
}
@@ -332,7 +342,8 @@ public class PackageParser {
final Service service = p.services.get(i);
if (service.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
- pi.services[j++] = generateServiceInfo(p.services.get(i), flags, userId);
+ pi.services[j++] = generateServiceInfo(p.services.get(i), flags, stopped,
+ enabledState, userId);
}
}
}
@@ -353,7 +364,8 @@ public class PackageParser {
final Provider provider = p.providers.get(i);
if (provider.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
- pi.providers[j++] = generateProviderInfo(p.providers.get(i), flags, userId);
+ pi.providers[j++] = generateProviderInfo(p.providers.get(i), flags, stopped,
+ enabledState, userId);
}
}
}
@@ -3068,11 +3080,11 @@ public class PackageParser {
// For use by package manager to keep track of where it has done dexopt.
public boolean mDidDexOpt;
- // User set enabled state.
- public int mSetEnabled = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
-
- // Whether the package has been stopped.
- public boolean mSetStopped = false;
+ // // User set enabled state.
+ // public int mSetEnabled = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+ //
+ // // Whether the package has been stopped.
+ // public boolean mSetStopped = false;
// Additional data supplied by callers.
public Object mExtras;
@@ -3337,9 +3349,9 @@ public class PackageParser {
}
}
- private static boolean copyNeeded(int flags, Package p, Bundle metaData) {
- if (p.mSetEnabled != PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
- boolean enabled = p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+ private static boolean copyNeeded(int flags, Package p, int enabledState, Bundle metaData) {
+ if (enabledState != PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
+ boolean enabled = enabledState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
if (p.applicationInfo.enabled != enabled) {
return true;
}
@@ -3355,30 +3367,32 @@ public class PackageParser {
return false;
}
- public static ApplicationInfo generateApplicationInfo(Package p, int flags) {
- return generateApplicationInfo(p, flags, UserId.getCallingUserId());
+ public static ApplicationInfo generateApplicationInfo(Package p, int flags, boolean stopped,
+ int enabledState) {
+ return generateApplicationInfo(p, flags, stopped, enabledState, UserId.getCallingUserId());
}
- public static ApplicationInfo generateApplicationInfo(Package p, int flags, int userId) {
+ public static ApplicationInfo generateApplicationInfo(Package p, int flags,
+ boolean stopped, int enabledState, int userId) {
if (p == null) return null;
- if (!copyNeeded(flags, p, null) && userId == 0) {
+ if (!copyNeeded(flags, p, enabledState, null) && userId == 0) {
// CompatibilityMode is global state. It's safe to modify the instance
// of the package.
if (!sCompatibilityModeEnabled) {
p.applicationInfo.disableCompatibilityMode();
}
- if (p.mSetStopped) {
+ if (stopped) {
p.applicationInfo.flags |= ApplicationInfo.FLAG_STOPPED;
} else {
p.applicationInfo.flags &= ~ApplicationInfo.FLAG_STOPPED;
}
- if (p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
+ if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
p.applicationInfo.enabled = true;
- } else if (p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
- || p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+ } else if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+ || enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
p.applicationInfo.enabled = false;
}
- p.applicationInfo.enabledSetting = p.mSetEnabled;
+ p.applicationInfo.enabledSetting = enabledState;
return p.applicationInfo;
}
@@ -3397,18 +3411,18 @@ public class PackageParser {
if (!sCompatibilityModeEnabled) {
ai.disableCompatibilityMode();
}
- if (p.mSetStopped) {
+ if (stopped) {
p.applicationInfo.flags |= ApplicationInfo.FLAG_STOPPED;
} else {
p.applicationInfo.flags &= ~ApplicationInfo.FLAG_STOPPED;
}
- if (p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
+ if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
ai.enabled = true;
- } else if (p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
- || p.mSetEnabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+ } else if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+ || enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
ai.enabled = false;
}
- ai.enabledSetting = p.mSetEnabled;
+ ai.enabledSetting = enabledState;
return ai;
}
@@ -3455,15 +3469,16 @@ public class PackageParser {
}
}
- public static final ActivityInfo generateActivityInfo(Activity a, int flags, int userId) {
+ public static final ActivityInfo generateActivityInfo(Activity a, int flags, boolean stopped,
+ int enabledState, int userId) {
if (a == null) return null;
- if (!copyNeeded(flags, a.owner, a.metaData) && userId == 0) {
+ if (!copyNeeded(flags, a.owner, enabledState, a.metaData) && userId == 0) {
return a.info;
}
// Make shallow copies so we can store the metadata safely
ActivityInfo ai = new ActivityInfo(a.info);
ai.metaData = a.metaData;
- ai.applicationInfo = generateApplicationInfo(a.owner, flags, userId);
+ ai.applicationInfo = generateApplicationInfo(a.owner, flags, stopped, enabledState, userId);
return ai;
}
@@ -3488,16 +3503,17 @@ public class PackageParser {
}
}
- public static final ServiceInfo generateServiceInfo(Service s, int flags, int userId) {
+ public static final ServiceInfo generateServiceInfo(Service s, int flags, boolean stopped,
+ int enabledState, int userId) {
if (s == null) return null;
- if (!copyNeeded(flags, s.owner, s.metaData)
+ if (!copyNeeded(flags, s.owner, enabledState, s.metaData)
&& userId == UserId.getUserId(s.info.applicationInfo.uid)) {
return s.info;
}
// Make shallow copies so we can store the metadata safely
ServiceInfo si = new ServiceInfo(s.info);
si.metaData = s.metaData;
- si.applicationInfo = generateApplicationInfo(s.owner, flags, userId);
+ si.applicationInfo = generateApplicationInfo(s.owner, flags, stopped, enabledState, userId);
return si;
}
@@ -3530,9 +3546,10 @@ public class PackageParser {
}
}
- public static final ProviderInfo generateProviderInfo(Provider p, int flags, int userId) {
+ public static final ProviderInfo generateProviderInfo(Provider p, int flags, boolean stopped,
+ int enabledState, int userId) {
if (p == null) return null;
- if (!copyNeeded(flags, p.owner, p.metaData)
+ if (!copyNeeded(flags, p.owner, enabledState, p.metaData)
&& ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) != 0
|| p.info.uriPermissionPatterns == null)
&& userId == 0) {
@@ -3544,7 +3561,7 @@ public class PackageParser {
if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) {
pi.uriPermissionPatterns = null;
}
- pi.applicationInfo = generateApplicationInfo(p.owner, flags, userId);
+ pi.applicationInfo = generateApplicationInfo(p.owner, flags, stopped, enabledState, userId);
return pi;
}
diff --git a/core/java/android/database/sqlite/SQLiteSession.java b/core/java/android/database/sqlite/SQLiteSession.java
index 43efb03..9410243 100644
--- a/core/java/android/database/sqlite/SQLiteSession.java
+++ b/core/java/android/database/sqlite/SQLiteSession.java
@@ -398,16 +398,16 @@ public final class SQLiteSession {
throwIfNoTransaction();
assert mConnection != null;
- endTransactionUnchecked(cancellationSignal);
+ endTransactionUnchecked(cancellationSignal, false);
}
- private void endTransactionUnchecked(CancellationSignal cancellationSignal) {
+ private void endTransactionUnchecked(CancellationSignal cancellationSignal, boolean yielding) {
if (cancellationSignal != null) {
cancellationSignal.throwIfCanceled();
}
final Transaction top = mTransactionStack;
- boolean successful = top.mMarkedSuccessful && !top.mChildFailed;
+ boolean successful = (top.mMarkedSuccessful || yielding) && !top.mChildFailed;
RuntimeException listenerException = null;
final SQLiteTransactionListener listener = top.mListener;
@@ -534,7 +534,7 @@ public final class SQLiteSession {
final int transactionMode = mTransactionStack.mMode;
final SQLiteTransactionListener listener = mTransactionStack.mListener;
final int connectionFlags = mConnectionFlags;
- endTransactionUnchecked(cancellationSignal); // might throw
+ endTransactionUnchecked(cancellationSignal, true); // might throw
if (sleepAfterYieldDelayMillis > 0) {
try {
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index 5c4b258..6a4f1f2 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -18,13 +18,11 @@ package android.net;
import android.os.SystemProperties;
import android.util.Log;
-
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.security.KeyManagementException;
import java.security.cert.X509Certificate;
-
import javax.net.SocketFactory;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
@@ -36,7 +34,6 @@ import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
-
import org.apache.harmony.xnet.provider.jsse.OpenSSLContextImpl;
import org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl;
import org.apache.harmony.xnet.provider.jsse.SSLClientSessionCache;
@@ -89,6 +86,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
private SSLSocketFactory mSecureFactory = null;
private TrustManager[] mTrustManagers = null;
private KeyManager[] mKeyManagers = null;
+ private byte[] mNpnProtocols = null;
private final int mHandshakeTimeoutMillis;
private final SSLClientSessionCache mSessionCache;
@@ -251,6 +249,60 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
}
/**
+ * Sets the <a href="http://technotes.googlecode.com/git/nextprotoneg.html">Next
+ * Protocol Negotiation (NPN)</a> protocols that this peer is interested in.
+ *
+ * <p>For servers this is the sequence of protocols to advertise as
+ * supported, in order of preference. This list is sent unencrypted to
+ * all clients that support NPN.
+ *
+ * <p>For clients this is a list of supported protocols to match against the
+ * server's list. If there is no protocol supported by both client and
+ * server then the first protocol in the client's list will be selected.
+ * The order of the client's protocols is otherwise insignificant.
+ *
+ * @param npnProtocols a possibly-empty list of protocol byte arrays. All
+ * arrays must be non-empty and of length less than 256.
+ */
+ public void setNpnProtocols(byte[][] npnProtocols) {
+ this.mNpnProtocols = toNpnProtocolsList(npnProtocols);
+ }
+
+ /**
+ * Returns an array containing the concatenation of length-prefixed byte
+ * strings.
+ */
+ static byte[] toNpnProtocolsList(byte[]... npnProtocols) {
+ int totalLength = 0;
+ for (byte[] s : npnProtocols) {
+ if (s.length == 0 || s.length > 255) {
+ throw new IllegalArgumentException("s.length == 0 || s.length > 255: " + s.length);
+ }
+ totalLength += 1 + s.length;
+ }
+ byte[] result = new byte[totalLength];
+ int pos = 0;
+ for (byte[] s : npnProtocols) {
+ result[pos++] = (byte) s.length;
+ for (byte b : s) {
+ result[pos++] = b;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns the <a href="http://technotes.googlecode.com/git/nextprotoneg.html">Next
+ * Protocol Negotiation (NPN)</a> protocol selected by client and server, or
+ * null if no protocol was negotiated.
+ *
+ * @param socket a socket created by this factory.
+ */
+ public byte[] getNpnSelectedProtocol(Socket socket) {
+ return ((OpenSSLSocketImpl) socket).getNpnSelectedProtocol();
+ }
+
+ /**
* Sets the {@link KeyManager}s to be used for connections made by this factory.
*/
public void setKeyManagers(KeyManager[] keyManagers) {
@@ -271,6 +323,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
@Override
public Socket createSocket(Socket k, String host, int port, boolean close) throws IOException {
OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(k, host, port, close);
+ s.setNpnProtocols(mNpnProtocols);
s.setHandshakeTimeout(mHandshakeTimeoutMillis);
if (mSecure) {
verifyHostname(s, host);
@@ -289,6 +342,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
@Override
public Socket createSocket() throws IOException {
OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket();
+ s.setNpnProtocols(mNpnProtocols);
s.setHandshakeTimeout(mHandshakeTimeoutMillis);
return s;
}
@@ -305,6 +359,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
throws IOException {
OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(
addr, port, localAddr, localPort);
+ s.setNpnProtocols(mNpnProtocols);
s.setHandshakeTimeout(mHandshakeTimeoutMillis);
return s;
}
@@ -319,6 +374,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
@Override
public Socket createSocket(InetAddress addr, int port) throws IOException {
OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(addr, port);
+ s.setNpnProtocols(mNpnProtocols);
s.setHandshakeTimeout(mHandshakeTimeoutMillis);
return s;
}
@@ -334,6 +390,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
throws IOException {
OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(
host, port, localAddr, localPort);
+ s.setNpnProtocols(mNpnProtocols);
s.setHandshakeTimeout(mHandshakeTimeoutMillis);
if (mSecure) {
verifyHostname(s, host);
@@ -350,6 +407,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
@Override
public Socket createSocket(String host, int port) throws IOException {
OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(host, port);
+ s.setNpnProtocols(mNpnProtocols);
s.setHandshakeTimeout(mHandshakeTimeoutMillis);
if (mSecure) {
verifyHostname(s, host);
diff --git a/core/java/android/view/DisplayList.java b/core/java/android/view/DisplayList.java
index a50f09f..e2aafa9 100644
--- a/core/java/android/view/DisplayList.java
+++ b/core/java/android/view/DisplayList.java
@@ -35,6 +35,30 @@ public abstract class DisplayList {
*/
public static final int FLAG_CLIP_CHILDREN = 0x1;
+ // NOTE: The STATUS_* values *must* match the enum in DrawGlInfo.h
+
+ /**
+ * Indicates that the display list is done drawing.
+ *
+ * @see HardwareCanvas#drawDisplayList(DisplayList, int, int, android.graphics.Rect, int)
+ */
+ public static final int STATUS_DONE = 0x0;
+
+ /**
+ * Indicates that the display list needs another drawing pass.
+ *
+ * @see HardwareCanvas#drawDisplayList(DisplayList, int, int, android.graphics.Rect, int)
+ */
+ public static final int STATUS_DRAW = 0x1;
+
+ /**
+ * Indicates that the display list needs to re-execute its GL functors.
+ *
+ * @see HardwareCanvas#drawDisplayList(DisplayList, int, int, android.graphics.Rect, int)
+ * @see HardwareCanvas#callDrawGLFunction(int)
+ */
+ public static final int STATUS_INVOKE = 0x2;
+
/**
* Starts recording the display list. All operations performed on the
* returned canvas are recorded and stored in this display list.
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
index d9bf918..9639faf 100644
--- a/core/java/android/view/FocusFinder.java
+++ b/core/java/android/view/FocusFinder.java
@@ -79,25 +79,45 @@ public class FocusFinder {
switch (direction) {
case View.FOCUS_RIGHT:
case View.FOCUS_DOWN:
+ setFocusBottomRight(root);
+ break;
case View.FOCUS_FORWARD:
- final int rootTop = root.getScrollY();
- final int rootLeft = root.getScrollX();
- mFocusedRect.set(rootLeft, rootTop, rootLeft, rootTop);
+ if (focused != null && focused.isLayoutRtl()) {
+ setFocusTopLeft(root);
+ } else {
+ setFocusBottomRight(root);
+ }
break;
case View.FOCUS_LEFT:
case View.FOCUS_UP:
+ setFocusTopLeft(root);
+ break;
case View.FOCUS_BACKWARD:
- final int rootBottom = root.getScrollY() + root.getHeight();
- final int rootRight = root.getScrollX() + root.getWidth();
- mFocusedRect.set(rootRight, rootBottom,
- rootRight, rootBottom);
+ if (focused != null && focused.isLayoutRtl()) {
+ setFocusBottomRight(root);
+ } else {
+ setFocusTopLeft(root);
break;
+ }
}
}
return findNextFocus(root, focused, mFocusedRect, direction);
}
+ private void setFocusTopLeft(ViewGroup root) {
+ final int rootBottom = root.getScrollY() + root.getHeight();
+ final int rootRight = root.getScrollX() + root.getWidth();
+ mFocusedRect.set(rootRight, rootBottom,
+ rootRight, rootBottom);
+ }
+
+ private void setFocusBottomRight(ViewGroup root) {
+ final int rootTop = root.getScrollY();
+ final int rootLeft = root.getScrollX();
+ mFocusedRect.set(rootLeft, rootTop, rootLeft, rootTop);
+ }
+
/**
* Find the next view to take focus in root's descendants, searching from
* a particular rectangle in root's coordinates.
@@ -135,22 +155,10 @@ public class FocusFinder {
final int count = focusables.size();
switch (direction) {
case View.FOCUS_FORWARD:
- if (focused != null) {
- int position = focusables.lastIndexOf(focused);
- if (position >= 0 && position + 1 < count) {
- return focusables.get(position + 1);
- }
- }
- return focusables.get(0);
+ return getForwardFocusable(focused, focusables, count);
case View.FOCUS_BACKWARD:
- if (focused != null) {
- int position = focusables.indexOf(focused);
- if (position > 0) {
- return focusables.get(position - 1);
- }
- }
- return focusables.get(count - 1);
+ return getBackwardFocusable(focused, focusables, count);
}
return null;
}
@@ -193,6 +201,38 @@ public class FocusFinder {
return closest;
}
+ private View getForwardFocusable(View focused, ArrayList<View> focusables, int count) {
+ return (focused != null && focused.isLayoutRtl()) ?
+ getPreviousFocusable(focused, focusables, count) :
+ getNextFocusable(focused, focusables, count);
+ }
+
+ private View getNextFocusable(View focused, ArrayList<View> focusables, int count) {
+ if (focused != null) {
+ int position = focusables.lastIndexOf(focused);
+ if (position >= 0 && position + 1 < count) {
+ return focusables.get(position + 1);
+ }
+ }
+ return focusables.get(0);
+ }
+
+ private View getBackwardFocusable(View focused, ArrayList<View> focusables, int count) {
+ return (focused != null && focused.isLayoutRtl()) ?
+ getNextFocusable(focused, focusables, count) :
+ getPreviousFocusable(focused, focusables, count);
+ }
+
+ private View getPreviousFocusable(View focused, ArrayList<View> focusables, int count) {
+ if (focused != null) {
+ int position = focusables.indexOf(focused);
+ if (position > 0) {
+ return focusables.get(position - 1);
+ }
+ }
+ return focusables.get(count - 1);
+ }
+
/**
* Is rect1 a better candidate than rect2 for a focus search in a particular
* direction from a source rect? This is the core routine that determines
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 1f75e70..0e96742 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -296,11 +296,11 @@ class GLES20Canvas extends HardwareCanvas {
///////////////////////////////////////////////////////////////////////////
@Override
- public boolean callDrawGLFunction(int drawGLFunction) {
+ public int callDrawGLFunction(int drawGLFunction) {
return nCallDrawGLFunction(mRenderer, drawGLFunction);
}
- private static native boolean nCallDrawGLFunction(int renderer, int drawGLFunction);
+ private static native int nCallDrawGLFunction(int renderer, int drawGLFunction);
///////////////////////////////////////////////////////////////////////////
// Memory
@@ -394,13 +394,13 @@ class GLES20Canvas extends HardwareCanvas {
private static native void nSetDisplayListName(int displayList, String name);
@Override
- public boolean drawDisplayList(DisplayList displayList, int width, int height,
+ public int drawDisplayList(DisplayList displayList, int width, int height,
Rect dirty, int flags) {
return nDrawDisplayList(mRenderer, ((GLES20DisplayList) displayList).getNativeDisplayList(),
width, height, dirty, flags);
}
- private static native boolean nDrawDisplayList(int renderer, int displayList,
+ private static native int nDrawDisplayList(int renderer, int displayList,
int width, int height, Rect dirty, int flags);
@Override
diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java
index 838c03c..2636ea2 100644
--- a/core/java/android/view/HardwareCanvas.java
+++ b/core/java/android/view/HardwareCanvas.java
@@ -59,11 +59,11 @@ public abstract class HardwareCanvas extends Canvas {
* if this method returns true, can be null.
* @param flags Optional flags about drawing, see {@link DisplayList} for
* the possible flags.
- *
- * @return True if the content of the display list requires another
- * drawing pass (invalidate()), false otherwise
+ *
+ * @return One of {@link DisplayList#STATUS_DONE}, {@link DisplayList#STATUS_DRAW} or
+ * {@link DisplayList#STATUS_INVOKE}
*/
- public abstract boolean drawDisplayList(DisplayList displayList, int width, int height,
+ public abstract int drawDisplayList(DisplayList displayList, int width, int height,
Rect dirty, int flags);
/**
@@ -90,10 +90,12 @@ public abstract class HardwareCanvas extends Canvas {
* This function may return true if an invalidation is needed after the call.
*
* @param drawGLFunction A native function pointer
- * @return true if an invalidate is needed after the call, false otherwise
+ *
+ * @return One of {@link DisplayList#STATUS_DONE}, {@link DisplayList#STATUS_DRAW} or
+ * {@link DisplayList#STATUS_INVOKE}
*/
- public boolean callDrawGLFunction(int drawGLFunction) {
+ public int callDrawGLFunction(int drawGLFunction) {
// Noop - this is done in the display list recorder subclass
- return false;
+ return DisplayList.STATUS_DONE;
}
}
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index f98cfc0..d40043f 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -966,7 +966,6 @@ public abstract class HardwareRenderer {
Log.d("DLProperties", "getDisplayList():\t" +
mProfileData[mProfileCurrentFrame]);
}
-
}
if (displayList != null) {
@@ -975,7 +974,7 @@ public abstract class HardwareRenderer {
drawDisplayListStartTime = System.nanoTime();
}
- boolean invalidateNeeded = canvas.drawDisplayList(displayList,
+ int status = canvas.drawDisplayList(displayList,
view.getWidth(), view.getHeight(), mRedrawClip,
DisplayList.FLAG_CLIP_CHILDREN);
@@ -986,19 +985,18 @@ public abstract class HardwareRenderer {
if (ViewDebug.DEBUG_LATENCY) {
Log.d(ViewDebug.DEBUG_LATENCY_TAG, "- drawDisplayList() took " +
- total + "ms, invalidateNeeded=" +
- invalidateNeeded + ".");
+ total + "ms, status=" + status);
}
}
- if (invalidateNeeded) {
+ if (status != DisplayList.STATUS_DONE) {
if (mRedrawClip.isEmpty()) {
attachInfo.mViewRootImpl.invalidate();
} else {
attachInfo.mViewRootImpl.invalidateChildInParent(
null, mRedrawClip);
+ mRedrawClip.setEmpty();
}
- mRedrawClip.setEmpty();
}
} else {
// Shouldn't reach here
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3afc20e..2deeba6 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1459,7 +1459,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
* apps.
* @hide
*/
- public static final boolean USE_DISPLAY_LIST_PROPERTIES = false;
+ public static final boolean USE_DISPLAY_LIST_PROPERTIES = true;
/**
* Map used to store views' tags.
@@ -4933,9 +4933,8 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
@RemotableViewMethod
public void setLayoutDirection(int layoutDirection) {
if (getLayoutDirection() != layoutDirection) {
- // Reset the current layout direction
+ // Reset the current layout direction and the resolved one
mPrivateFlags2 &= ~LAYOUT_DIRECTION_MASK;
- // Reset the current resolved layout direction
resetResolvedLayoutDirection();
// Set the new layout direction (filtered) and ask for a layout pass
mPrivateFlags2 |=
@@ -4955,7 +4954,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
@ViewDebug.IntToString(from = LAYOUT_DIRECTION_RTL, to = "RESOLVED_DIRECTION_RTL")
})
public int getResolvedLayoutDirection() {
- resolveLayoutDirectionIfNeeded();
+ // The layout diretion will be resolved only if needed
+ if ((mPrivateFlags2 & LAYOUT_DIRECTION_RESOLVED) != LAYOUT_DIRECTION_RESOLVED) {
+ resolveLayoutDirection();
+ }
return ((mPrivateFlags2 & LAYOUT_DIRECTION_RESOLVED_RTL) == LAYOUT_DIRECTION_RESOLVED_RTL) ?
LAYOUT_DIRECTION_RTL : LAYOUT_DIRECTION_LTR;
}
@@ -7399,7 +7401,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
invalidateViewProperty(false, false);
if (USE_DISPLAY_LIST_PROPERTIES && mDisplayList != null) {
- mDisplayList.setCameraDistance(distance);
+ mDisplayList.setCameraDistance(-Math.abs(distance) / dpi);
}
}
@@ -9832,7 +9834,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
jumpDrawablesToCurrentState();
// Order is important here: LayoutDirection MUST be resolved before Padding
// and TextDirection
- resolveLayoutDirectionIfNeeded();
+ resolveLayoutDirection();
resolvePadding();
resolveTextDirection();
if (isFocused()) {
@@ -9863,31 +9865,24 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
/**
* Resolve and cache the layout direction. LTR is set initially. This is implicitly supposing
* that the parent directionality can and will be resolved before its children.
+ * Will call {@link View#onResolvedLayoutDirectionChanged} when resolution is done.
*/
- private void resolveLayoutDirectionIfNeeded() {
- // Do not resolve if it is not needed
- if ((mPrivateFlags2 & LAYOUT_DIRECTION_RESOLVED) == LAYOUT_DIRECTION_RESOLVED) return;
-
+ public void resolveLayoutDirection() {
// Clear any previous layout direction resolution
mPrivateFlags2 &= ~LAYOUT_DIRECTION_RESOLVED_MASK;
// Set resolved depending on layout direction
switch (getLayoutDirection()) {
case LAYOUT_DIRECTION_INHERIT:
- // We cannot do the resolution if there is no parent
- if (mParent == null) return;
-
// If this is root view, no need to look at parent's layout dir.
- if (mParent instanceof ViewGroup) {
+ if (canResolveLayoutDirection()) {
ViewGroup viewGroup = ((ViewGroup) mParent);
- // Check if the parent view group can resolve
- if (! viewGroup.canResolveLayoutDirection()) {
- return;
- }
if (viewGroup.getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) {
mPrivateFlags2 |= LAYOUT_DIRECTION_RESOLVED_RTL;
}
+ } else {
+ // Nothing to do, LTR by default
}
break;
case LAYOUT_DIRECTION_RTL:
@@ -9990,7 +9985,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
public boolean canResolveLayoutDirection() {
switch (getLayoutDirection()) {
case LAYOUT_DIRECTION_INHERIT:
- return (mParent != null);
+ return (mParent != null) && (mParent instanceof ViewGroup);
default:
return true;
}
@@ -10752,16 +10747,25 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
int layerType = (
!(mParent instanceof ViewGroup) || ((ViewGroup)mParent).mDrawLayers) ?
getLayerType() : LAYER_TYPE_NONE;
- if (!isLayer && layerType == LAYER_TYPE_HARDWARE && USE_DISPLAY_LIST_PROPERTIES) {
- final HardwareLayer layer = getHardwareLayer();
- if (layer != null && layer.isValid()) {
- canvas.drawHardwareLayer(layer, 0, 0, mLayerPaint);
+ if (!isLayer && layerType != LAYER_TYPE_NONE && USE_DISPLAY_LIST_PROPERTIES) {
+ if (layerType == LAYER_TYPE_HARDWARE) {
+ final HardwareLayer layer = getHardwareLayer();
+ if (layer != null && layer.isValid()) {
+ canvas.drawHardwareLayer(layer, 0, 0, mLayerPaint);
+ } else {
+ canvas.saveLayer(0, 0, mRight - mLeft, mBottom - mTop, mLayerPaint,
+ Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |
+ Canvas.CLIP_TO_LAYER_SAVE_FLAG);
+ }
+ caching = true;
} else {
- canvas.saveLayer(0, 0,
- mRight - mLeft, mBottom - mTop, mLayerPaint,
- Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
+ buildDrawingCache(true);
+ Bitmap cache = getDrawingCache(true);
+ if (cache != null) {
+ canvas.drawBitmap(cache, 0, 0, mLayerPaint);
+ caching = true;
+ }
}
- caching = true;
} else {
computeScroll();
@@ -11400,7 +11404,11 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
mTransformationInfo.mRotation, mTransformationInfo.mRotationX,
mTransformationInfo.mRotationY, mTransformationInfo.mScaleX,
mTransformationInfo.mScaleY);
- displayList.setCameraDistance(getCameraDistance());
+ if (mTransformationInfo.mCamera == null) {
+ mTransformationInfo.mCamera = new Camera();
+ mTransformationInfo.matrix3D = new Matrix();
+ }
+ displayList.setCameraDistance(mTransformationInfo.mCamera.getLocationZ());
if ((mPrivateFlags & PIVOT_EXPLICITLY_SET) == PIVOT_EXPLICITLY_SET) {
displayList.setPivotX(getPivotX());
displayList.setPivotY(getPivotY());
@@ -11494,8 +11502,12 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
} else {
switch (layerType) {
case LAYER_TYPE_SOFTWARE:
- buildDrawingCache(true);
- cache = getDrawingCache(true);
+ if (useDisplayListProperties) {
+ hasDisplayList = canHaveDisplayList();
+ } else {
+ buildDrawingCache(true);
+ cache = getDrawingCache(true);
+ }
break;
case LAYER_TYPE_HARDWARE:
if (useDisplayListProperties) {
@@ -14545,14 +14557,13 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
*/
public void setTextDirection(int textDirection) {
if (getTextDirection() != textDirection) {
- // Reset the current text direction
+ // Reset the current text direction and the resolved one
mPrivateFlags2 &= ~TEXT_DIRECTION_MASK;
+ resetResolvedTextDirection();
// Set the new text direction
mPrivateFlags2 |= ((textDirection << TEXT_DIRECTION_MASK_SHIFT) & TEXT_DIRECTION_MASK);
- // Reset the current resolved text direction
- resetResolvedTextDirection();
- // Ask for a layout pass
requestLayout();
+ invalidate(true);
}
}
@@ -14573,7 +14584,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
* {@link #TEXT_DIRECTION_LOCALE},
*/
public int getResolvedTextDirection() {
- // The text direction is not inherited so return it back
+ // The text direction will be resolved only if needed
if ((mPrivateFlags2 & TEXT_DIRECTION_RESOLVED) != TEXT_DIRECTION_RESOLVED) {
resolveTextDirection();
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 6ccac78..30d6ec7 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3906,9 +3906,15 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
do {
if (parent instanceof ViewGroup) {
ViewGroup parentVG = (ViewGroup) parent;
- parent = parentVG.invalidateChildInParentFast(left, top, dirty);
- left = parentVG.mLeft;
- top = parentVG.mTop;
+ if (parentVG.mLayerType != LAYER_TYPE_NONE) {
+ // Layered parents should be recreated, not just re-issued
+ parentVG.invalidate();
+ parent = null;
+ } else {
+ parent = parentVG.invalidateChildInParentFast(left, top, dirty);
+ left = parentVG.mLeft;
+ top = parentVG.mTop;
+ }
} else {
// Reached the top; this calls into the usual invalidate method in
// ViewRootImpl, which schedules a traversal
@@ -4664,6 +4670,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
public void clearDisappearingChildren() {
if (mDisappearingChildren != null) {
mDisappearingChildren.clear();
+ invalidate();
}
}
@@ -4775,7 +4782,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
view.mParent = null;
}
}
- mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
+ invalidate();
}
}
}
diff --git a/core/java/android/webkit/DebugFlags.java b/core/java/android/webkit/DebugFlags.java
index a21d3ee..349113e 100644
--- a/core/java/android/webkit/DebugFlags.java
+++ b/core/java/android/webkit/DebugFlags.java
@@ -42,12 +42,7 @@ class DebugFlags {
public static final boolean WEB_BACK_FORWARD_LIST = false;
public static final boolean WEB_SETTINGS = false;
public static final boolean WEB_SYNC_MANAGER = false;
- public static final boolean WEB_TEXT_VIEW = false;
public static final boolean WEB_VIEW = false;
public static final boolean WEB_VIEW_CORE = false;
- /*
- * Set to true to allow the WebTextView to draw on top of the web page in a
- * different color with no background so you can see how the two line up.
- */
- public static final boolean DRAW_WEBTEXTVIEW = false;
+ public static final boolean MEASURE_PAGE_SWAP_FPS = false;
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index dd373de..d225594 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -312,6 +312,24 @@ public class WebView extends AbsoluteLayout
public static final String SCHEME_GEO = "geo:0,0?q=";
/**
+ * Interface to listen for find results.
+ * @hide
+ */
+ public interface FindListener {
+ /**
+ * Notify the listener about progress made by a find operation.
+ *
+ * @param numberOfMatches How many matches have been found.
+ * @param activeMatchOrdinal The zero-based ordinal of the currently selected match.
+ * @param isDoneCounting Whether the find operation has actually completed. The listener
+ * may be notified multiple times while the operation is underway, and the numberOfMatches
+ * value should not be considered final unless isDoneCounting is true.
+ */
+ public void onFindResultReceived(int numberOfMatches, int activeMatchOrdinal,
+ boolean isDoneCounting);
+ }
+
+ /**
* Interface to listen for new pictures as they change.
* @deprecated This interface is now obsolete.
*/
@@ -1228,10 +1246,10 @@ public class WebView extends AbsoluteLayout
}
/**
- * Register the interface to be used when a find-on-page result has become
- * available. This will replace the current handler.
+ * Register the listener to be notified as find-on-page operations progress.
+ * This will replace the current listener.
*
- * @param listener An implementation of FindListener
+ * @param listener An implementation of {@link WebView#FindListener}.
* @hide
*/
public void setFindListener(FindListener listener) {
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index 03329b8..5ae2fe0 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -1440,7 +1440,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
private PictureListener mPictureListener;
// Used to notify listeners about find-on-page results.
- private FindListener mFindListener;
+ private WebView.FindListener mFindListener;
/**
* Refer to {@link WebView#requestFocusNodeHref(Message)} for more information
@@ -3620,12 +3620,10 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Register the interface to be used when a find-on-page result has become
- * available. This will replace the current handler.
- *
- * @param listener An implementation of FindListener
+ * See {@link WebView#setFindListener(WebView.FindListener)}.
+ * @hide
*/
- public void setFindListener(FindListener listener) {
+ public void setFindListener(WebView.FindListener listener) {
mFindListener = listener;
}
@@ -4719,10 +4717,10 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
queueFull = nativeSetBaseLayer(mNativeClass, layer, invalRegion,
showVisualIndicator, isPictureAfterFirstLayout);
- if (layer == 0 || isPictureAfterFirstLayout) {
- mWebViewCore.resumeWebKitDraw();
- } else if (queueFull) {
+ if (queueFull) {
mWebViewCore.pauseWebKitDraw();
+ } else {
+ mWebViewCore.resumeWebKitDraw();
}
if (mHTML5VideoViewProxy != null) {
@@ -8617,9 +8615,19 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
void onPageSwapOccurred(boolean notifyAnimationStarted);
}
+ long mLastSwapTime;
+ double mAverageSwapFps;
+
/** Called by JNI when pages are swapped (only occurs with hardware
* acceleration) */
protected void pageSwapCallback(boolean notifyAnimationStarted) {
+ if (DebugFlags.MEASURE_PAGE_SWAP_FPS) {
+ long now = System.currentTimeMillis();
+ long diff = now - mLastSwapTime;
+ mAverageSwapFps = ((1000.0 / diff) + mAverageSwapFps) / 2;
+ Log.d(LOGTAG, "page swap fps: " + mAverageSwapFps);
+ mLastSwapTime = now;
+ }
mWebViewCore.resumeWebKitDraw();
if (notifyAnimationStarted) {
mWebViewCore.sendMessage(EventHub.NOTIFY_ANIMATION_STARTED);
@@ -8681,7 +8689,12 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
Log.v(LOGTAG, "NEW_PICTURE_MSG_ID {" +
b.left+","+b.top+","+b.right+","+b.bottom+"}");
}
- invalidateContentRect(draw.mInvalRegion.getBounds());
+ Rect invalBounds = draw.mInvalRegion.getBounds();
+ if (!invalBounds.isEmpty()) {
+ invalidateContentRect(invalBounds);
+ } else {
+ mWebView.invalidate();
+ }
if (mPictureListener != null) {
mPictureListener.onNewPicture(getWebView(), capturePicture());
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index b47f71d..afb2992 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -2271,6 +2271,7 @@ public final class WebViewCore {
mFirstLayoutForNonStandardLoad = false;
}
if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
+ pauseWebKitDraw();
Message.obtain(mWebViewClassic.mPrivateHandler,
WebViewClassic.NEW_PICTURE_MSG_ID, draw).sendToTarget();
}
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index 7d47e14..f049198 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -192,7 +192,7 @@ public interface WebViewProvider {
public WebBackForwardList copyBackForwardList();
- public void setFindListener(FindListener listener);
+ public void setFindListener(WebView.FindListener listener);
public void findNext(boolean forward);
diff --git a/core/java/android/widget/AdapterViewFlipper.java b/core/java/android/widget/AdapterViewFlipper.java
index 5096227..aea029b 100644
--- a/core/java/android/widget/AdapterViewFlipper.java
+++ b/core/java/android/widget/AdapterViewFlipper.java
@@ -127,13 +127,29 @@ public class AdapterViewFlipper extends AdapterViewAnimator {
}
/**
- * How long to wait before flipping to the next view
+ * Returns the flip interval, in milliseconds.
*
- * @param milliseconds
- * time in milliseconds
+ * @return the flip interval in milliseconds
+ *
+ * @see #setFlipInterval(int)
+ *
+ * @attr ref android.R.styleable#AdapterViewFlipper_flipInterval
+ */
+ public int getFlipInterval() {
+ return mFlipInterval;
+ }
+
+ /**
+ * How long to wait before flipping to the next view.
+ *
+ * @param flipInterval flip interval in milliseconds
+ *
+ * @see #getFlipInterval()
+ *
+ * @attr ref android.R.styleable#AdapterViewFlipper_flipInterval
*/
- public void setFlipInterval(int milliseconds) {
- mFlipInterval = milliseconds;
+ public void setFlipInterval(int flipInterval) {
+ mFlipInterval = flipInterval;
}
/**
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 3001ea1..b1a75e1 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -223,11 +223,28 @@ public class ImageView extends View {
}
/**
+ * True when ImageView is adjusting its bounds
+ * to preserve the aspect ratio of its drawable
+ *
+ * @return whether to adjust the bounds of this view
+ * to presrve the original aspect ratio of the drawable
+ *
+ * @see #setAdjustViewBounds(boolean)
+ *
+ * @attr ref android.R.styleable#ImageView_adjustViewBounds
+ */
+ public boolean getAdjustViewBounds() {
+ return mAdjustViewBounds;
+ }
+
+ /**
* Set this to true if you want the ImageView to adjust its bounds
* to preserve the aspect ratio of its drawable.
* @param adjustViewBounds Whether to adjust the bounds of this view
* to presrve the original aspect ratio of the drawable
*
+ * @see #getAdjustViewBounds()
+ *
* @attr ref android.R.styleable#ImageView_adjustViewBounds
*/
@android.view.RemotableViewMethod
@@ -237,7 +254,20 @@ public class ImageView extends View {
setScaleType(ScaleType.FIT_CENTER);
}
}
-
+
+ /**
+ * The maximum width of this view.
+ *
+ * @return The maximum width of this view
+ *
+ * @see #setMaxWidth(int)
+ *
+ * @attr ref android.R.styleable#ImageView_maxWidth
+ */
+ public int getMaxWidth() {
+ return mMaxWidth;
+ }
+
/**
* An optional argument to supply a maximum width for this view. Only valid if
* {@link #setAdjustViewBounds(boolean)} has been set to true. To set an image to be a maximum
@@ -253,14 +283,29 @@ public class ImageView extends View {
* </p>
*
* @param maxWidth maximum width for this view
- *
+ *
+ * @see #getMaxWidth()
+ *
* @attr ref android.R.styleable#ImageView_maxWidth
*/
@android.view.RemotableViewMethod
public void setMaxWidth(int maxWidth) {
mMaxWidth = maxWidth;
}
-
+
+ /**
+ * The maximum height of this view.
+ *
+ * @return The maximum height of this view
+ *
+ * @see #setMaxHeight(int)
+ *
+ * @attr ref android.R.styleable#ImageView_maxHeight
+ */
+ public int getMaxHeight() {
+ return mMaxHeight;
+ }
+
/**
* An optional argument to supply a maximum height for this view. Only valid if
* {@link #setAdjustViewBounds(boolean)} has been set to true. To set an image to be a
@@ -276,7 +321,9 @@ public class ImageView extends View {
* </p>
*
* @param maxHeight maximum height for this view
- *
+ *
+ * @see #getMaxHeight()
+ *
* @attr ref android.R.styleable#ImageView_maxHeight
*/
@android.view.RemotableViewMethod
@@ -522,7 +569,37 @@ public class ImageView extends View {
invalidate();
}
}
-
+
+ /**
+ * Return whether this ImageView crops to padding.
+ *
+ * @return whether this ImageView crops to padding
+ *
+ * @see #setCropToPadding(boolean)
+ *
+ * @attr ref android.R.styleable#ImageView_cropToPadding
+ */
+ public boolean getCropToPadding() {
+ return mCropToPadding;
+ }
+
+ /**
+ * Sets whether this ImageView will crop to padding.
+ *
+ * @param cropToPadding whether this ImageView will crop to padding
+ *
+ * @see #getCropToPadding()
+ *
+ * @attr ref android.R.styleable#ImageView_cropToPadding
+ */
+ public void setCropToPadding(boolean cropToPadding) {
+ if (mCropToPadding != cropToPadding) {
+ mCropToPadding = cropToPadding;
+ requestLayout();
+ invalidate();
+ }
+ }
+
private void resolveUri() {
if (mDrawable != null) {
return;
@@ -997,11 +1074,24 @@ public class ImageView extends View {
public final void clearColorFilter() {
setColorFilter(null);
}
-
+
+ /**
+ * Returns the active color filter for this ImageView.
+ *
+ * @return the active color filter for this ImageView
+ *
+ * @see #setColorFilter(android.graphics.ColorFilter)
+ */
+ public ColorFilter getColorFilter() {
+ return mColorFilter;
+ }
+
/**
* Apply an arbitrary colorfilter to the image.
*
* @param cf the colorfilter to apply (may be null)
+ *
+ * @see #getColorFilter()
*/
public void setColorFilter(ColorFilter cf) {
if (mColorFilter != cf) {
@@ -1012,6 +1102,37 @@ public class ImageView extends View {
}
}
+ /**
+ * Returns the alpha that will be applied to the drawable of this ImageView.
+ *
+ * @return the alpha that will be applied to the drawable of this ImageView
+ *
+ * @see #setImageAlpha(int)
+ */
+ public int getImageAlpha() {
+ return mAlpha;
+ }
+
+ /**
+ * Sets the alpha value that should be applied to the image.
+ *
+ * @param alpha the alpha value that should be applied to the image
+ *
+ * @see #getImageAlpha()
+ */
+ @RemotableViewMethod
+ public void setImageAlpha(int alpha) {
+ setAlpha(alpha);
+ }
+
+ /**
+ * Sets the alpha value that should be applied to the image.
+ *
+ * @param alpha the alpha value that should be applied to the image
+ *
+ * @deprecated use #setImageAlpha(int) instead
+ */
+ @Deprecated
@RemotableViewMethod
public void setAlpha(int alpha) {
alpha &= 0xFF; // keep it legal
diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java
index 7f53ffd..f217c9c 100644
--- a/core/java/android/widget/RadioGroup.java
+++ b/core/java/android/widget/RadioGroup.java
@@ -190,6 +190,8 @@ public class RadioGroup extends LinearLayout {
*
* @see #check(int)
* @see #clearCheck()
+ *
+ * @attr ref android.R.styleable#RadioGroup_checkedButton
*/
public int getCheckedRadioButtonId() {
return mCheckedId;
diff --git a/core/java/android/widget/RatingBar.java b/core/java/android/widget/RatingBar.java
index e69577b..524d272 100644
--- a/core/java/android/widget/RatingBar.java
+++ b/core/java/android/widget/RatingBar.java
@@ -145,6 +145,8 @@ public class RatingBar extends AbsSeekBar {
* by the user).
*
* @param isIndicator Whether it should be an indicator.
+ *
+ * @attr ref android.R.styleable#RatingBar_isIndicator
*/
public void setIsIndicator(boolean isIndicator) {
mIsUserSeekable = !isIndicator;
@@ -153,6 +155,8 @@ public class RatingBar extends AbsSeekBar {
/**
* @return Whether this rating bar is only an indicator.
+ *
+ * @attr ref android.R.styleable#RatingBar_isIndicator
*/
public boolean isIndicator() {
return !mIsUserSeekable;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 16d1b94..1f2410b 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -7496,7 +7496,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* Returns true, only while processing a touch gesture, if the initial
* touch down event caused focus to move to the text view and as a result
* its selection changed. Only valid while processing the touch gesture
- * of interest.
+ * of interest, in an editable text view.
*/
public boolean didTouchFocusSelect() {
return mEditor != null && getEditor().mTouchFocusSelected;
@@ -11683,7 +11683,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
highlight = null;
}
- if (false /* TEMP patch for bugs 6198276 & 6193544 */ && canHaveDisplayList() && canvas.isHardwareAccelerated()) {
+ if (canHaveDisplayList() && canvas.isHardwareAccelerated()) {
drawHardwareAccelerated(canvas, layout, highlight, cursorOffsetVertical);
} else {
layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical);
@@ -11758,7 +11758,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
hardwareCanvas.onPostDraw();
blockDisplayList.end();
if (USE_DISPLAY_LIST_PROPERTIES) {
- blockDisplayList.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);
+ blockDisplayList.setLeftTopRightBottom(0, 0, width, height);
}
}
}
diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java
index da189f1..07496a7 100644
--- a/core/java/com/android/internal/util/StateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -44,10 +44,14 @@ import java.util.Vector;
* <p>When a state machine is created <code>addState</code> is used to build the
* hierarchy and <code>setInitialState</code> is used to identify which of these
* is the initial state. After construction the programmer calls <code>start</code>
- * which initializes the state machine and calls <code>enter</code> for all of the initial
- * state's hierarchy, starting at its eldest parent. For example given the simple
- * state machine below after start is called mP1.enter will have been called and
- * then mS1.enter.</p>
+ * which initializes and starts the state machine. The first action the StateMachine
+ * is to the invoke <code>enter</code> for all of the initial state's hierarchy,
+ * starting at its eldest parent. The calls to enter will be done in the context
+ * of the StateMachines Handler not in the context of the call to start and they
+ * will be invoked before any messages are processed. For example, given the simple
+ * state machine below mP1.enter will be invoked and then mS1.enter. Finally,
+ * messages sent to the state machine will be processed by the current state,
+ * in our simple state machine below that would initially be mS1.processMessage.</p>
<code>
mP1
/ \
@@ -621,8 +625,8 @@ public class StateMachine {
/** The debug flag */
private boolean mDbg = false;
- /** The quit object */
- private static final Object mQuitObj = new Object();
+ /** The SmHandler object, identifies that message is internal */
+ private static final Object mSmHandlerObj = new Object();
/** The current message */
private Message mMsg;
@@ -726,19 +730,18 @@ public class StateMachine {
/** Save the current message */
mMsg = msg;
- /**
- * Check that construction was completed
- */
- if (!mIsConstructionCompleted) {
- Log.e(TAG, "The start method not called, ignore msg: " + msg);
- return;
+ if (mIsConstructionCompleted) {
+ /** Normal path */
+ processMsg(msg);
+ } else if (!mIsConstructionCompleted &&
+ (mMsg.what == SM_INIT_CMD) && (mMsg.obj == mSmHandlerObj)) {
+ /** Initial one time path. */
+ mIsConstructionCompleted = true;
+ invokeEnterMethods(0);
+ } else {
+ throw new RuntimeException("StateMachine.handleMessage: " +
+ "The start method not called, received msg: " + msg);
}
-
- /**
- * Process the message abiding by the hierarchical semantics
- * and perform any requested transitions.
- */
- processMsg(msg);
performTransitions();
if (mDbg) Log.d(TAG, "handleMessage: X");
@@ -852,18 +855,8 @@ public class StateMachine {
mTempStateStack = new StateInfo[maxDepth];
setupInitialStateStack();
- /**
- * Construction is complete call all enter methods
- * starting at the first entry.
- */
- mIsConstructionCompleted = true;
- mMsg = obtainMessage(SM_INIT_CMD);
- invokeEnterMethods(0);
-
- /**
- * Perform any transitions requested by the enter methods
- */
- performTransitions();
+ /** Sending SM_INIT_CMD message to invoke enter methods asynchronously */
+ sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
if (mDbg) Log.d(TAG, "completeConstruction: X");
}
@@ -1103,14 +1096,14 @@ public class StateMachine {
/** @see StateMachine#setInitialState(State) */
private final void setInitialState(State initialState) {
- if (mDbg) Log.d(TAG, "setInitialState: initialState" + initialState.getName());
+ if (mDbg) Log.d(TAG, "setInitialState: initialState=" + initialState.getName());
mInitialState = initialState;
}
/** @see StateMachine#transitionTo(IState) */
private final void transitionTo(IState destState) {
mDestState = (State) destState;
- if (mDbg) Log.d(TAG, "StateMachine.transitionTo EX destState" + mDestState.getName());
+ if (mDbg) Log.d(TAG, "transitionTo: destState=" + mDestState.getName());
}
/** @see StateMachine#deferMessage(Message) */
@@ -1127,12 +1120,12 @@ public class StateMachine {
/** @see StateMachine#deferMessage(Message) */
private final void quit() {
if (mDbg) Log.d(TAG, "quit:");
- sendMessage(obtainMessage(SM_QUIT_CMD, mQuitObj));
+ sendMessage(obtainMessage(SM_QUIT_CMD, mSmHandlerObj));
}
/** @see StateMachine#isQuit(Message) */
private final boolean isQuit(Message msg) {
- return (msg.what == SM_QUIT_CMD) && (msg.obj == mQuitObj);
+ return (msg.what == SM_QUIT_CMD) && (msg.obj == mSmHandlerObj);
}
/** @see StateMachine#isDbg() */
diff --git a/core/java/com/google/android/mms/pdu/PduPersister.java b/core/java/com/google/android/mms/pdu/PduPersister.java
index b04f890..7c937ed 100644
--- a/core/java/com/google/android/mms/pdu/PduPersister.java
+++ b/core/java/com/google/android/mms/pdu/PduPersister.java
@@ -20,6 +20,8 @@ package com.google.android.mms.pdu;
import com.google.android.mms.ContentType;
import com.google.android.mms.InvalidHeaderValueException;
import com.google.android.mms.MmsException;
+import com.google.android.mms.util.DownloadDrmHelper;
+import com.google.android.mms.util.DrmConvertSession;
import com.google.android.mms.util.PduCache;
import com.google.android.mms.util.PduCacheEntry;
import com.google.android.mms.util.SqliteWrapper;
@@ -30,7 +32,11 @@ import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.DatabaseUtils;
+import android.database.sqlite.SQLiteException;
+import android.drm.DrmManagerClient;
import android.net.Uri;
+import android.os.FileUtils;
+import android.provider.MediaStore;
import android.provider.Telephony;
import android.provider.Telephony.Mms;
import android.provider.Telephony.MmsSms;
@@ -42,6 +48,7 @@ import android.text.TextUtils;
import android.util.Log;
import java.io.ByteArrayOutputStream;
+import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
@@ -271,10 +278,12 @@ public class PduPersister {
private final Context mContext;
private final ContentResolver mContentResolver;
+ private final DrmManagerClient mDrmManagerClient;
private PduPersister(Context context) {
mContext = context;
mContentResolver = context.getContentResolver();
+ mDrmManagerClient = new DrmManagerClient(context);
}
/** Get(or create if not exist) an instance of PduPersister */
@@ -761,6 +770,9 @@ public class PduPersister {
throws MmsException {
OutputStream os = null;
InputStream is = null;
+ DrmConvertSession drmConvertSession = null;
+ Uri dataUri = null;
+ String path = null;
try {
byte[] data = part.getData();
@@ -773,9 +785,38 @@ public class PduPersister {
throw new MmsException("unable to update " + uri.toString());
}
} else {
+ boolean isDrm = DownloadDrmHelper.isDrmConvertNeeded(contentType);
+ if (isDrm) {
+ if (uri != null) {
+ try {
+ path = convertUriToPath(mContext, uri);
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "drm uri: " + uri + " path: " + path);
+ }
+ File f = new File(path);
+ long len = f.length();
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "drm path: " + path + " len: " + len);
+ }
+ if (len > 0) {
+ // we're not going to re-persist and re-encrypt an already
+ // converted drm file
+ return;
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Can't get file info for: " + part.getDataUri(), e);
+ }
+ }
+ // We haven't converted the file yet, start the conversion
+ drmConvertSession = DrmConvertSession.open(mContext, contentType);
+ if (drmConvertSession == null) {
+ throw new MmsException("Mimetype " + contentType +
+ " can not be converted.");
+ }
+ }
os = mContentResolver.openOutputStream(uri);
if (data == null) {
- Uri dataUri = part.getDataUri();
+ dataUri = part.getDataUri();
if ((dataUri == null) || (dataUri == uri)) {
Log.w(TAG, "Can't find data for this part.");
return;
@@ -788,13 +829,32 @@ public class PduPersister {
byte[] buffer = new byte[8192];
for (int len = 0; (len = is.read(buffer)) != -1; ) {
- os.write(buffer, 0, len);
+ if (!isDrm) {
+ os.write(buffer, 0, len);
+ } else {
+ byte[] convertedData = drmConvertSession.convert(buffer, len);
+ if (convertedData != null) {
+ os.write(convertedData, 0, convertedData.length);
+ } else {
+ throw new MmsException("Error converting drm data.");
+ }
+ }
}
} else {
if (LOCAL_LOGV) {
Log.v(TAG, "Saving data to: " + uri);
}
- os.write(data);
+ if (!isDrm) {
+ os.write(data);
+ } else {
+ dataUri = uri;
+ byte[] convertedData = drmConvertSession.convert(data, data.length);
+ if (convertedData != null) {
+ os.write(convertedData, 0, convertedData.length);
+ } else {
+ throw new MmsException("Error converting drm data.");
+ }
+ }
}
}
} catch (FileNotFoundException e) {
@@ -818,7 +878,65 @@ public class PduPersister {
Log.e(TAG, "IOException while closing: " + is, e);
} // Ignore
}
+ if (drmConvertSession != null) {
+ drmConvertSession.close(path);
+
+ // Reset the permissions on the encrypted part file so everyone has only read
+ // permission.
+ File f = new File(path);
+ ContentValues values = new ContentValues(0);
+ SqliteWrapper.update(mContext, mContentResolver,
+ Uri.parse("content://mms/resetFilePerm/" + f.getName()),
+ values, null, null);
+ }
+ }
+ }
+
+ /**
+ * This method expects uri in the following format
+ * content://media/<table_name>/<row_index> (or)
+ * file://sdcard/test.mp4
+ * http://test.com/test.mp4
+ *
+ * Here <table_name> shall be "video" or "audio" or "images"
+ * <row_index> the index of the content in given table
+ */
+ static public String convertUriToPath(Context context, Uri uri) {
+ String path = null;
+ if (null != uri) {
+ String scheme = uri.getScheme();
+ if (null == scheme || scheme.equals("") ||
+ scheme.equals(ContentResolver.SCHEME_FILE)) {
+ path = uri.getPath();
+
+ } else if (scheme.equals("http")) {
+ path = uri.toString();
+
+ } else if (scheme.equals(ContentResolver.SCHEME_CONTENT)) {
+ String[] projection = new String[] {MediaStore.MediaColumns.DATA};
+ Cursor cursor = null;
+ try {
+ cursor = context.getContentResolver().query(uri, projection, null,
+ null, null);
+ if (null == cursor || 0 == cursor.getCount() || !cursor.moveToFirst()) {
+ throw new IllegalArgumentException("Given Uri could not be found" +
+ " in media store");
+ }
+ int pathIndex = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
+ path = cursor.getString(pathIndex);
+ } catch (SQLiteException e) {
+ throw new IllegalArgumentException("Given Uri is not formatted in a way " +
+ "so that it can be found in media store.");
+ } finally {
+ if (null != cursor) {
+ cursor.close();
+ }
+ }
+ } else {
+ throw new IllegalArgumentException("Given Uri scheme is not supported");
+ }
}
+ return path;
}
private void updateAddress(
diff --git a/core/java/com/google/android/mms/util/DownloadDrmHelper.java b/core/java/com/google/android/mms/util/DownloadDrmHelper.java
new file mode 100644
index 0000000..6852eca
--- /dev/null
+++ b/core/java/com/google/android/mms/util/DownloadDrmHelper.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.google.android.mms.util;
+
+import android.content.Context;
+import android.drm.DrmManagerClient;
+import android.util.Log;
+
+public class DownloadDrmHelper {
+ private static final String TAG = "DownloadDrmHelper";
+
+ /** The MIME type of special DRM files */
+ public static final String MIMETYPE_DRM_MESSAGE = "application/vnd.oma.drm.message";
+
+ /** The extensions of special DRM files */
+ public static final String EXTENSION_DRM_MESSAGE = ".dm";
+
+ public static final String EXTENSION_INTERNAL_FWDL = ".fl";
+
+ /**
+ * Checks if the Media Type is a DRM Media Type
+ *
+ * @param drmManagerClient A DrmManagerClient
+ * @param mimetype Media Type to check
+ * @return True if the Media Type is DRM else false
+ */
+ public static boolean isDrmMimeType(Context context, String mimetype) {
+ boolean result = false;
+ if (context != null) {
+ try {
+ DrmManagerClient drmClient = new DrmManagerClient(context);
+ if (drmClient != null && mimetype != null && mimetype.length() > 0) {
+ result = drmClient.canHandle("", mimetype);
+ }
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG,
+ "DrmManagerClient instance could not be created, context is Illegal.");
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "DrmManagerClient didn't initialize properly.");
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Checks if the Media Type needs to be DRM converted
+ *
+ * @param mimetype Media type of the content
+ * @return True if convert is needed else false
+ */
+ public static boolean isDrmConvertNeeded(String mimetype) {
+ return MIMETYPE_DRM_MESSAGE.equals(mimetype);
+ }
+
+ /**
+ * Modifies the file extension for a DRM Forward Lock file NOTE: This
+ * function shouldn't be called if the file shouldn't be DRM converted
+ */
+ public static String modifyDrmFwLockFileExtension(String filename) {
+ if (filename != null) {
+ int extensionIndex;
+ extensionIndex = filename.lastIndexOf(".");
+ if (extensionIndex != -1) {
+ filename = filename.substring(0, extensionIndex);
+ }
+ filename = filename.concat(EXTENSION_INTERNAL_FWDL);
+ }
+ return filename;
+ }
+
+ /**
+ * Gets the original mime type of DRM protected content.
+ *
+ * @param context The context
+ * @param path Path to the file
+ * @param containingMime The current mime type of of the file i.e. the
+ * containing mime type
+ * @return The original mime type of the file if DRM protected else the
+ * currentMime
+ */
+ public static String getOriginalMimeType(Context context, String path, String containingMime) {
+ String result = containingMime;
+ DrmManagerClient drmClient = new DrmManagerClient(context);
+ try {
+ if (drmClient.canHandle(path, null)) {
+ result = drmClient.getOriginalMimeType(path);
+ }
+ } catch (IllegalArgumentException ex) {
+ Log.w(TAG,
+ "Can't get original mime type since path is null or empty string.");
+ } catch (IllegalStateException ex) {
+ Log.w(TAG, "DrmManagerClient didn't initialize properly.");
+ }
+ return result;
+ }
+}
diff --git a/core/java/com/google/android/mms/util/DrmConvertSession.java b/core/java/com/google/android/mms/util/DrmConvertSession.java
new file mode 100644
index 0000000..2d8f274
--- /dev/null
+++ b/core/java/com/google/android/mms/util/DrmConvertSession.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.android.mms.util;
+
+import android.content.Context;
+import android.drm.DrmConvertedStatus;
+import android.drm.DrmManagerClient;
+import android.util.Log;
+import android.provider.Downloads;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+
+public class DrmConvertSession {
+ private DrmManagerClient mDrmClient;
+ private int mConvertSessionId;
+ private static final String TAG = "DrmConvertSession";
+
+ private DrmConvertSession(DrmManagerClient drmClient, int convertSessionId) {
+ mDrmClient = drmClient;
+ mConvertSessionId = convertSessionId;
+ }
+
+ /**
+ * Start of converting a file.
+ *
+ * @param context The context of the application running the convert session.
+ * @param mimeType Mimetype of content that shall be converted.
+ * @return A convert session or null in case an error occurs.
+ */
+ public static DrmConvertSession open(Context context, String mimeType) {
+ DrmManagerClient drmClient = null;
+ int convertSessionId = -1;
+ if (context != null && mimeType != null && !mimeType.equals("")) {
+ try {
+ drmClient = new DrmManagerClient(context);
+ try {
+ convertSessionId = drmClient.openConvertSession(mimeType);
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "Conversion of Mimetype: " + mimeType
+ + " is not supported.", e);
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Could not access Open DrmFramework.", e);
+ }
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG,
+ "DrmManagerClient instance could not be created, context is Illegal.");
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "DrmManagerClient didn't initialize properly.");
+ }
+ }
+
+ if (drmClient == null || convertSessionId < 0) {
+ return null;
+ } else {
+ return new DrmConvertSession(drmClient, convertSessionId);
+ }
+ }
+ /**
+ * Convert a buffer of data to protected format.
+ *
+ * @param buffer Buffer filled with data to convert.
+ * @param size The number of bytes that shall be converted.
+ * @return A Buffer filled with converted data, if execution is ok, in all
+ * other case null.
+ */
+ public byte [] convert(byte[] inBuffer, int size) {
+ byte[] result = null;
+ if (inBuffer != null) {
+ DrmConvertedStatus convertedStatus = null;
+ try {
+ if (size != inBuffer.length) {
+ byte[] buf = new byte[size];
+ System.arraycopy(inBuffer, 0, buf, 0, size);
+ convertedStatus = mDrmClient.convertData(mConvertSessionId, buf);
+ } else {
+ convertedStatus = mDrmClient.convertData(mConvertSessionId, inBuffer);
+ }
+
+ if (convertedStatus != null &&
+ convertedStatus.statusCode == DrmConvertedStatus.STATUS_OK &&
+ convertedStatus.convertedData != null) {
+ result = convertedStatus.convertedData;
+ }
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "Buffer with data to convert is illegal. Convertsession: "
+ + mConvertSessionId, e);
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Could not convert data. Convertsession: " +
+ mConvertSessionId, e);
+ }
+ } else {
+ throw new IllegalArgumentException("Parameter inBuffer is null");
+ }
+ return result;
+ }
+
+ /**
+ * Ends a conversion session of a file.
+ *
+ * @param fileName The filename of the converted file.
+ * @return Downloads.Impl.STATUS_SUCCESS if execution is ok.
+ * Downloads.Impl.STATUS_FILE_ERROR in case converted file can not
+ * be accessed. Downloads.Impl.STATUS_NOT_ACCEPTABLE if a problem
+ * occurs when accessing drm framework.
+ * Downloads.Impl.STATUS_UNKNOWN_ERROR if a general error occurred.
+ */
+ public int close(String filename) {
+ DrmConvertedStatus convertedStatus = null;
+ int result = Downloads.Impl.STATUS_UNKNOWN_ERROR;
+ if (mDrmClient != null && mConvertSessionId >= 0) {
+ try {
+ convertedStatus = mDrmClient.closeConvertSession(mConvertSessionId);
+ if (convertedStatus == null ||
+ convertedStatus.statusCode != DrmConvertedStatus.STATUS_OK ||
+ convertedStatus.convertedData == null) {
+ result = Downloads.Impl.STATUS_NOT_ACCEPTABLE;
+ } else {
+ RandomAccessFile rndAccessFile = null;
+ try {
+ rndAccessFile = new RandomAccessFile(filename, "rw");
+ rndAccessFile.seek(convertedStatus.offset);
+ rndAccessFile.write(convertedStatus.convertedData);
+ result = Downloads.Impl.STATUS_SUCCESS;
+ } catch (FileNotFoundException e) {
+ result = Downloads.Impl.STATUS_FILE_ERROR;
+ Log.w(TAG, "File: " + filename + " could not be found.", e);
+ } catch (IOException e) {
+ result = Downloads.Impl.STATUS_FILE_ERROR;
+ Log.w(TAG, "Could not access File: " + filename + " .", e);
+ } catch (IllegalArgumentException e) {
+ result = Downloads.Impl.STATUS_FILE_ERROR;
+ Log.w(TAG, "Could not open file in mode: rw", e);
+ } catch (SecurityException e) {
+ Log.w(TAG, "Access to File: " + filename +
+ " was denied denied by SecurityManager.", e);
+ } finally {
+ if (rndAccessFile != null) {
+ try {
+ rndAccessFile.close();
+ } catch (IOException e) {
+ result = Downloads.Impl.STATUS_FILE_ERROR;
+ Log.w(TAG, "Failed to close File:" + filename
+ + ".", e);
+ }
+ }
+ }
+ }
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Could not close convertsession. Convertsession: " +
+ mConvertSessionId, e);
+ }
+ }
+ return result;
+ }
+}
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 8e5db36..baf296d 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -21,12 +21,16 @@
#include "jni.h"
#include "GraphicsJNI.h"
#include <nativehelper/JNIHelp.h>
+
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_graphics_SurfaceTexture.h>
-#include <cutils/properties.h>
+#include <gui/SurfaceTexture.h>
+
#include <androidfw/ResourceTypes.h>
-#include <gui/SurfaceTexture.h>
+#include <private/hwui/DrawGlInfo.h>
+
+#include <cutils/properties.h>
#include <SkBitmap.h>
#include <SkCanvas.h>
@@ -196,7 +200,7 @@ static jint android_view_GLES20Canvas_getStencilSize(JNIEnv* env, jobject clazz)
// Functor
// ----------------------------------------------------------------------------
-static bool android_view_GLES20Canvas_callDrawGLFunction(JNIEnv* env, jobject clazz,
+static jint android_view_GLES20Canvas_callDrawGLFunction(JNIEnv* env, jobject clazz,
OpenGLRenderer* renderer, Functor *functor) {
android::uirenderer::Rect dirty;
return renderer->callDrawGLFunction(functor, dirty);
@@ -682,16 +686,16 @@ static void android_view_GLES20Canvas_destroyDisplayList(JNIEnv* env,
DisplayList::destroyDisplayListDeferred(displayList);
}
-static bool android_view_GLES20Canvas_drawDisplayList(JNIEnv* env,
+static jint android_view_GLES20Canvas_drawDisplayList(JNIEnv* env,
jobject clazz, OpenGLRenderer* renderer, DisplayList* displayList,
jint width, jint height, jobject dirty, jint flags) {
android::uirenderer::Rect bounds;
- bool redraw = renderer->drawDisplayList(displayList, width, height, bounds, flags);
- if (redraw && dirty != NULL) {
+ status_t status = renderer->drawDisplayList(displayList, width, height, bounds, flags);
+ if (status != DrawGlInfo::kStatusDone && dirty != NULL) {
env->CallVoidMethod(dirty, gRectClassInfo.set,
int(bounds.left), int(bounds.top), int(bounds.right), int(bounds.bottom));
}
- return redraw;
+ return status;
}
static void android_view_GLES20Canvas_outputDisplayList(JNIEnv* env,
@@ -865,7 +869,7 @@ static JNINativeMethod gMethods[] = {
{ "nGetStencilSize", "()I", (void*) android_view_GLES20Canvas_getStencilSize },
- { "nCallDrawGLFunction", "(II)Z",
+ { "nCallDrawGLFunction", "(II)I",
(void*) android_view_GLES20Canvas_callDrawGLFunction },
{ "nSave", "(II)I", (void*) android_view_GLES20Canvas_save },
@@ -943,7 +947,7 @@ static JNINativeMethod gMethods[] = {
{ "nGetDisplayListSize", "(I)I", (void*) android_view_GLES20Canvas_getDisplayListSize },
{ "nSetDisplayListName", "(ILjava/lang/String;)V",
(void*) android_view_GLES20Canvas_setDisplayListName },
- { "nDrawDisplayList", "(IIIILandroid/graphics/Rect;I)Z",
+ { "nDrawDisplayList", "(IIIILandroid/graphics/Rect;I)I",
(void*) android_view_GLES20Canvas_drawDisplayList },
{ "nCreateDisplayListRenderer", "()I", (void*) android_view_GLES20Canvas_createDisplayListRenderer },
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index d1e3642..5ae12b6 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -60,6 +60,9 @@
<protected-broadcast android:name="android.intent.action.REBOOT" />
<protected-broadcast android:name="android.intent.action.DOCK_EVENT" />
<protected-broadcast android:name="android.intent.action.MASTER_CLEAR_NOTIFICATION" />
+ <protected-broadcast android:name="android.intent.action.USER_ADDED" />
+ <protected-broadcast android:name="android.intent.action.USER_REMOVED" />
+ <protected-broadcast android:name="android.intent.action.USER_SWITCHED" />
<protected-broadcast android:name="android.app.action.ENTER_CAR_MODE" />
<protected-broadcast android:name="android.app.action.EXIT_CAR_MODE" />
@@ -1032,15 +1035,6 @@
android:label="@string/permlab_clearAppCache"
android:description="@string/permdesc_clearAppCache" />
- <!-- Allows an application to read the low-level system log files.
- Log entries can contain the user's private information,
- which is why this permission is 'dangerous'. -->
- <permission android:name="android.permission.READ_LOGS"
- android:permissionGroup="android.permission-group.PERSONAL_INFO"
- android:protectionLevel="dangerous"
- android:label="@string/permlab_readLogs"
- android:description="@string/permdesc_readLogs" />
-
<!-- Allows an application to use any media decoder when decoding for playback
@hide -->
<permission android:name="android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"
@@ -1076,6 +1070,15 @@
android:label="@string/permlab_dump"
android:description="@string/permdesc_dump" />
+ <!-- Allows an application to read the low-level system log files.
+ Log entries can contain the user's private information,
+ which is why this permission is not available to normal apps. -->
+ <permission android:name="android.permission.READ_LOGS"
+ android:permissionGroup="android.permission-group.DEVELOPMENT_TOOLS"
+ android:protectionLevel="signature|system|development"
+ android:label="@string/permlab_readLogs"
+ android:description="@string/permdesc_readLogs" />
+
<!-- Configure an application for debugging. -->
<permission android:name="android.permission.SET_DEBUG_APP"
android:permissionGroup="android.permission-group.DEVELOPMENT_TOOLS"
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index f1379f7..0481a67 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -342,9 +342,9 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"ስለ ገቢ እና ወጪ ጥሪዎችን ውሂብ ጨምሮ፣ የጡባዊተኮህን ምዝግብ ማስታወሻ ለመቀየር ለመተግበሪያው ይፈቅዳል። ይሄንን ተንኮል አዘል መተግበሪያዎች የስልክህን ምዝግብ ማስታወሻ ለመሰረዝ ወይም ለመለወጥ ሊጠቀሙበት ይችላሉ።"</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"ስለ ገቢ እና ወጪ ጥሪዎችን ውሂብ ጨምሮ፣ የስልክህን ምዝግብ ማስታወሻ ለመቀየር ለመተግበሪያው ይፈቅዳል። ይሄንን ተንኮል አዘል መተግበሪያዎች የስልክህን ምዝግብ ማስታወሻ ለመሰረዝ ወይም ለመለወጥ ሊጠቀሙበት ይችላሉ።"</string>
<string name="permlab_readProfile" msgid="6824681438529842282">"የመገለጫ ውሂብዎን ያንብቡ"</string>
- <string name="permdesc_readProfile" product="default" msgid="94520753797630679">"ልክ እንደ አንተ ስም እና የዕውቂያ መረጃ ፣ ባንተ መሳሪያ ወስጥ የተከማቹ የግል መገለጫ መረጃ ለማንበብ ለመተግበሪያው ይፈቅዳሉ፡፡ይሄም ማለት ሌሎች መተግበሪያዎች ሊለዩህ ይችላሉ እና ለሌሎች የመገለጫ መረጃህን ይልካሉ፡፡"</string>
+ <string name="permdesc_readProfile" product="default" msgid="94520753797630679">"ልክ እንደ አንተ ስም እና የዕውቂያ መረጃ ፣ በአንተ መሳሪያ ወስጥ የተከማቹ የግል መገለጫ መረጃ ለማንበብ ለመተግበሪያው ይፈቅዳሉ፡፡ይሄም ማለት ሌሎች መተግበሪያዎች ሊለዩህ ይችላሉ እና ለሌሎች የመገለጫ መረጃህን ይልካሉ፡፡"</string>
<string name="permlab_writeProfile" msgid="4679878325177177400">"የአርስዎ መገለጫ ውሂብ ላይ ይፃፉ"</string>
- <string name="permdesc_writeProfile" product="default" msgid="4637366723793045603">"ልክ እንደ አንተ ስም እና የዕውቂያ መረጃ ፣ ባንተ መሳሪያ ወስጥ የተከማቹ የግል መገለጫ መረጃ ለመለወጥ ወይም ለማከል ለመተግበሪያው ይፈቅዳሉ፡፡ይሄም ማለት ሌሎች መተግበሪያዎች ሊለዩህ ይችላሉ እና ለሌሎች የመገለጫ መረጃህን ይልካሉ፡፡"</string>
+ <string name="permdesc_writeProfile" product="default" msgid="4637366723793045603">"ልክ እንደ አንተ ስም እና የዕውቂያ መረጃ ፣ በአንተ መሳሪያ ወስጥ የተከማቹ የግል መገለጫ መረጃ ለመለወጥ ወይም ለማከል ለመተግበሪያው ይፈቅዳሉ፡፡ይሄም ማለት ሌሎች መተግበሪያዎች ሊለዩህ ይችላሉ እና ለሌሎች የመገለጫ መረጃህን ይልካሉ፡፡"</string>
<string name="permlab_readSocialStream" product="default" msgid="1268920956152419170">"የአንተን ማህበራዊ የውይይት ክፍሎች አንብብ"</string>
<string name="permdesc_readSocialStream" product="default" msgid="3419050808547335320">" ከአንተ ጓደኞች ማህበራዊ ዝማኔዎችን እንዲደርስባቸው እና እንዲያመሳስል ለመተግበሪያውይፈቅዳሉ፡፡ ተንኮል አዘል መተግበሪያዎች ይህን መዳረሻ ባንተና በጓደኞችህ መካከል በማህበራዊ አውታረመረቦች ያሉ የግል ተግባቦቶችን ለመዳረስ ሊጠቀሙበት ይችላሉ፡፡"</string>
<string name="permlab_writeSocialStream" product="default" msgid="3504179222493235645">"ወደ የአንተ ማህበራዊ የውይይት ክፍሎች ጻፍ"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 8384b48..55a298e 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -108,7 +108,7 @@
<string name="fcComplete" msgid="3118848230966886575">"Kode fitur selesai."</string>
<string name="fcError" msgid="3327560126588500777">"Masalah sambungan atau kode fitur tidak valid."</string>
<string name="httpErrorOk" msgid="1191919378083472204">"OK"</string>
- <string name="httpError" msgid="7956392511146698522">"Terjadi galat jaringan."</string>
+ <string name="httpError" msgid="7956392511146698522">"Terjadi kesalahan jaringan."</string>
<string name="httpErrorLookup" msgid="4711687456111963163">"Tidak dapat menemukan URL."</string>
<string name="httpErrorUnsupportedAuthScheme" msgid="6299980280442076799">"Skema autentikasi situs tidak didukung."</string>
<string name="httpErrorAuth" msgid="1435065629438044534">"Tidak dapat mengautentikasi."</string>
@@ -123,7 +123,7 @@
<string name="httpErrorFile" msgid="2170788515052558676">"Tidak dapat mengakses file."</string>
<string name="httpErrorFileNotFound" msgid="6203856612042655084">"Tidak dapat menemukan file yang diminta."</string>
<string name="httpErrorTooManyRequests" msgid="1235396927087188253">"Terlalu banyak permintaan yang diproses. Coba lagi nanti."</string>
- <string name="notification_title" msgid="8967710025036163822">"Galat saat masuk untuk <xliff:g id="ACCOUNT">%1$s</xliff:g>"</string>
+ <string name="notification_title" msgid="8967710025036163822">"Kesalahan saat masuk untuk <xliff:g id="ACCOUNT">%1$s</xliff:g>"</string>
<string name="contentServiceSync" msgid="8353523060269335667">"Sinkron"</string>
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Sinkron"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Terlalu banyak <xliff:g id="CONTENT_TYPE">%s</xliff:g> penghapusan."</string>
@@ -1061,7 +1061,7 @@
<string name="candidates_style" msgid="4333913089637062257"><u>"calon"</u></string>
<string name="ext_media_checking_notification_title" product="nosdcard" msgid="3449816005351468560">"Menyiapkan penyimpanan USB"</string>
<string name="ext_media_checking_notification_title" product="default" msgid="5457603418970994050">"Menyiapkan kartu SD"</string>
- <string name="ext_media_checking_notification_message" msgid="8287319882926737053">"Memeriksa galat."</string>
+ <string name="ext_media_checking_notification_message" msgid="8287319882926737053">"Memeriksa kesalahan."</string>
<string name="ext_media_nofs_notification_title" product="nosdcard" msgid="7788040745686229307">"Penyimpanan USB kosong"</string>
<string name="ext_media_nofs_notification_title" product="default" msgid="780477838241212997">"Kartu SD kosong"</string>
<string name="ext_media_nofs_notification_message" product="nosdcard" msgid="7840121067427269500">"Penyimpanan USB kosong atau sistem berkasnya tidak didukung."</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 3fc2f34..447af8a 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -743,8 +743,7 @@
<string name="double_tap_toast" msgid="4595046515400268881">"提示:輕按兩下即可縮放。"</string>
<string name="autofill_this_form" msgid="4616758841157816676">"自動填入功能"</string>
<string name="setup_autofill" msgid="7103495070180590814">"設定自動填入功能"</string>
- <!-- no translation found for autofill_address_name_separator (6350145154779706772) -->
- <skip />
+ <string name="autofill_address_name_separator" msgid="6350145154779706772">" //*** Empty segment here as a separator ***//"</string>
<string name="autofill_address_summary_name_format" msgid="3268041054899214945">"$1$2$3"</string>
<string name="autofill_address_summary_separator" msgid="7483307893170324129">", "</string>
<string name="autofill_address_summary_format" msgid="4874459455786827344">"$1$2$3"</string>
diff --git a/core/tests/coretests/src/android/net/SSLTest.java b/core/tests/coretests/src/android/net/SSLTest.java
index 810ed0d..c573498 100644
--- a/core/tests/coretests/src/android/net/SSLTest.java
+++ b/core/tests/coretests/src/android/net/SSLTest.java
@@ -16,17 +16,16 @@
package android.net;
-import android.net.SSLCertificateSocketFactory;
import android.test.suitebuilder.annotation.Suppress;
-import junit.framework.TestCase;
-
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
+import java.util.Arrays;
+import junit.framework.TestCase;
-//This test relies on network resources.
-@Suppress
public class SSLTest extends TestCase {
+ //This test relies on network resources.
+ @Suppress
public void testCertificate() throws Exception {
// test www.fortify.net/sslcheck.html
Socket ssl = SSLCertificateSocketFactory.getDefault().createSocket("www.fortify.net",443);
@@ -49,4 +48,35 @@ public class SSLTest extends TestCase {
// System.out.println(new String(b));
}
+
+ public void testStringsToNpnBytes() {
+ byte[] expected = {
+ 6, 's', 'p', 'd', 'y', '/', '2',
+ 8, 'h', 't', 't', 'p', '/', '1', '.', '1',
+ };
+ assertTrue(Arrays.equals(expected, SSLCertificateSocketFactory.toNpnProtocolsList(
+ new byte[] { 's', 'p', 'd', 'y', '/', '2' },
+ new byte[] { 'h', 't', 't', 'p', '/', '1', '.', '1' })));
+ }
+
+ public void testStringsToNpnBytesEmptyByteArray() {
+ try {
+ SSLCertificateSocketFactory.toNpnProtocolsList(new byte[0]);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testStringsToNpnBytesEmptyArray() {
+ byte[] expected = {};
+ assertTrue(Arrays.equals(expected, SSLCertificateSocketFactory.toNpnProtocolsList()));
+ }
+
+ public void testStringsToNpnBytesOversizedInput() {
+ try {
+ SSLCertificateSocketFactory.toNpnProtocolsList(new byte[256]);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
}
diff --git a/data/etc/android.hardware.audio.low_latency.xml b/data/etc/android.hardware.audio.low_latency.xml
deleted file mode 100644
index 677ec1c..0000000
--- a/data/etc/android.hardware.audio.low_latency.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<!-- This is the feature indicating low-latency audio, as specified by the
- CDD. ONLY devices that meet the CDD's requirements may declare this
- feature. -->
-<permissions>
- <feature name="android.hardware.audio.low_latency" />
-</permissions>
diff --git a/data/etc/android.hardware.bluetooth.xml b/data/etc/android.hardware.bluetooth.xml
deleted file mode 100644
index 4aa1744..0000000
--- a/data/etc/android.hardware.bluetooth.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<!-- Adds the feature indicating support for the Bluetooth API -->
-<permissions>
- <feature name="android.hardware.bluetooth" />
-</permissions>
diff --git a/data/etc/android.hardware.camera.autofocus.xml b/data/etc/android.hardware.camera.autofocus.xml
deleted file mode 100644
index d6e2b90..0000000
--- a/data/etc/android.hardware.camera.autofocus.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- This is the standard set of features for an auto-focus camera. -->
-<permissions>
- <feature name="android.hardware.camera" />
- <feature name="android.hardware.camera.autofocus" />
-</permissions>
diff --git a/data/etc/android.hardware.camera.flash-autofocus.xml b/data/etc/android.hardware.camera.flash-autofocus.xml
deleted file mode 100644
index 55f1900..0000000
--- a/data/etc/android.hardware.camera.flash-autofocus.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- This is the standard set of features for a camera with a flash. Note
- that this currently requires having auto-focus as well. -->
-<permissions>
- <feature name="android.hardware.camera" />
- <feature name="android.hardware.camera.autofocus" />
- <feature name="android.hardware.camera.flash" />
-</permissions>
diff --git a/data/etc/android.hardware.camera.front.xml b/data/etc/android.hardware.camera.front.xml
deleted file mode 100644
index a5a6998..0000000
--- a/data/etc/android.hardware.camera.front.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<!-- This is the standard set of features for a front facing camera. -->
-<permissions>
- <feature name="android.hardware.camera.front" />
-</permissions>
diff --git a/data/etc/android.hardware.camera.xml b/data/etc/android.hardware.camera.xml
deleted file mode 100644
index 00a1ed7..0000000
--- a/data/etc/android.hardware.camera.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- This is the standard set of features for a non auto-focus camera. -->
-<permissions>
- <feature name="android.hardware.camera" />
-</permissions>
diff --git a/data/etc/android.hardware.faketouch.multitouch.distinct.xml b/data/etc/android.hardware.faketouch.multitouch.distinct.xml
deleted file mode 100644
index b6e8d09..0000000
--- a/data/etc/android.hardware.faketouch.multitouch.distinct.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- This is the standard set of features for a indirect touch input device that supports
- independently-trackable multiple-finger multitouch. -->
-<permissions>
- <feature name="android.hardware.faketouch.multitouch" />
- <feature name="android.hardware.faketouch.multitouch.distinct" />
- <feature name="android.hardware.faketouch" />
-</permissions>
diff --git a/data/etc/android.hardware.faketouch.xml b/data/etc/android.hardware.faketouch.xml
deleted file mode 100644
index cb99097..0000000
--- a/data/etc/android.hardware.faketouch.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- This is the standard set of features for a devices that does not have
- a touch screen, but does have some other indirect input device such as
- a track pad. -->
-<permissions>
- <feature name="android.hardware.faketouch" />
-</permissions>
diff --git a/data/etc/android.hardware.location.gps.xml b/data/etc/android.hardware.location.gps.xml
deleted file mode 100644
index 72ab732..0000000
--- a/data/etc/android.hardware.location.gps.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- These are the location-related features for devices that include GPS. -->
-<permissions>
- <feature name="android.hardware.location" />
- <feature name="android.hardware.location.network" />
- <feature name="android.hardware.location.gps" />
-</permissions>
diff --git a/data/etc/android.hardware.location.xml b/data/etc/android.hardware.location.xml
deleted file mode 100644
index ab901cf..0000000
--- a/data/etc/android.hardware.location.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- These are the location features for devices that do not include GPS. Note
- that network location is required for all devices. -->
-<permissions>
- <feature name="android.hardware.location" />
- <feature name="android.hardware.location.network" />
-</permissions>
diff --git a/data/etc/android.hardware.nfc.xml b/data/etc/android.hardware.nfc.xml
deleted file mode 100644
index 81c4a84..0000000
--- a/data/etc/android.hardware.nfc.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<!-- This is the standard feature indicating that the device can communicate
- using Near-Field Communications (NFC). -->
-<permissions>
- <feature name="android.hardware.nfc" />
-</permissions>
diff --git a/data/etc/android.hardware.sensor.accelerometer.xml b/data/etc/android.hardware.sensor.accelerometer.xml
deleted file mode 100644
index 22f18b8..0000000
--- a/data/etc/android.hardware.sensor.accelerometer.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- Feature for devices with an accelerometer sensor. -->
-<permissions>
- <feature name="android.hardware.sensor.accelerometer" />
-</permissions>
diff --git a/data/etc/android.hardware.sensor.barometer.xml b/data/etc/android.hardware.sensor.barometer.xml
deleted file mode 100644
index ebd392d..0000000
--- a/data/etc/android.hardware.sensor.barometer.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<!-- Feature for devices with barometer. -->
-<permissions>
- <feature name="android.hardware.sensor.barometer" />
-</permissions>
diff --git a/data/etc/android.hardware.sensor.compass.xml b/data/etc/android.hardware.sensor.compass.xml
deleted file mode 100644
index 963847d..0000000
--- a/data/etc/android.hardware.sensor.compass.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- Feature for devices with a compass. -->
-<permissions>
- <feature name="android.hardware.sensor.compass" />
-</permissions>
diff --git a/data/etc/android.hardware.sensor.gyroscope.xml b/data/etc/android.hardware.sensor.gyroscope.xml
deleted file mode 100644
index fe79632..0000000
--- a/data/etc/android.hardware.sensor.gyroscope.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<!-- Feature for devices with gyroscope. -->
-<permissions>
- <feature name="android.hardware.sensor.gyroscope" />
-</permissions>
diff --git a/data/etc/android.hardware.sensor.light.xml b/data/etc/android.hardware.sensor.light.xml
deleted file mode 100644
index 78b0fec..0000000
--- a/data/etc/android.hardware.sensor.light.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- Feature for devices with an ambient light sensor. -->
-<permissions>
- <feature name="android.hardware.sensor.light" />
-</permissions>
diff --git a/data/etc/android.hardware.sensor.proximity.xml b/data/etc/android.hardware.sensor.proximity.xml
deleted file mode 100644
index d1948de..0000000
--- a/data/etc/android.hardware.sensor.proximity.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- Feature for devices with a proximity sensor. -->
-<permissions>
- <feature name="android.hardware.sensor.proximity" />
-</permissions>
diff --git a/data/etc/android.hardware.telephony.cdma.xml b/data/etc/android.hardware.telephony.cdma.xml
deleted file mode 100644
index 082378d..0000000
--- a/data/etc/android.hardware.telephony.cdma.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- This is the standard set of telephony features for a CDMA phone. -->
-<permissions>
- <feature name="android.hardware.telephony" />
- <feature name="android.hardware.telephony.cdma" />
-</permissions>
diff --git a/data/etc/android.hardware.telephony.gsm.xml b/data/etc/android.hardware.telephony.gsm.xml
deleted file mode 100644
index 7927fa8..0000000
--- a/data/etc/android.hardware.telephony.gsm.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- This is the standard set of telephony features for a GSM phone. -->
-<permissions>
- <feature name="android.hardware.telephony" />
- <feature name="android.hardware.telephony.gsm" />
-</permissions>
diff --git a/data/etc/android.hardware.touchscreen.multitouch.distinct.xml b/data/etc/android.hardware.touchscreen.multitouch.distinct.xml
deleted file mode 100644
index 35eeefd..0000000
--- a/data/etc/android.hardware.touchscreen.multitouch.distinct.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- This is the standard set of features for a touchscreen that supports
- independently-trackable multiple-finger multitouch. -->
-<permissions>
- <feature name="android.hardware.touchscreen" />
- <feature name="android.hardware.touchscreen.multitouch" />
- <feature name="android.hardware.touchscreen.multitouch.distinct" />
- <feature name="android.hardware.faketouch" />
-</permissions>
diff --git a/data/etc/android.hardware.touchscreen.multitouch.jazzhand.xml b/data/etc/android.hardware.touchscreen.multitouch.jazzhand.xml
deleted file mode 100644
index ed6606d..0000000
--- a/data/etc/android.hardware.touchscreen.multitouch.jazzhand.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<!-- This is the standard set of features for a touchscreen that supports
- independently-trackable multiple-finger multitouch. -->
-<permissions>
- <feature name="android.hardware.touchscreen" />
- <feature name="android.hardware.touchscreen.multitouch" />
- <feature name="android.hardware.touchscreen.multitouch.distinct" />
- <feature name="android.hardware.touchscreen.multitouch.jazzhand" />
- <feature name="android.hardware.faketouch" />
-</permissions>
diff --git a/data/etc/android.hardware.touchscreen.multitouch.xml b/data/etc/android.hardware.touchscreen.multitouch.xml
deleted file mode 100644
index 1d59a27..0000000
--- a/data/etc/android.hardware.touchscreen.multitouch.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- This is the standard set of features for a touchscreen that supports
- basic multitouch capable of gestures but not fully-indendent finger
- tracking. -->
-<permissions>
- <feature name="android.hardware.touchscreen" />
- <feature name="android.hardware.touchscreen.multitouch" />
- <feature name="android.hardware.faketouch" />
-</permissions>
diff --git a/data/etc/android.hardware.touchscreen.xml b/data/etc/android.hardware.touchscreen.xml
deleted file mode 100644
index 5b5ddf9..0000000
--- a/data/etc/android.hardware.touchscreen.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- This is the standard set of features for a touchscreen that does not
- support multitouch. -->
-<permissions>
- <feature name="android.hardware.touchscreen" />
- <feature name="android.hardware.faketouch" />
-</permissions>
diff --git a/data/etc/android.hardware.usb.accessory.xml b/data/etc/android.hardware.usb.accessory.xml
deleted file mode 100644
index 80a0904..0000000
--- a/data/etc/android.hardware.usb.accessory.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- This is the standard feature indicating that the device supports USB accessories. -->
-<permissions>
- <feature name="android.hardware.usb.accessory" />
- <library name="com.android.future.usb.accessory"
- file="/system/framework/com.android.future.usb.accessory.jar" />
-</permissions>
diff --git a/data/etc/android.hardware.usb.host.xml b/data/etc/android.hardware.usb.host.xml
deleted file mode 100644
index b0ca82c..0000000
--- a/data/etc/android.hardware.usb.host.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- This is the standard feature indicating that the device can communicate
- with USB devices as the USB host. -->
-<permissions>
- <feature name="android.hardware.usb.host" />
-</permissions>
diff --git a/data/etc/android.hardware.wifi.direct.xml b/data/etc/android.hardware.wifi.direct.xml
deleted file mode 100644
index 78cb474..0000000
--- a/data/etc/android.hardware.wifi.direct.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- This is the standard feature indicating that the device includes WiFi Direct. -->
-<permissions>
- <feature name="android.hardware.wifi.direct" />
-</permissions>
diff --git a/data/etc/android.hardware.wifi.xml b/data/etc/android.hardware.wifi.xml
deleted file mode 100644
index 512570c..0000000
--- a/data/etc/android.hardware.wifi.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- This is the standard feature indicating that the device includes WiFi. -->
-<permissions>
- <feature name="android.hardware.wifi" />
-</permissions>
diff --git a/data/etc/android.software.sip.voip.xml b/data/etc/android.software.sip.voip.xml
deleted file mode 100644
index edd06c1..0000000
--- a/data/etc/android.software.sip.voip.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- This is the standard set of features for devices that support SIP-based VoIP. -->
-<permissions>
- <feature name="android.software.sip" />
- <feature name="android.software.sip.voip" />
-</permissions>
diff --git a/data/etc/android.software.sip.xml b/data/etc/android.software.sip.xml
deleted file mode 100644
index d9fcaad..0000000
--- a/data/etc/android.software.sip.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- This is the standard set of features for devices that support the SIP API. -->
-<permissions>
- <feature name="android.software.sip" />
-</permissions>
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
deleted file mode 100644
index 9d2a0cb..0000000
--- a/data/etc/handheld_core_hardware.xml
+++ /dev/null
@@ -1,65 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- These are the hardware components that all handheld devices
- must include. Devices with optional hardware must also include extra
- hardware files, per the comments below.
-
- Handheld devices include phones, mobile Internet devices (MIDs),
- Personal Media Players (PMPs), small tablets (7" or less), and similar
- devices.
--->
-<permissions>
- <feature name="android.hardware.camera" />
- <feature name="android.hardware.location" />
- <feature name="android.hardware.location.network" />
- <feature name="android.hardware.sensor.compass" />
- <feature name="android.hardware.sensor.accelerometer" />
- <feature name="android.hardware.bluetooth" />
- <feature name="android.hardware.touchscreen" />
- <feature name="android.hardware.microphone" />
- <feature name="android.hardware.screen.portrait" />
- <feature name="android.hardware.screen.landscape" />
- <!-- devices with GPS must include android.hardware.location.gps.xml -->
- <!-- devices with an autofocus camera and/or flash must include either
- android.hardware.camera.autofocus.xml or
- android.hardware.camera.autofocus-flash.xml -->
- <!-- devices with a front facing camera must include
- android.hardware.camera.front.xml -->
- <!-- devices with WiFi must also include android.hardware.wifi.xml -->
- <!-- devices that support multitouch must include the most appropriate one
- of these files:
-
- If only partial (non-independent) pointers are supported:
- android.hardware.touchscreen.multitouch.xml
-
- If up to 4 independently tracked pointers are supported:
- include android.hardware.touchscreen.multitouch.distinct.xml
-
- If 5 or more independently tracked pointers are supported:
- include android.hardware.touchscreen.multitouch.jazzhand.xml
-
- ONLY ONE of the above should be included. -->
- <!-- devices with an ambient light sensor must also include
- android.hardware.sensor.light.xml -->
- <!-- devices with a proximity sensor must also include
- android.hardware.sensor.proximity.xml -->
- <!-- GSM phones must also include android.hardware.telephony.gsm.xml -->
- <!-- CDMA phones must also include android.hardware.telephony.cdma.xml -->
- <!-- Devices that have low-latency audio stacks suitable for apps like
- VoIP may include android.hardware.audio.low_latency.xml. ONLY apps
- that meet the requirements specified in the CDD may include this. -->
-</permissions>
diff --git a/data/etc/tablet_core_hardware.xml b/data/etc/tablet_core_hardware.xml
deleted file mode 100644
index bf29fe4..0000000
--- a/data/etc/tablet_core_hardware.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<!-- These are the hardware components that all handheld devices
- must include. Devices with optional hardware must also include extra
- hardware files, per the comments below.
-
- Handheld devices include phones, mobile Internet devices (MIDs),
- Personal Media Players (PMPs), small tablets (7" or less), and similar
- devices.
--->
-<permissions>
- <feature name="android.hardware.location" />
- <feature name="android.hardware.location.network" />
- <feature name="android.hardware.sensor.compass" />
- <feature name="android.hardware.sensor.accelerometer" />
- <feature name="android.hardware.bluetooth" />
- <feature name="android.hardware.touchscreen" />
- <feature name="android.hardware.touchscreen.multitouch" />
- <feature name="android.hardware.touchscreen.multitouch.distinct" />
- <feature name="android.hardware.microphone" />
- <feature name="android.hardware.screen.portrait" />
- <feature name="android.hardware.screen.landscape" />
- <!-- devices with GPS must include android.hardware.location.gps.xml -->
- <!-- devices with a rear-facing camera must include one of these as appropriate:
- android.hardware.camera.xml or
- android.hardware.camera.autofocus.xml or
- android.hardware.camera.autofocus-flash.xml -->
- <!-- devices with a front facing camera must include
- android.hardware.camera.front.xml -->
- <!-- devices with WiFi must also include android.hardware.wifi.xml -->
- <!-- devices with an ambient light sensor must also include
- android.hardware.sensor.light.xml -->
- <!-- devices with a proximity sensor must also include
- android.hardware.sensor.proximity.xml -->
- <!-- devices with a barometer must also include
- android.hardware.sensor.barometer.xml -->
- <!-- devices with a gyroscope must also include
- android.hardware.sensor.gyroscope.xml -->
- <!-- GSM phones must also include android.hardware.telephony.gsm.xml -->
- <!-- CDMA phones must also include android.hardware.telephony.cdma.xml -->
-</permissions>
diff --git a/docs/html/guide/appendix/api-levels.jd b/docs/html/guide/appendix/api-levels.jd
index cc98f8f..bc7d83b 100644
--- a/docs/html/guide/appendix/api-levels.jd
+++ b/docs/html/guide/appendix/api-levels.jd
@@ -350,11 +350,11 @@ including the latest version, and provides an updater tool that you can use to
download other platform versions as necessary. </p>
<p>To access the updater, use the <code>android</code> command-line tool,
-located in the &lt;sdk&gt;/tools directory. You can launch the Updater by using
-the <code>android</code> command without specifying any options. You can
+located in the &lt;sdk&gt;/tools directory. You can launch the SDK updater by
+executing <code>android sdk</code>. You can
also simply double-click the android.bat (Windows) or android (OS X/Linux) file.
In ADT, you can also access the updater by selecting
-<strong>Window</strong>&nbsp;>&nbsp;<strong>Android SDK and AVD
+<strong>Window</strong>&nbsp;>&nbsp;<strong>Android SDK
Manager</strong>.</p>
<p>To run your application against different platform versions in the emulator,
diff --git a/docs/html/guide/developing/building/building-cmdline.jd b/docs/html/guide/developing/building/building-cmdline.jd
index c43962a..fd90b1a 100644
--- a/docs/html/guide/developing/building/building-cmdline.jd
+++ b/docs/html/guide/developing/building/building-cmdline.jd
@@ -202,12 +202,12 @@ ant release
<ol>
<li>
- <strong>Open the SDK and AVD Manager and launch a virtual device</strong>
+ <strong>Open the AVD Manager and launch a virtual device</strong>
- <p>From your SDK's <code>platform-tools/</code> directory, execute the {@code android} tool with no
- arguments:</p>
+ <p>From your SDK's <code>platform-tools/</code> directory, execute the {@code android} tool
+with the <code>avd</code> options:</p>
<pre>
-android
+android avd
</pre>
<p>In the <em>Virtual Devices</em> view, select an AVD and click <strong>Start</strong>.</p>
@@ -237,7 +237,7 @@ adb -s emulator-5554 install <em>path/to/your/app</em>.apk
</ol>
<p>If you don't see your application on the emulator, try closing the emulator and launching the
- virtual device again from the SDK and AVD Manager. Sometimes when you install an application for the
+ virtual device again from the AVD Manager. Sometimes when you install an application for the
first time, it won't show up in the application launcher or be accessible by other applications.
This is because the package manager usually examines manifests completely only on emulator
startup.</p>
diff --git a/docs/html/guide/developing/devices/index.jd b/docs/html/guide/developing/devices/index.jd
index a7d00f3..64651a1 100644
--- a/docs/html/guide/developing/devices/index.jd
+++ b/docs/html/guide/developing/devices/index.jd
@@ -7,9 +7,9 @@ page.title=Managing Virtual Devices
<p>The easiest way to create an AVD is to use the graphical <a href=
"{@docRoot}guide/developing/devices/managing-avds.html">AVD Manager</a>, which you launch
- from Eclipse by clicking <strong>Window &gt; Android SDK and AVD Manager</strong>. You can also start
- the AVD Manager from the command line by calling the <code>android</code> tool in the <strong>tools</strong>
- directory of the Android SDK.</p>
+ from Eclipse by clicking <strong>Window &gt; AVD Manager</strong>. You can also start the AVD
+Manager from the command line by calling the <code>android</code> tool with the <code>avd</code>
+options, from the <strong>&lt;sdk>/tools/</strong> directory.</p>
<p>You can also create AVDs on the command line by passing the <code>android</code> tool options.
For more information on how to create AVDs in this manner, see <a href=
diff --git a/docs/html/guide/developing/devices/managing-avds.jd b/docs/html/guide/developing/devices/managing-avds.jd
index e70a0bb..412bd91 100644
--- a/docs/html/guide/developing/devices/managing-avds.jd
+++ b/docs/html/guide/developing/devices/managing-avds.jd
@@ -42,8 +42,8 @@ parent.link=index.html
<li>Start the AVD Manager:
<ul>
- <li>In Eclipse: select <strong>Window &gt; Android SDK and AVD Manager</strong>, or click
- the Android SDK and AVD Manager icon in the Eclipse toolbar.</li>
+ <li>In Eclipse: select <strong>Window &gt; AVD Manager</strong>, or click
+ the AVD Manager icon in the Eclipse toolbar.</li>
<li>In other IDEs: Navigate to your SDK's <code>tools/</code> directory and execute the
<code>android</code> tool with no arguments.</li>
@@ -72,7 +72,7 @@ parent.link=index.html
<li>Click <strong>Create AVD</strong>.</li>
</ol>
- <p>Your AVD is now ready and you can either close the SDK and AVD Manager, create more AVDs, or
+ <p>Your AVD is now ready and you can either close the AVD Manager, create more AVDs, or
launch an emulator with the AVD by selecting a device and clicking <strong>Start</strong>.</p>
<h3 id="hardwareopts">Hardware options</h3>
diff --git a/docs/html/guide/developing/projects/index.jd b/docs/html/guide/developing/projects/index.jd
index 63e67cd..b16e466 100644
--- a/docs/html/guide/developing/projects/index.jd
+++ b/docs/html/guide/developing/projects/index.jd
@@ -209,8 +209,8 @@ used.</dd>
application uses code and resources from an example library project called TicTacToeLib.</p>
<p>To download the sample applications and run them as projects in
- your environment, use the <em>Android SDK and AVD Manager</em> to download the "Samples for
- SDK API 8" (or later) component into your SDK.</p>
+ your environment, use the <em>Android SDK Manager</em> to download the "Samples for
+ SDK API 8" (or later) package into your SDK.</p>
<p>For more information and to browse the code of the samples, see
the <a href="{@docRoot}resources/samples/TicTacToeMain/index.html">TicTacToeMain
@@ -227,8 +227,8 @@ used.</dd>
<p class="note"><strong>Note:</strong> You need SDK Tools r14 or newer to use the new library
project feature that generates each library project into its own JAR file.
You can download the tools and platforms using the
- <em>Android SDK and AVD Manager</em>, as described in
- <a href="{@docRoot}sdk/adding-components.html">Adding SDK Components</a>.</p>
+ <em>Android SDK Manager</em>, as described in
+ <a href="{@docRoot}sdk/adding-components.html">Adding SDK Packages</a>.</p>
<p>If you have source code and resources that are common to multiple Android projects, you
can move them to a library project so that it is easier to maintain across applications and
diff --git a/docs/html/guide/developing/tools/adt.jd b/docs/html/guide/developing/tools/adt.jd
index e48a5ae..d473e85 100644
--- a/docs/html/guide/developing/tools/adt.jd
+++ b/docs/html/guide/developing/tools/adt.jd
@@ -102,9 +102,8 @@ Project site.</p>
(<strong>Window &gt; Open Perspective &gt; Traceview</strong>). </li>
<li><a href="{@docRoot}guide/developing/tools/android.html">android</a>: Provides access to
- the Android SDK and AVD Manager. Other <code>android</code> features such as creating or
- updating projects (application and library) are integrated throughout the Eclipse IDE
- (<strong>Window &gt; Android SDK and AVD Manager</strong>). </li>
+ the Android SDK Manager and AVD Manager. Other <code>android</code> features such as creating or
+ updating projects (application and library) are integrated throughout the Eclipse IDE. </li>
<li><a href="{@docRoot}guide/developing/debugging/debugging-ui.html#HierarchyViewer">Hierarchy
Viewer</a>: Allows you to visualize your application's view hierarchy to find inefficiencies
diff --git a/docs/html/guide/developing/tools/android.jd b/docs/html/guide/developing/tools/android.jd
index a67012f..295a720 100644
--- a/docs/html/guide/developing/tools/android.jd
+++ b/docs/html/guide/developing/tools/android.jd
@@ -15,9 +15,16 @@ Line</a>.</li>
the Command Line</a>.</li>
<li>Update your Android SDK with new platforms, add-ons, and documentation. See <a href=
- "{@docRoot}sdk/adding-components.html">Adding SDK Components</a>.</li>
+ "{@docRoot}sdk/adding-components.html">Adding SDK Packages</a>.</li>
</ul>If you are using Eclipse, the <code>android</code> tool's features are integrated
into ADT, so you should not need to use this tool directly.
+
+ <p class="note"><strong>Note:</strong> The documentation of options below is not exhaustive
+and may be out of date. For the most current list of options, execute <code>android
+--help</code>.</p>
+
+
+
<h2>Syntax</h2>
<pre>android [global options] action [action options]</pre>
@@ -52,6 +59,26 @@ Line</a>.</li>
</tr>
<tr>
+ <td rowspan="6"><code>avd</code></td>
+
+ <td>None</td>
+
+ <td>Launch the AVD Manager</td>
+
+ <td></td>
+ </tr>
+
+ <tr>
+ <td rowspan="6"><code>sdk</code></td>
+
+ <td>None</td>
+
+ <td>Launch the Android SDK Manager</td>
+
+ <td></td>
+ </tr>
+
+ <tr>
<td rowspan="6"><code>create avd</code></td>
<td><code>-n &lt;name&gt;</code></td>
diff --git a/docs/html/guide/developing/tools/index.jd b/docs/html/guide/developing/tools/index.jd
index 3d831f3..5e9f686 100644
--- a/docs/html/guide/developing/tools/index.jd
+++ b/docs/html/guide/developing/tools/index.jd
@@ -12,8 +12,8 @@ latest Android platform.</p>
<h2 id="tools-sdk">SDK Tools</h2>
<p>The SDK tools are installed with the SDK starter package and are periodically updated.
The SDK tools are required if you are developing Android applications. The most important SDK tools
-include the Android SDK and AVD Manager (<code>android</code>), the emulator
-(<code>emulator</code>), and the Dalvik Debug Monitor Server
+include the Android SDK Manager (<code>android sdk</code>), the AVD Manager (<code>android
+avd</code>) the emulator (<code>emulator</code>), and the Dalvik Debug Monitor Server
(<code>ddms</code>). A short summary of some frequently-used SDK tools is provided below.</p>
<dl>
diff --git a/docs/html/guide/market/billing/billing_integrate.jd b/docs/html/guide/market/billing/billing_integrate.jd
index b49dc25..4b3650f 100755
--- a/docs/html/guide/market/billing/billing_integrate.jd
+++ b/docs/html/guide/market/billing/billing_integrate.jd
@@ -149,8 +149,8 @@ relies on these utility classes.</td>
</table>
<p>The in-app billing sample application is available as a downloadable component of the Android
-SDK. To download the sample application component, launch the Android SDK and AVD Manager and then
-select the "Google Market Billing package" component (see figure 1), and click <strong>Install
+SDK. To download the sample application component, launch the Android SDK Manager and then
+select the <strong>Google Market Billing package</strong> component (see figure 1), and click <strong>Install
Selected</strong> to begin the download.</p>
@@ -160,7 +160,7 @@ Selected</strong> to begin the download.</p>
the AIDL file.
</p>
-<p>When the download is complete, the Android SDK and AVD Manager saves the component into the
+<p>When the download is complete, the Android SDK Manager saves the component into the
following directory:</p>
<p><code>&lt;sdk&gt;/extras/google/market_billing/</code></p>
diff --git a/docs/html/guide/market/licensing/overview.jd b/docs/html/guide/market/licensing/overview.jd
index 05a3a40..e7e23f8 100644
--- a/docs/html/guide/market/licensing/overview.jd
+++ b/docs/html/guide/market/licensing/overview.jd
@@ -58,8 +58,9 @@ information about the application and user&mdash;your application and the Google
together to assemble the information and the Google Play client passes it to the server. </p>
<p>To help you add licensing to your application, the Android SDK provides a downloadable set of
-library sources that you can include in your application project: the "Google Market Licensing
-package." The License Verification Library (LVL) is a library you can add to your application that
+library sources that you can include in your application project: the Google Market
+Licensing package. The License Verification Library (LVL) is a library you can add to your
+application that
handles all of the licensing-related communication with the Google Play licensing service. With
the LVL added to your application, your application can determine its licensing status for the
current user by simply calling a method and implementing a callback that receives the status
@@ -116,7 +117,7 @@ tampered with or that are spoofed.</p>
<h2 id="LVL">Licensing Verification Library</h2>
-<p>The Android SDK provides a downloadable component called the "Google Market Licensing package,"
+<p>The Android SDK provides a downloadable package called the Google Market Licensing package,
which includes the License Verification Library (LVL). The LVL greatly simplifies the process of
adding licensing to your application and helps ensure a more secure, robust implementation for your
application. The LVL provides internal classes that handle most of the standard operations of a
@@ -162,8 +163,8 @@ does not cache any response data and allows the application access <em>only</em>
when the server returns a licensed response.</dd>
</dl>
-<p>The LVL is available as a downloadable component of the Android SDK. The
-component includes both the LVL itself and an example application that shows how
+<p>The LVL is available as a downloadable package of the Android SDK. The
+package includes both the LVL itself and an example application that shows how
the library should be integrated with your application and how your application
should manage response data, UI interaction, and error conditions. </p>
diff --git a/docs/html/guide/market/licensing/setting-up.jd b/docs/html/guide/market/licensing/setting-up.jd
index 41e3bc4..0de7819 100644
--- a/docs/html/guide/market/licensing/setting-up.jd
+++ b/docs/html/guide/market/licensing/setting-up.jd
@@ -173,25 +173,25 @@ your application without having publish it. For more information see <a
href="#acct-signin">Signing in to an authorized account</a>, below.</p></li>
</ul>
-<p>Several versions of the add-on are available through the SDK Manager, but only
-<strong>Google APIs Add-On, API 8 (release 2) or higher</strong> includes the necessary Google
+<p>Several versions of the Google APIs add-on are available through the SDK Manager, but only
+the version for Android 2.2 and higher includes the necessary Google
Play services.</p>
<p>To set up an emulator for adding licensing to an application, follow
these steps: </p>
<ol>
- <li>Launch the Android SDK Manager. </li>
- <li>In the <strong>Available Packages</strong> panel, select and download the
-SDK component "Google APIs (Google Inc.) - API Level 8" (or higher) from the SDK
-repository.
- <p>When the download is complete, use the Android SDK Manager to
-create a new AVD based on that component, described next.</p></li>
- <li>In the <strong>Virtual
-Devices</strong> panel of the Android SDK Manager, click
+ <li>Launch the Android SDK Manager (available under the Eclipse <strong>Window</strong>
+menu or by executing {@code &lt;sdk>/tools/android sdk}).</li>
+ <li>Select and download <strong>Google APIs</strong> for the Android version you'd like to target
+(must be Android 2.2 or higher).</li>
+ <li>When the download is complete, open the AVD Manager (available under the Eclipse
+<strong>Window</strong>
+menu or by executing {@code &lt;sdk>/tools/android avd}).</li>
+ <li>Click
<strong>New</strong> and set the configuration details for the new AVD. </li>
<li>In the dialog that appears, assign a descriptive name to the AVD and then
-use the "Target" menu to choose the "Google APIs (Google Inc.) - API Level 8" as
+use the Target menu to choose the <strong>Google APIs</strong> as
the system image to run on the new AVD. Set the other configuration details as
needed and then click <strong>Create AVD</strong> to finish. The SDK tools
create the new AVD configuration, which then appears in the list of available
@@ -238,8 +238,8 @@ greatly simplify the work that you need to do to add licensing to your
application. In all cases, we recommend that you download the LVL and use it as
the basis for the licensing implementation in your application.</p>
-<p>The LVL is available as a downloadable component of the Android SDK. The
-component includes: </p>
+<p>The LVL is available as a downloadable package of the Android SDK. The
+package includes: </p>
<ul>
<li>The LVL sources, stored inside an Android library project. </li>
@@ -248,9 +248,9 @@ project. The example illustrates how an application uses the library helper
classes to check and enforce licensing.</li>
</ul>
-<p>To download the LVL component into your development environment, use the
+<p>To download the LVL package into your development environment, use the
Android SDK Manager. Launch the Android SDK Manager and then
-select the "Google Market Licensing" component, as shown in figure 2.
+select the <strong>Google Market Licensing</strong> package, as shown in figure 2.
Accept the terms and click <strong>Install Selected</strong> to begin the download. </p>
<img src="{@docRoot}images/licensing_package.png" alt=""/>
@@ -265,8 +265,8 @@ the LVL library project and the example application into these directories: </p>
<code>&lt;<em>sdk</em>&gt;/extras/google/market_licensing/sample/</code>&nbsp;&nbsp;(the example
application)</p>
-<p>If you aren't familiar with how to download components into your SDK, see the
-<a href="{@docRoot}sdk/adding-components.html">Adding SDK Components</a>
+<p>If you aren't familiar with how to download packess into your SDK, see the
+<a href="{@docRoot}sdk/adding-components.html">Adding SDK Packages</a>
document. </p>
@@ -676,7 +676,7 @@ credentials. </p>
<ol>
<li>Open Settings &gt; Accounts &amp; sync</li>
-<li>Select <strong>Add Account</strong> and choose to add a "Google" account.
+<li>Select <strong>Add Account</strong> and choose to add a Google account.
</li>
<li>Select <strong>Next</strong> and then <strong>Sign in</strong>.</li>
<li>Enter the username and password of either the publisher account or a test
diff --git a/docs/html/guide/practices/optimizing-for-3.0.jd b/docs/html/guide/practices/optimizing-for-3.0.jd
index 140c50c..d6c621e 100644
--- a/docs/html/guide/practices/optimizing-for-3.0.jd
+++ b/docs/html/guide/practices/optimizing-for-3.0.jd
@@ -108,7 +108,7 @@ SDK with the new platform:</p>
SDK starter package now</a>.)</p>
<ol>
- <li><a href="{@docRoot}sdk/adding-components.html#launching">Launch the Android SDK and AVD
+ <li><a href="{@docRoot}sdk/adding-components.html#launching">Launch the Android SDK
Manager</a> and install the following:
<ul>
<li>SDK Platform Android 3.0</li>
@@ -147,7 +147,7 @@ Android 3.0, the emulator is still best way to evaluate your application's appea
functionality on Android 3.0.</p>
<p class="note"><strong>Tip:</strong> To improve the startup time for the emulator, enable snapshots
-for the AVD when you create it with the SDK and AVD Manager (there's a checkbox in the AVD creator
+for the AVD when you create it with the AVD Manager (there's a checkbox in the AVD creator
to <strong>Enable</strong> snapshots). Then, start the AVD from the AVD manager and check <b>Launch
from snapshot</b> and <b>Save to snapshot</b>. This way, when you close the emulator, a snapshot of
the AVD state is saved and used to quickly relaunch the AVD next time. However, when you choose to
@@ -281,7 +281,7 @@ to help you add features from Android 3.0 without requiring you to change your <
href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code android:minSdkVersion}</a>
or build target, we're providing a static library called the <a
href="{@docRoot}sdk/compatibility-library.html">Compatibility Library</a>
-(downloadable from the AVD and SDK Manager).</p>
+(downloadable from the Android SDK Manager).</p>
<p>This library includes APIs for <a
href="{@docRoot}guide/topics/fundamentals/fragments.html">fragments</a>, <a
href="{@docRoot}guide/topics/fundamentals/loaders.html">loaders</a>, and some updated classes. By
@@ -421,7 +421,7 @@ href="{@docRoot}sdk/android-3.0.html">Android 3.0 Platform</a> document.</p>
href="{@docRoot}sdk/android-3.0.html#api">Android 3.0 Platform</a> document also have accompanying
samples that allow you to preview the effects and can help you understand how to use them. To get
the samples, download them from the SDK repository <a href="{@docRoot}sdk/adding-components.html"
->using the Android SDK and AVD Manager</a>. After downloading the samples ("Samples for SDK API
+>using the Android SDK Manager</a>. After downloading the samples ("Samples for SDK API
11"), you can find them in <code>&lt;sdk_root&gt;/samples/android-11/</code>. The following list
provides links to the browsable source code for some of the samples:</p>
diff --git a/docs/html/guide/practices/screens_support.jd b/docs/html/guide/practices/screens_support.jd
index 29dff26..a870b22 100644
--- a/docs/html/guide/practices/screens_support.jd
+++ b/docs/html/guide/practices/screens_support.jd
@@ -1242,12 +1242,12 @@ have to buy various devices just to test your application's screen support.</p>
<p>To set up an environment for testing your application's screen support, you should create a
series of AVDs (Android Virtual Devices), using emulator skins and screen configurations that
emulate the screen sizes and densities you want your application to support. To do so, you can use
-the Android SDK and AVD Manager to create the AVDs and launch them with a graphical interface.</p>
+the AVD Manager to create the AVDs and launch them with a graphical interface.</p>
-<p>To launch the Android SDK and AVD Manager, execute the {@code
+<p>To launch the Android SDK Manager, execute the {@code
SDK Manager.exe} from your Android SDK directory (on Windows only) or execute {@code android} from
-the {@code &lt;sdk&gt;/tools/} directory (on all platforms). Figure 6 shows the Android SDK and
-AVD Manager with a selection of AVDs, for testing various screen configurations.</p>
+the {@code &lt;sdk&gt;/tools/} directory (on all platforms). Figure 6 shows the AVD
+Manager with a selection of AVDs, for testing various screen configurations.</p>
<p>Table 3 shows the various emulator skins that are available in the Android SDK, which you can use
to emulate some of the most common screen configurations.</p>
@@ -1340,7 +1340,7 @@ dashboard.</p>
<div class="figure" style="width:204px">
<img src="{@docRoot}images/screens_support/avd-start.png" alt="" />
<p class="img-caption"><strong>Figure 7.</strong>
- Size and density options you can set, when starting an AVD from the Android SDK and AVD
+ Size and density options you can set, when starting an AVD from the AVD
Manager.</p>
</div>
@@ -1349,12 +1349,12 @@ up to run at a physical size that closely matches an actual device. This makes
it a lot easier to compare the results at various sizes and densities. To
do so you need to know the approximate density, in dpi, of your computer
monitor (for instance, a 30" Dell monitor has a density of about 96 dpi). When you launch an AVD
-from the Android SDK and AVD Manager, you can specify the screen size for the emulator and your
+from the AVD Manager, you can specify the screen size for the emulator and your
monitor dpi in the Launch Options, as shown in figure 7.</p>
<p>If you would like to test your application on a screen that uses a resolution
or density not supported by the built-in skins, you can create an AVD that uses a custom resolution
-or density. When creating the AVD from the Android SDK and AVD Manager, specify the Resolution,
+or density. When creating the AVD from the AVD Manager, specify the Resolution,
instead of selecting a Built-in Skin.</p>
<p>If you are launching your AVD from the command line, you can specify the scale for
diff --git a/docs/html/guide/topics/location/index.jd b/docs/html/guide/topics/location/index.jd
index 5f98902..8a2e9cd 100644
--- a/docs/html/guide/topics/location/index.jd
+++ b/docs/html/guide/topics/location/index.jd
@@ -98,7 +98,7 @@ Google APIs add-on, visit</p>
href="http://code.google.com/android/add-ons/google-apis">http://code.google.com/android/add-ons/google-apis</a></p>
<p>For your convenience, the Google APIs add-on is also available as a downloadable component from
-the Android SDK and AVD Manager (see <a href="{@docRoot}sdk/adding-components.html">Adding SDK
+the Android SDK Manager (see <a href="{@docRoot}sdk/adding-components.html">Adding SDK
Components</a>).</p>
<p class="note"><strong>Note:</strong> In order to display Google Maps data in a
diff --git a/docs/html/guide/topics/manifest/uses-feature-element.jd b/docs/html/guide/topics/manifest/uses-feature-element.jd
index 9175566..5f0a501 100644
--- a/docs/html/guide/topics/manifest/uses-feature-element.jd
+++ b/docs/html/guide/topics/manifest/uses-feature-element.jd
@@ -501,7 +501,7 @@ If you are using SDK Tools r8 or higher, you can find <code>aapt</code> in the
<p class="note"><strong>Note:</strong> You must use the version of
<code>aapt</code> that is provided for the latest Platform-Tools component available. If
you do not have the latest Platform-Tools component, download it using the <a
-href="{@docRoot}sdk/adding-components.html">Android SDK and AVD Manager</a>.
+href="{@docRoot}sdk/adding-components.html">Android SDK Manager</a>.
</p></li>
<li>Run <code>aapt</code> using this syntax: </li>
</ol>
diff --git a/docs/html/guide/tutorials/views/hello-mapview.jd b/docs/html/guide/tutorials/views/hello-mapview.jd
index 458db4f..5217b6b 100644
--- a/docs/html/guide/tutorials/views/hello-mapview.jd
+++ b/docs/html/guide/tutorials/views/hello-mapview.jd
@@ -14,8 +14,8 @@ location:</p>
href="http://code.google.com/android/add-ons/google-apis">http://code.google.com/android/add-ons/google-apis</a></p>
<p>The Google APIs add-on requires Android 1.5 SDK or later release. After
-installing the add-on in your SDK, set your project properties to use the build
-target called "Google APIs Add-on". See the instructions for setting a build
+installing the add-on in your SDK, set your project properties to use a <strong>Google
+APIs</strong> build target. See the instructions for setting a build
target in <a href="{@docRoot}guide/developing/eclipse-adt.html">Developing in
Eclipse with ADT</a> or <a
href="{@docRoot}guide/developing/other-ide.html">Developing in Other IDEs</a>,
diff --git a/docs/html/images/efficient-downloads/DDMS.png b/docs/html/images/efficient-downloads/DDMS.png
new file mode 100644
index 0000000..e7b0b94
--- /dev/null
+++ b/docs/html/images/efficient-downloads/DDMS.png
Binary files differ
diff --git a/docs/html/images/efficient-downloads/graphs.png b/docs/html/images/efficient-downloads/graphs.png
new file mode 100644
index 0000000..65faeaa
--- /dev/null
+++ b/docs/html/images/efficient-downloads/graphs.png
Binary files differ
diff --git a/docs/html/images/efficient-downloads/mobile_radio_state_machine.png b/docs/html/images/efficient-downloads/mobile_radio_state_machine.png
new file mode 100644
index 0000000..e06608b
--- /dev/null
+++ b/docs/html/images/efficient-downloads/mobile_radio_state_machine.png
Binary files differ
diff --git a/docs/html/images/screens_support/avds-config.png b/docs/html/images/screens_support/avds-config.png
index 3af1c39..c7d534c 100644
--- a/docs/html/images/screens_support/avds-config.png
+++ b/docs/html/images/screens_support/avds-config.png
Binary files differ
diff --git a/docs/html/index.jd b/docs/html/index.jd
index 787a655..d3203bb 100644
--- a/docs/html/index.jd
+++ b/docs/html/index.jd
@@ -53,28 +53,32 @@ Android apps!</p>
<p><a href="{@docRoot}sdk/index.html">Learn more &raquo;</a></p>
</td>
</tr>
+
+
<tr>
<td colspan="2"><div class="seperator">&nbsp;</div></td>
</tr>
<tr>
- <td class="imageCell"><a href="http://play.google.com/apps/publish"><img src="{@docRoot}assets/images/icon_play.png" style="padding:0" /></a></td>
+ <td class="imageCell"><a href="{@docRoot}design/index.html"><img src="{@docRoot}assets/images/icon_design.png" style="padding:5px" /></a></td>
<td>
- <h2 class="green">Publish</h2>
- <p>Google Play is an open service that lets you distribute your apps to devices.</p>
- <p><a href="http://play.google.com/apps/publish">Learn more &raquo;</a></p>
+ <h2 class="green">Design</h2>
+ <p>Learn about principles, building blocks, and patterns for creating world-class Android user interfaces.</p>
+ <p><a href="{@docRoot}design/index.html">Learn more &raquo;</a></p>
</td>
</tr>
+
<tr>
<td colspan="2"><div class="seperator">&nbsp;</div></td>
</tr>
<tr>
- <td class="imageCell"><a href="http://source.android.com"><img src="{@docRoot}assets/images/icon_contribute.jpg" style="padding:0" /></a></td>
+ <td class="imageCell"><a href="http://play.google.com/apps/publish"><img src="{@docRoot}assets/images/icon_play.png" style="padding:0" /></a></td>
<td>
- <h2 class="green">Contribute</h2>
- <p>Android Open Source Project gives you access to the entire platform source.</p>
- <p><a href="http://source.android.com">Learn more &raquo;</a></p>
+ <h2 class="green">Publish</h2>
+ <p>Google Play is an open service that lets you distribute your apps to devices.</p>
+ <p><a href="http://play.google.com/apps/publish">Learn more &raquo;</a></p>
</td>
</tr>
+
<tr>
<td colspan="2"><div class="seperator">&nbsp;</div></td>
</tr>
diff --git a/docs/html/offline.jd b/docs/html/offline.jd
index 5f8e37ce..edd8eb0 100644
--- a/docs/html/offline.jd
+++ b/docs/html/offline.jd
@@ -40,7 +40,7 @@ tools</li>
<p>Follow the guide to <a href="{@docRoot}sdk/installing.html">Installing the Android SDK</a>, which
will help you setup your development environment.</p>
-<h3>If you've installed new SDK components using the SDK and AVD Manager</h3>
+<h3>If you've installed new SDK components using the Android SDK Manager</h3>
<p>There's no additional setup.</p>
diff --git a/docs/html/resources/articles/creating-input-method.jd b/docs/html/resources/articles/creating-input-method.jd
index e4b77f4..84c2704 100644
--- a/docs/html/resources/articles/creating-input-method.jd
+++ b/docs/html/resources/articles/creating-input-method.jd
@@ -3,248 +3,526 @@ parent.title=Articles
parent.link=../browser.html?tag=article
@jd:body
-
<div id="qv-wrapper">
<div id="qv">
-
- <h2>See also</h2>
- <ol>
- <li><a href="{@docRoot}resources/articles/on-screen-inputs.html">Onscreen Input Methods</a></li>
- <li><a href="{@docRoot}resources/samples/SoftKeyboard/index.html">Soft Keyboard sample</a></li>
- </ol>
-
+<h2>See also</h2>
+<ol>
+ <li>
+ <a href="{@docRoot}resources/articles/on-screen-inputs.html">Onscreen Input Methods</a>
+ </li>
+ <li>
+ <a href="{@docRoot}resources/samples/SoftKeyboard/index.html">Soft Keyboard sample</a>
+ </li>
+</ol>
</div>
</div>
-
-
-<p>To create an input method (IME) for entering text into text fields
-and other Views, you need to extend the {@link android.inputmethodservice.InputMethodService}.
-class. This class provides much of the basic implementation for an input
-method, in terms of managing the state and visibility of the input method and
-communicating with the currently visible activity.</p>
-
-<p>A good starting point would be the SoftKeyboard sample code provided as part
-of the SDK. You can modify the sample code to start building your own input
-method.</p>
-
-<p>An input method is packaged like any other application or service. In the
-<code>AndroidManifest.xml</code> file, you declare the input method as a
-service, with the appropriate intent filter and any associated meta data:</p>
-
-<pre>&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.example.fastinput"&gt;
-
- &lt;application android:label="@string/app_label"&gt;<br>
- &lt;!-- Declares the input method service --&gt;
- &lt;service android:name="FastInputIME"
- android:label="@string/fast_input_label"
- android:permission="android.permission.BIND_INPUT_METHOD"&gt;
- &lt;intent-filter&gt;
- &lt;action android:name="android.view.InputMethod" /&gt;
- &lt;/intent-filter&gt;
- &lt;meta-data android:name="android.view.im" android:resource="@xml/method" /&gt;
- &lt;/service&gt;
-
- &lt;!-- Optional activities. A good idea to have some user settings. --&gt;
- &lt;activity android:name="FastInputIMESettings" android:label="@string/fast_input_settings"&gt;
- &lt;intent-filter&gt;
- &lt;action android:name="android.intent.action.MAIN"/&gt;
- &lt;/intent-filter&gt;
- &lt;/activity&gt;
- &lt;/application&gt;
-&lt;/manifest&gt;</pre>
-
-<p>If your input method allows the user to tweak some settings, you should
-provide a settings activity that can be launched from the Settings application.
-This is optional and you may choose to provide all user settings directly in
-your IME's UI.</p>
-
-<p>The typical life-cycle of an <code>InputMethodService</code> looks like
-this:</p>
-
-<p><img src="images/ime_003.png" style="border: medium none ; width: 374px; height: 871px;"></p>
-
-<h3>Visual Elements</h3>
-
-<p>There are two main visual elements for an input method—the input view and the
-candidates view. You don't have to follow this style though, if one of them is
-not relevant to your input method experience.</p>
-
-<h4>Input View</h4>
-
-<p>This is where the user can input text either in the form of keypresses,
-handwriting or other gestures. When the input method is displayed for the first
-time, <code>InputMethodService.onCreateInputView()</code> will be called. Create
-and return the view hierarchy that you would like to display in the input method
-window.</p>
-
-<h4>Candidates View</h4>
-
-<p>This is where potential word corrections or completions are presented to the
-user for selection. Again, this may or may not be relevant to your input method
-and you can return <code>null</code> from calls to
-<code>InputMethodService.onCreateCandidatesView()</code>, which is the default
-behavior.</p>
-
-<h3>Designing for the different Input Types</h3>
-
-<p>An application's text fields can have different input types specified on
-them, such as free form text, numeric, URL, email address and search. When you
-implement a new input method, you need to be aware of the different input types.
-Input methods are not automatically switched for different input types and so
-you need to support all types in your IME. However, the IME is not responsible
-for validating the input sent to the application. That's the responsibility of
-the application.</p>
-
-<p>For example, the LatinIME provided with the Android platform provides
-different layouts for text and phone number entry:</p>
-
-<p><img style="margin: 0pt 10px 0pt 0pt; width: 319px; height: 198px;" src="images/ime_002.png"><img style="width: 320px; height: 199px;" src="images/ime.png"></p>
-
-<p><code>InputMethodService.onStartInputView()</code> is called with an<code>
-EditorInfo</code> object that contains details about the input type and other
-attributes of the application's text field.</p><p>(<code>EditorInfo.inputType
-&amp; EditorInfo.TYPE_CLASS_MASK</code>) can be one of many different values,
-including:</p>
-
+<p>
+ An input method editor (IME) is a user control that enables users to enter text. Android
+ provides an extensible input method framework that allows applications to provide users
+ alternative input methods, such as on-screen keyboards or even speech input. Once installed,
+ users can select which IME they want to use from the system settings and use it across the
+ entire system; only one IME may be enabled at a time.
+</p>
+<p>
+ To add an IME to the Android system, you create an Android application
+ containing a class that extends {@link android.inputmethodservice.InputMethodService}. In
+ addition, you usually create a "settings" activity that passes options to the IME
+ service. You can also define a settings UI that's displayed as part of the system settings.
+</p>
+<p>This article covers the following:</p>
<ul>
-<li><code>TYPE_CLASS_NUMBER</code></li>
-<li><code>TYPE_CLASS_DATETIME</code></li>
-<li><code>TYPE_CLASS_PHONE</code></li>
-<li><code>TYPE_CLASS_TEXT</code></li>
+ <li>The IME lifecycle.</li>
+ <li>Declaring IME components in the application manifest.</li>
+ <li>The IME API.</li>
+ <li>Designing an IME UI.</li>
+ <li>Sending text from an IME to an application.</li>
+ <li>Working with IME subtypes.</li>
</ul>
+<p>
+ If you haven't worked with IMEs before, you should read the introductory article
+ <a href="{@docRoot}resources/articles/on-screen-inputs.html">Onscreen Input Methods</a> first.
+ Also, the Soft Keyboard sample app included in the SDK contains sample code that you can modify
+ to start building your own IME.
+</p>
+<h2 id="InputMethodLifecycle">The IME Lifecycle</h2>
+<p>
+ The following diagram describes the life cycle of an IME:
+</p>
+<img src="{@docRoot}resources/articles/images/inputmethod_lifecycle_image.png" alt="" height="845"
+ id="figure1" />
+<p class="img-caption">
+ <strong>Figure 1.</strong> The life cycle of an IME.
+</p>
+<p>
+ The following sections describe how to implement the UI and code associated with an IME that
+ follows this lifecycle.
+</p>
+<h2 id="DefiningIME">Declaring IME Components in the Manifest</h2>
+<p>
+ In the Android system, an IME is an Android application that contains a special IME service.
+ The application's manifest file must declare the service, request the necessary permissions,
+ provide an intent filter that matches the action <code>action.view.InputMethod</code>, and
+ provide metadata that defines characteristics of the IME. In addition, to provide a settings
+ interface that allows the user to modify the behavior of the IME, you can define a "settings"
+ activity that can be launched from System Settings.
+</p>
+<p>
+ The following snippet declares IME service. It requests the permission {@link
+ android.Manifest.permission#BIND_INPUT_METHOD} to allow the service to connect the IME to
+ the system, sets up an intent filter that matches the action
+ <code>android.view.InputMethod</code>, and defines metadata for the IME:
+</p>
+<pre>
+&lt;!-- Declares the input method service --&gt;
+ &lt;service android:name="FastInputIME"
+ android:label="&#64;string/fast_input_label"
+ android:permission="android.permission.BIND_INPUT_METHOD"&gt;
+ &lt;intent-filter&gt;
+ &lt;action android:name="android.view.InputMethod" /&gt;
+ &lt;/intent-filter&gt;
+ &lt;meta-data android:name="android.view.im" android:resource="&#64;xml/method" /&gt;
+ &lt;/service&gt;
+</pre>
+<p>
+ This next snippet declares the settings activity for the IME. It has an intent filter for
+ {@link android.content.Intent#ACTION_MAIN} that indicates this activity is the main entry point
+ for the IME application:</p>
+<pre>
+ &lt;!-- Optional: an activity for controlling the IME settings --&gt;
+ &lt;activity android:name="FastInputIMESettings"
+ android:label="&#64;string/fast_input_settings"&gt;
+ &lt;intent-filter&gt;
+ &lt;action android:name="android.intent.action.MAIN"/&gt;
+ &lt;/intent-filter&gt;
+ &lt;/activity&gt;
+</pre>
+<p>
+ You can also provide access to the IME's settings directly from its UI.
+</p>
+<h2 id="IMEAPI">The Input Method API</h2>
+<p>
+ Classes specific to IMEs are found in the {@link android.inputmethodservice} and {@link
+ android.view.inputmethod} packages. The {@link android.view.KeyEvent} class is important for
+ handling keyboard characters.
+</p>
+<p>
+ The central part of an IME is a service component, a class that extends
+ {@link android.inputmethodservice.InputMethodService}. In addition to implementing the
+ normal service lifecycle, this class has callbacks for providing your IME's UI, handling user
+ input, and delivering text to the field that currently has focus. By default, the
+ {@link android.inputmethodservice.InputMethodService} class provides most of the implementation
+ for managing the state and visibility of the IME and communicating with the current
+ input field.
+</p>
+<p>
+ The following classes are also important:
+</p>
+<dl>
+ <dt>{@link android.view.inputmethod.BaseInputConnection}</dt>
+ <dd>
+ Defines the communication channel from an {@link android.view.inputmethod.InputMethod}
+ back to the application that is receiving its input. You use it to read text around the
+ cursor, commit text to the text box, and send raw key events to the application.
+ Applications should extend this class rather than implementing the base interface
+ {@link android.view.inputmethod.InputConnection}.
+ </dd>
+ <dt>{@link android.inputmethodservice.KeyboardView}</dt>
+ <dd>
+ An extension of {@link android.view.View} that renders a keyboard and responds to user
+ input events. The keyboard layout is specified by an instance of
+ {@link android.inputmethodservice.Keyboard}, which you can define in an XML file.
+ </dd>
+</dl>
+<h2 id="IMEUI">Designing the Input Method UI</h2>
+<p>
+ There are two main visual elements for an IME: the <strong>input</strong> view and the
+ <strong>candidates</strong> view. You only have to implement the elements that are relevant to
+ the input method you're designing.
+</p>
+<h3 id="InputView">Input view</h3>
+<p>
+ The input view is the UI where the user inputs text, in the form of keyclicks, handwriting or
+ gestures. When the iIME is displayed for the first time, the system calls the
+ {@link android.inputmethodservice.InputMethodService#onCreateInputView()} callback. In your
+ implementation of this method, you create the layout you want to display in the IME
+ window and return the layout to the system. This snippet is an example of implementing the
+ {@link android.inputmethodservice.InputMethodService#onCreateInputView()} method:
+<pre>
+ &#64;Override
+ public View onCreateInputView() {
+ MyKeyboardView inputView =
+ (MyKeyboardView) getLayoutInflater().inflate( R.layout.input, null);
+
+ inputView.setOnKeyboardActionListener(this); inputView.setKeyboard(mLatinKeyboard);
+
+ return mInputView;
+ }
+</pre>
+<p>
+ In this example, {@code MyKeyboardView} is an instance of a custom implementation of
+ {@link android.inputmethodservice.KeyboardView} that renders a
+ {@link android.inputmethodservice.Keyboard}. If you’re building a traditional QWERTY keyboard,
+ see the <a href=”{@docRoot}resources/samples/SoftKeyboard/index.html”>Soft Keyboard</a> sample
+ app for an example of how to extend the {@link android.inputmethodservice.KeyboardView} class.
+</p>
+<h3 id="CandidateView">Candidates view</h3>
+<p>
+ The candidates view is the UI where the IME displays potential word corrections or
+ suggestions for the user to select. In the IME lifecycle, the system calls
+ {@link android.inputmethodservice.InputMethodService#onCreateCandidatesView()} when it's ready
+ to display the candidate view. In your implementation of this method, return a layout that shows
+ word suggestions, or return null if you don’t want to show anything (a null response is the
+ default behavior, so you don’t have to implement this if you don’t provide suggestions).</p>
+<p>
+ For an example implementation that provides user suggestions, see the
+ <a href=”{@docRoot}resources/samples/SoftKeyboard/index.html”>Soft Keyboard</a> sample app.
+</p>
+<h3 id="DesignConsiderations">UI design considerations</h3>
+<p>
+ This section describes some specific UI design considerations for IMEs.
+</p>
+<h4>Handling multiple screen sizes</h4>
+<p>
+ The UI for your IME must be able to scale for different screen sizes, and it also
+ must handle both landscape and portrait orientations. In non-fullscreen IME mode, leave
+ sufficient space for the application to show the text field and any associated context, so that
+ no more than half the screen is occupied by the IME. In fullscreen IME mode this is not an
+ issue.
+</p>
+<h4>Handling different input types</h4>
+<p>
+ Android text fields allow you to set a specific input type, such as free form text, numbers,
+ URLs, email addresses, and search strings. When you implement a new IME, you need to
+ detect the input type of each field and provide the appropriate interface for it. However, you
+ don't have to set up your IME to check that the user entered text that's valid for the
+ input type; that's the responsibility of the application that owns the text field.
+</p>
+<p>
+ For example, here are screenshots of the interfaces that the Latin IME provided with the
+ Android platform provides for text and phone number inputs:
+</p>
+<img src="{@docRoot}resources/articles/images/inputmethod_text_type_screenshot.png" alt=""
+ height="142" id="figure2" />
+<img src="{@docRoot}resources/articles/images/inputmethod_numeric_type_screenshot.png" alt=""
+ height="120" id="figure2a" />
+<p class="img-caption">
+ <strong>Figure 2.</strong> Latin IME input types.
+</p>
+<p>
+ When an input field receives focus and your IME starts, the system calls
+ {@link android.inputmethodservice.InputMethodService#onStartInputView(EditorInfo, boolean)
+ onStartInputView()}, passing in an {@link android.view.inputmethod.EditorInfo} object that
+ contains details about the input type and other attributes of the text field. In this object,
+ the {@link android.view.inputmethod.EditorInfo#inputType} field contains the text field's input
+ type.
+</p>
+<p>
+ The {@link android.view.inputmethod.EditorInfo#inputType} field is an <code>int</code>
+ that contains bit patterns for various input type settings. To test it for the text field's
+ input type, mask it with the constant {@link android.text.InputType#TYPE_MASK_CLASS}, like
+ this:
+</p>
+<pre>
+inputType &amp; InputType.TYPE_MASK_CLASS
+</pre>
+<p>
+The input type bit pattern can have one of several values, including:
+</p>
+<dl>
+ <dt>{@link android.text.InputType#TYPE_CLASS_NUMBER}</dt>
+ <dd>
+ A text field for entering numbers. As illustrated in the previous screen shot, the
+ Latin IME displays a number pad for fields of this type.
+ </dd>
+ <dt>{@link android.text.InputType#TYPE_CLASS_DATETIME}</dt>
+ <dd>
+ A text field for entering a date and time.
+ </dd>
+ <dt>{@link android.text.InputType#TYPE_CLASS_PHONE}</dt>
+ <dd>
+ A text field for entering telephone numbers.
+ </dd>
+ <dt>{@link android.text.InputType#TYPE_CLASS_TEXT}</dt>
+ <dd>
+ A text field for entering all supported characters.
+ </dd>
+</dl>
+<p>
+ These constants are described in more detail in the reference documentation for
+ {@link android.text.InputType}.
+</p>
+<p>
+ The {@link android.view.inputmethod.EditorInfo#inputType} field can contain other bits that
+ indicate a variant of the text field type, such as:
+</p>
+<dl>
+ <dt>{@link android.text.InputType#TYPE_TEXT_VARIATION_PASSWORD}</dt>
+ <dd>
+ A variant of {@link android.text.InputType#TYPE_CLASS_TEXT} for entering passwords. The
+ input method will display dingbats instead of the actual text.
+ </dd>
+ <dt>{@link android.text.InputType#TYPE_TEXT_VARIATION_URI}</dt>
+ <dd>
+ A variant of {@link android.text.InputType#TYPE_CLASS_TEXT} for entering web URLs and
+ other Uniform Resource Identifiers (URIs).
+ </dd>
+ <dt>{@link android.text.InputType#TYPE_TEXT_FLAG_AUTO_COMPLETE}</dt>
+ <dd>
+ A variant of {@link android.text.InputType#TYPE_CLASS_TEXT} for entering text that the
+ application "auto-completes" from a dictionary, search, or other facility.
+ </dd>
+</dl>
+<p>
+ Remember to mask {@link android.view.inputmethod.EditorInfo#inputType} with the appropriate
+ constant when you test for these variants. The available mask constants are listed in the
+ reference documentation for {@link android.text.InputType}.
+</p>
+<p class="caution">
+ <strong>Caution:</strong> In your own IME, make sure you handle text correctly when you send it
+ to a password field. Hide the password in your UI both in the input view and in the candidates
+ view. Also remember that you shouldn't store passwords on a device. To learn more, see the <a
+ href="{@docRoot}guide/practices/security.html">Designing for Security</a> guide.
+</p>
+<h2 id="SendText">Sending Text to the Application</h2>
+<p>
+ As the user inputs text with your IME, you can send text to the application by
+ sending individual key events or by editing the text around the cursor in the application's text
+ field. In either case, you use an instance of {@link android.view.inputmethod.InputConnection}
+ to deliver the text. To get this instance, call
+ {@link android.inputmethodservice.InputMethodService#getCurrentInputConnection
+ InputMethodService.getCurrentInputConnection()}.
+</p>
+<h3 id="EditingCursor">Editing the text around the cursor</h3>
+<p>
+ When you're handling the editing of existing text in a text field, some of the more useful
+ methods in {@link android.view.inputmethod.BaseInputConnection} are:
+</p>
+<dl>
+ <dt>
+ {@link android.view.inputmethod.BaseInputConnection#getTextBeforeCursor(int, int)
+ getTextBeforeCursor()}</dt>
+ <dd>
+ Returns a {@link java.lang.CharSequence} containing the number of requested characters
+ before the current cursor position.
+ </dd>
+ <dt>
+ {@link android.view.inputmethod.BaseInputConnection#getTextAfterCursor(int, int)
+ getTextAfterCursor()}
+ </dt>
+ <dd>
+ Returns a {@link java.lang.CharSequence} containing the number of requested characters
+ following the current cursor position.
+ </dd>
+ <dt>
+ {@link android.view.inputmethod.BaseInputConnection#deleteSurroundingText(int, int)
+ deleteSurroundingText()}
+ </dt>
+ <dd>
+ Deletes the specified number of characters before and following the current cursor
+ position.
+ </dd>
+ <dt>
+ {@link android.view.inputmethod.BaseInputConnection#commitText(CharSequence, int)
+ commitText()}
+ </dt>
+ <dd>
+ Commit a {@link java.lang.CharSequence} to the text field and set a new cursor
+ position.
+ </dd>
+</dl>
+<p>
+ For example, the following snippet shows how to replace the text "Fell" to the left of the
+ with the text "Hello!":
+</p>
+<pre>
+ InputConnection ic = getCurrentInputConnection();
+
+ ic.deleteSurroundingText(4, 0);
+
+ ic.commitText("Hello", 1);
+
+ ic.commitText("!", 1);
+</pre>
+<h3 id="ComposeThenCommit">Composing text before committing</h3>
+<p>
+ If your IME does text prediction or requires multiple steps to compose a glyph or
+ word, you can show the progress in the text field until the user commits the word, and then you
+ can replace the partial composition with the completed text. You may give special treatment to
+ the text by adding a "span" to it when you pass it to InputConnection#setComposingText().
+</p>
+<p>
+ The following snippet shows how to show progress in a text field:
+</p>
+<pre>
+ InputConnection ic = getCurrentInputConnection();
+
+ ic.setComposingText("Composi", 1);
+...
-<p>See <code>android.text.InputType</code> for more details.</p>
-
-<p><code>EditorInfo.inputType</code> can contain other masked bits that
-indicate the class variation and other flags. For example,
-<code>TYPE_TEXT_VARIATION_PASSWORD</code> or <code>TYPE_TEXT_VARIATION_URI</code>
-or <code>TYPE_TEXT_FLAG_AUTO_COMPLETE</code>.</p>
-
-<h4>Password fields</h4>
-
-<p>Pay
-specific attention when sending text to password fields. Make sure that
-the password is not visible within your UI &mdash; neither in the input
-view or the candidates view. Also, do not save the password anywhere without
-explicitly informing the user.</p>
-
-<h3>Landscape vs. portrait</h3>
-
-<p>The UI needs to be able to scale between landscape and portrait orientations.
-In non-fullscreen IME mode, leave sufficient space for the application to show
-the text field and any associated context. Preferably, no more than half the
-screen should be occupied by the IME. In fullscreen IME mode this is not an
-issue.</p>
-
-<h3>Sending text to the application</h3>
-
-<p>There are two ways to send text to the application. You can either send
-individual key events or you can edit the text around the cursor in the
-application's text field.</p>
-
-<p>To send a key event, you can simply construct KeyEvent objects and call
-<code>InputConnection.sendKeyEvent()</code>. Here are some examples:</p>
-
-<pre>InputConnection ic = getCurrentInputConnection();
-long eventTime = SystemClock.uptimeMillis();
-ic.sendKeyEvent(new KeyEvent(eventTime, eventTime,
- KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, 0, 0,
- KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
-ic.sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
- KeyEvent.ACTION_UP, keyEventCode, 0, 0, 0, 0,
- KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));</pre>
-
-<p>Or use the convenience method:</p>
-
-<pre>InputMethodService.sendDownUpKeyEvents(keyEventCode);</pre>
-
-<p class="note"><strong>Note</strong>:
-It is recommended to use the above method for certain fields such as
-phone number fields because of filters that may be applied to the text
-after each key press. Return key and delete key should also be sent as
-raw key events for certain input types, as applications may be watching
-for specific key events in order to perform an action.</p>
+ ic.setComposingText("Composin", 1);
-<p>When editing text in a text field, some of the more useful methods on
-<code>android.view.inputmethod.InputConnection</code> are:</p>
+...
+ ic.commitText("Composing ", 1);
+</pre>
+<p>
+ The following screenshots show how this appears to the user:
+</p>
+<img src="{@docRoot}resources/articles/images/inputmethod_composing_text_1.png" alt="" height="54"
+ id="figure3a" />
+<img src="{@docRoot}resources/articles/images/inputmethod_composing_text_2.png" alt="" height="53"
+ id="figure3b" />
+<img src="{@docRoot}resources/articles/images/inputmethod_composing_text_3.png" alt="" height="31"
+ id="figure3c" />
+<p class="img-caption">
+ <strong>Figure 3.</strong> Composing text before committing.
+</p>
+<h3 id="HardwareKeyEvents">Intercepting hardware key events</h3>
+<p>
+ Even though the input method window doesn't have explicit focus, it receives hardware key
+ events first and can choose to consume them or forward them along to the application. For
+ example, you may want to consume the directional keys to navigate within your UI for candidate
+ selection during composition. You may also want to trap the back key to dismiss any popups
+ originating from the input method window.</p>
+<p>
+ To intercept hardware keys, override
+ {@link android.inputmethodservice.InputMethodService#onKeyDown(int, KeyEvent) onKeyDown()}
+ and {@link android.inputmethodservice.InputMethodService#onKeyUp(int, KeyEvent) onKeyUp()}.
+ See the <a href=”{@docRoot}resources/samples/SoftKeyboard/index.html”>Soft Keyboard</a> sample
+ app for an example.
+</p>
+<p>
+ Remember to call the <code>super()</code> method for keys you don't want to handle yourself.
+</p>
+<h2 id="IMESubTypes">Creating an IME Subtype</h2>
+<p>
+ Subtypes allow the IME to expose multiple input modes and languages supported by an IME. A
+ subtype can represent:
+</p>
<ul>
-<li><code>getTextBeforeCursor()</code></li>
-<li><code>getTextAfterCursor()</code></li>
-<li><code>deleteSurroundingText()</code></li>
-<li><code>commitText()</code></li>
+ <li>A locale such as en_US or fr_FR</li>
+ <li>An input mode such as voice, keyboard, or handwriting</li>
+ <li>
+ Other input styles, forms, or properties specific to the IME, such as 10-key or qwerty
+ keyboard layouts.
+ </li>
</ul>
-
-<p>For example, let's say the text "Fell" is to the left of the cursor
-and you want to replace it with "Hello!":</p>
-
-<pre>InputConnection ic = getCurrentInputConnection();
-ic.deleteSurroundingText(4, 0);
-ic.commitText("Hello", 1);
-ic.commitText("!", 1);</pre>
-
-<h4>Composing text before committing</h4>
-
-<p>If your input method does some kind of text prediction or requires multiple
-steps to compose a word or glyph, you can show the progress in the text field
-until the user commits the word and then you can replace the partial composition
-with the completed text. The text that is being composed will be highlighted in
-the text field in some fashion, such as an underline.</p>
-
-<pre>InputConnection ic = getCurrentInputConnection();
-ic.setComposingText("Composi", 1);
-...
-ic.setComposingText("Composin", 1);
-...
-ic.commitText("Composing ", 1);</pre>
-
-<p><img style="width: 320px; height: 98px; margin-bottom: 10px;" src="images/ime_006.png">
-<img style="width: 320px; height: 97px; margin-bottom: 10px;" src="images/ime_005.png">
-<img style="width: 320px; height: 97px;" src="images/ime_004.png"></p>
-
-<h3>Intercepting hard key events</h3>
-
-<p>Even though the input method window doesn't have explicit focus, it receives
-hard key events first and can choose to consume them or forward them along to
-the application. For instance, you may want to consume the directional keys to
-navigate within your UI for candidate selection during composition. Or you may
-want to trap the back key to dismiss any popups originating from the input
-method window. To intercept hard keys, override
-<code>InputMethodService.onKeyDown()</code> and
-<code>InputMethodService.onKeyUp().</code> Remember to call
-<code>super.onKey</code>* if you don't want to consume a certain key
-yourself.</p>
-
-<h3>Other considerations</h3>
-
+<p>
+ Basically, the mode can be any text such as "keyboard", "voice", and so forth.
+</p>
+<p>A subtype can also expose a combination of these.</p>
+<p>
+ Subtype information is used for an IME switcher dialog that's available from the notification
+ bar and also for IME settings. The information also allows the framework to bring up a
+ specific subtype of an IME directly. When you build an IME, use the subtype facility, because
+ it helps the user identify and switch between different IME languages and modes.
+</p>
+<p>
+ You define subtypes in one of the input method's XML resource files, using the
+ <code>&lt;subtype&gt;</code> element. The following snippet defines an IME with two
+ subtypes: a keyboard subtype for the US English locale, and another keyboard subtype for the
+ French language locale for France:
+</p>
+<pre>
+&lt;input-method xmlns:android="http://schemas.android.com/apk/res/android"
+ android:settingsActivity="com.example.softkeyboard.Settings"
+ android:icon="&#64;drawable/ime_icon"
+ &lt;subtype android:name="&#64;string/display_name_english_keyboard_ime"
+ android:icon="&#64;drawable/subtype_icon_english_keyboard_ime"
+ android:imeSubtypeLanguage="en_US"
+ android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="somePrivateOption=true"
+ /&gt;
+ &lt;subtype android:name="&#64;string/display_name_french_keyboard_ime"
+ android:icon="&#64;drawable/subtype_icon_french_keyboard_ime"
+ android:imeSubtypeLanguage="fr_FR"
+ android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="foobar=30,someInternalOption=false"
+ /&gt;
+ &lt;subtype android:name="&#64;string/display_name_german_keyboard_ime"
+ ...
+ /&gt;
+/&gt;
+</pre>
+<p>
+ To ensure that your subtypes are labeled correctly in the UI, use %s to get a subtype label
+ that is the same as the subtype’s locale label. This is demonstrated in the next two snippets.
+ The first snippet shows part of the input method's XML file:
+</p>
+<pre>
+ &lt;subtype
+ android:label="&#64;string/label_subtype_generic"
+ android:imeSubtypeLocale="en_US"
+ android:icon="&#64;drawable/icon_en_us"
+ android:imeSubtypeMode="keyboard" /&gt;
+</pre>
+<p>
+ The next snippet is part of the IME's <code>strings.xml</code> file. The string
+ resource <code>label_subtype_generic</code>, which is used by the input method UI definition to
+ set the subtype's label, is defined as:
+</p>
+<pre>
+&lt;string name="label_subtype_generic"&gt;%s&lt;/string&gt;
+</pre>
+<p>
+ This sets the subtype’s display name to “English (United States)” in any English language
+ locale, or to the appropriate localization in other locales.
+</p>
+<h3 id="SubtypeProcessing">Choosing IME subtypes from the notification bar</h3>
+<p>
+ The Android system manages all subtypes exposed by all IMEs. IME subtypes are
+ treated as modes of the IME they belong to. In the notification bar, a user can select an
+ available subtype for the currently-set IME, as shown in the following screenshot:
+</p>
+<img src="{@docRoot}resources/articles/images/inputmethod_subtype_notification.png" alt=""
+ height="85" id="figure4" />
+<p class="img-caption">
+ <strong>Figure 4.</strong> Choosing an IME subtype from the notification bar.
+</p>
+<img src="{@docRoot}resources/articles/images/inputmethod_subtype_preferences.png" alt=""
+ height="165" id="figure5" />
+<p class="img-caption">
+ <strong>Figure 5.</strong> Setting subtype preferences in System Settings.
+</p>
+<h3 id="SubtypeSettings">Choosing IME subtypes from System Settings</h3>
+<p>
+ A user can control how subtypes are used in the “Language &amp; input” settings panel in the
+ System Settings area. In the Soft Keyboard sample, the file
+ <code>InputMethodSettingsFragment.java</code> contains an implementation that
+ facilitates a subtype enabler in the IME settings. Please refer to the SoftKeyboard sample in
+ the Android SDK for more information about how to support Input Method Subtypes in your IME.
+</p>
+<img src="{@docRoot}resources/articles/images/inputmethod_subtype_settings.png" alt=""
+ height="210" id="figure6" />
+<p class="img-caption">
+ <strong>Figure 6.</strong> Choosing a language for the IME.
+</p>
+<h2 id="GeneralDesign">General IME Considerations</h2>
+<p>
+ Here are some other things to consider as you're implementing your IME:
+</p>
<ul>
-<li>Provide a way for the user to easily bring up any associated settings
-directly from the input method UI</li>
-<li>Provide
-a way for the user to switch to a different input method (multiple
-input methods may be installed) directly from the input method UI.</li>
-<li>Bring
-up the UI quickly - preload or lazy-load any large resources so that
-the user sees the input method quickly on tapping on a text field. And
-cache any resources and views for subsequent invocations of the input
-method.</li>
-<li>On the flip side, any large memory allocations should
-be released soon after the input method window is hidden so that
-applications can have sufficient memory to run. Consider using a
-delayed message to release resources if the input method is in a hidden
-state for a few seconds.</li>
-<li>Make sure that most common characters
-can be entered using the input method, as users may use punctuation in
-passwords or user names and they shouldn't be stuck in a situation
-where they can't enter a certain character in order to gain access into
-a password-locked device.</li>
+<li>
+ Provide a way for users to set options directly from the IME's UI.
+</li>
+<li>
+ Because multiple IMEs may be installed on the device, provide a way for the user to switch to a
+ different IME directly from the input method UI.
+</li>
+<li>
+ Bring up the IME's UI quickly. Preload or load on demand any large resources so that users
+ see the IME as soon as they tap on a text field. Cache resources and views for subsequent
+ invocations of the input method.
+</li>
+<li>
+ Conversely, you should release large memory allocations soon after the input method window is
+ hidden, so that applications can have sufficient memory to run. Consider using a delayed message
+ to release resources if the IME is in a hidden state for a few seconds.
+</li>
+<li>
+ Make sure that users can enter as many characters as possible for the language or locale
+ associated with the IME. Remember that users may use punctuation in passwords or user
+ names, so your IME has to provide many different characters to allow users to enter a
+ password and get access to the device.
+</li>
</ul>
-
-<h3>Samples</h3>
-
-<p>For a real world example, with support for multiple input types and text
-prediction, see the <a id="ccpb"
-href="http://android.git.kernel.org/?p=platform/packages/inputmethods/LatinIME.
-git;a=tree" title="LatinIME source code online">LatinIME source code</a>. The
-Android SDK also includes a SoftKeyboard sample as well.</p>
diff --git a/docs/html/resources/articles/images/inputmethod_composing_text_1.png b/docs/html/resources/articles/images/inputmethod_composing_text_1.png
new file mode 100644
index 0000000..3403489
--- /dev/null
+++ b/docs/html/resources/articles/images/inputmethod_composing_text_1.png
Binary files differ
diff --git a/docs/html/resources/articles/images/inputmethod_composing_text_2.png b/docs/html/resources/articles/images/inputmethod_composing_text_2.png
new file mode 100644
index 0000000..67bd1d9
--- /dev/null
+++ b/docs/html/resources/articles/images/inputmethod_composing_text_2.png
Binary files differ
diff --git a/docs/html/resources/articles/images/inputmethod_composing_text_3.png b/docs/html/resources/articles/images/inputmethod_composing_text_3.png
new file mode 100644
index 0000000..4fd1a30
--- /dev/null
+++ b/docs/html/resources/articles/images/inputmethod_composing_text_3.png
Binary files differ
diff --git a/docs/html/resources/articles/images/inputmethod_lifecycle_image.png b/docs/html/resources/articles/images/inputmethod_lifecycle_image.png
new file mode 100644
index 0000000..5f7cf95
--- /dev/null
+++ b/docs/html/resources/articles/images/inputmethod_lifecycle_image.png
Binary files differ
diff --git a/docs/html/resources/articles/images/inputmethod_numeric_type_screenshot.png b/docs/html/resources/articles/images/inputmethod_numeric_type_screenshot.png
new file mode 100644
index 0000000..61b7483
--- /dev/null
+++ b/docs/html/resources/articles/images/inputmethod_numeric_type_screenshot.png
Binary files differ
diff --git a/docs/html/resources/articles/images/inputmethod_subtype_notification.png b/docs/html/resources/articles/images/inputmethod_subtype_notification.png
new file mode 100644
index 0000000..3f13927
--- /dev/null
+++ b/docs/html/resources/articles/images/inputmethod_subtype_notification.png
Binary files differ
diff --git a/docs/html/resources/articles/images/inputmethod_subtype_preferences.png b/docs/html/resources/articles/images/inputmethod_subtype_preferences.png
new file mode 100644
index 0000000..d8aa0cf
--- /dev/null
+++ b/docs/html/resources/articles/images/inputmethod_subtype_preferences.png
Binary files differ
diff --git a/docs/html/resources/articles/images/inputmethod_subtype_settings.png b/docs/html/resources/articles/images/inputmethod_subtype_settings.png
new file mode 100644
index 0000000..b8942c6
--- /dev/null
+++ b/docs/html/resources/articles/images/inputmethod_subtype_settings.png
Binary files differ
diff --git a/docs/html/resources/articles/images/inputmethod_text_type_screenshot.png b/docs/html/resources/articles/images/inputmethod_text_type_screenshot.png
new file mode 100644
index 0000000..8008b27
--- /dev/null
+++ b/docs/html/resources/articles/images/inputmethod_text_type_screenshot.png
Binary files differ
diff --git a/docs/html/resources/resources_toc.cs b/docs/html/resources/resources_toc.cs
index e1a5e0f..303a6d4 100644
--- a/docs/html/resources/resources_toc.cs
+++ b/docs/html/resources/resources_toc.cs
@@ -97,6 +97,30 @@
</li>
</ul>
</li>
+
+ <li class="toggle-list">
+ <div><a href="<?cs var:toroot ?>training/efficient-downloads/index.html">
+ <span class="en">Transferring Data Without Draining the Battery</span>
+ </a> <span class="new">new!</span></div>
+ <ul>
+ <li><a href="<?cs var:toroot ?>training/efficient-downloads/efficient-network-access.html">
+ <span class="en">Optimizing Downloads for Efficient Network Access</span>
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/efficient-downloads/regular_updates.html">
+ <span class="en">Minimizing the Effect of Regular Updates</span>
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/efficient-downloads/redundant_redundant.html">
+ <span class="en">Redundant Downloads are Redundant</span>
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/efficient-downloads/connectivity_patterns.html">
+ <span class="en">Modifying Patterns Based on the Connectivity Type</span>
+ </a>
+ </li>
+ </ul>
+ </li>
<li class="toggle-list">
<div><a href="<?cs var:toroot ?>training/search/index.html">
diff --git a/docs/html/resources/samples/get.jd b/docs/html/resources/samples/get.jd
index 86ec836..751965f 100644
--- a/docs/html/resources/samples/get.jd
+++ b/docs/html/resources/samples/get.jd
@@ -26,15 +26,15 @@ needed!</p>
<p>The SDK sample code is available to you as a set of downloadable SDK
components, each of which contains the samples for a specific Android platform
version. Once you have installed the SDK, you can download one or more samples
-component(s) into your SDK environment using the Android SDK and AVD Manager
+component(s) into your SDK environment using the Android SDK Manager
tool, which is pre-installed in the SDK. </p>
-<p>To download the samples, launch the Android SDK and AVD Manager tool and
+<p>To download the samples, launch the Android SDK Manager tool and
select one of the samples components from the <strong>Available
Packages</strong> panel, for example "Samples for SDK API 7". Select
<strong>Install Selected</strong>, verify and accept the download, then select
<strong>Install Accepted</strong> to download the component into your SDK. If
-you aren't familiar with the Android SDK and AVD Manager and how to launch or
+you aren't familiar with the Android SDK Manager and how to launch or
use it, please read the <a href="{@docRoot}sdk/adding-components.html">Adding
SDK Components</a> document. </p>
@@ -74,7 +74,7 @@ From there you can read a short summary of each sample application and what
types of concepts, features, or APIs it includes. Then, use the links provided
to move through the directories and files of each sample. The browseable source
is generated from the same source code that is downloadable through the Android
-SDK and AVD Manager, as described above. </p>
+SDK Manager, as described above. </p>
<p>The browseable samples files are available online, at the Android Developers
site only and are not included in the downloadable offline documentation.
diff --git a/docs/html/resources/tutorials/hello-world.jd b/docs/html/resources/tutorials/hello-world.jd
index cc8cb3e..70ba06c 100644
--- a/docs/html/resources/tutorials/hello-world.jd
+++ b/docs/html/resources/tutorials/hello-world.jd
@@ -42,7 +42,7 @@ you need to do it now.</p>
<ol>
- <li>In the Android SDK and AVD Manager, choose <strong>Available
+ <li>In the Android SDK Manager, choose <strong>Available
Packages</strong> in the left panel.</li>
<li>In the right panel, expand the Android Repository list to display
@@ -72,7 +72,7 @@ device settings used by the emulator.</p>
<p>To create an AVD:</p>
<ol>
- <li>In Eclipse, select <strong>Window &gt; Android SDK and AVD Manager</strong>.</li>
+ <li>In Eclipse, select <strong>Window &gt; AVD Manager</strong>.</li>
<li>Select <strong>Virtual Devices</strong> in the left panel.</li>
<li>Click <strong>New...</strong>.
diff --git a/docs/html/resources/tutorials/testing/activity_test.jd b/docs/html/resources/tutorials/testing/activity_test.jd
index 4b861e2..f88b768 100644
--- a/docs/html/resources/tutorials/testing/activity_test.jd
+++ b/docs/html/resources/tutorials/testing/activity_test.jd
@@ -163,7 +163,7 @@ parent.link=../../browser.html?tag=tutorial
open the Android SDK and AVD Manager and check in the
<strong>Installed Packages</strong> panel.
If aren't sure how to download a platform into your SDK,
- read <a href="{@docRoot}sdk/adding-components.html">Adding SDK Components</a>.
+ read <a href="{@docRoot}sdk/adding-components.html">Adding SDK Packages</a>.
</p>
</li>
</ul>
diff --git a/docs/html/sdk/adding-components.jd b/docs/html/sdk/adding-components.jd
index ca27bba..599b2a8 100644
--- a/docs/html/sdk/adding-components.jd
+++ b/docs/html/sdk/adding-components.jd
@@ -1,4 +1,4 @@
-page.title=Adding SDK Components
+page.title=Adding SDK Packages
@jd:body
@@ -6,74 +6,74 @@ page.title=Adding SDK Components
<div id="qv">
<h2>Quickview</h2>
<ul>
- <li>Use the Android SDK and AVD Manager to
+ <li>Use the Android SDK Manager to
set up your SDK and keep it up-to-date.</li>
</ul>
<h2>In this document</h2>
<ol>
- <li><a href="#launching">Launching the Android SDK and AVD Manager</a>
- <li><a href="#InstallingComponents">Installing SDK Components</a>
- <li><a href="#UpdatingComponents">Updating SDK Components</a>
- <li><a href="#dependencies">Component Dependencies</a></li>
+ <li><a href="#launching">Launching the Android SDK Manager</a>
+ <li><a href="#InstallingComponents">Installing SDK Packages</a>
+ <li><a href="#UpdatingComponents">Updating SDK Packages</a>
+ <li><a href="#dependencies">Package Dependencies</a></li>
<li><a href="#AddingSites">Adding New Sites</a></li>
<li><a href="#troubleshooting">Troubleshooting</a></li>
</ol>
</div>
</div>
-<p>Adding and updating components in your Android SDK is fast and easy. To add or
-update the individual SDK components that you need, use the <em>Android SDK and AVD
+<p>Adding and updating packages in your Android SDK is fast and easy. To add or
+update the individual SDK packages that you need, use the <em>Android SDK
Manager</em> (included in the SDK Tools).</p>
<p>It only takes a couple of clicks to install individual versions of the
Android platform, new development tools, new documentation, and SDK add-ons. The
-new SDK components are automatically installed into your existing SDK directory,
+new SDK packages are automatically installed into your existing SDK directory,
so you don't need to update your development environment to specify a new SDK
location.</p>
<p>If you're setting up your Android SDK for the first time,
see <a href="{@docRoot}sdk/installing.html#components">Installing the SDK</a> for information about
-what components to install.</p>
+what packages to install.</p>
<p class="note"><strong>Note:</strong> If you develop in Eclipse, you might also need
to update your ADT plugin when you update your development tools. See the revisions listed in the
<a href="{@docRoot}sdk/eclipse-adt.html">ADT Plugin for Eclipse</a> document.</p>
<img src="{@docRoot}images/sdk_manager_packages.png" alt="" />
-<p class="img-caption"><strong>Figure 1.</strong> The Android SDK and AVD Manager's
-<strong>Available Packages</strong> panel, which shows the SDK components that are
+<p class="img-caption"><strong>Figure 1.</strong> The Android SDK Manager's
+<strong>Available Packages</strong> panel, which shows the SDK packages that are
available for you to download into your environment. </p>
</div>
-<h2 id="launching">Launching the Android SDK and AVD Manager</h2>
+<h2 id="launching">Launching the Android SDK Manager</h2>
-<p>The Android SDK and AVD Manager is the tool that you use to install and
-upgrade SDK components in your development environment. </p>
+<p>The Android SDK Manager is the tool that you use to install and
+upgrade SDK packages in your development environment. </p>
-<p>You can launch the Android SDK and AVD Manager in one of the following ways.</p>
+<p>You can launch the Android SDK Manager in one of the following ways.</p>
<h4>Launching from Eclipse/ADT</h4>
<p>If you are developing in Eclipse and have already installed the ADT Plugin,
-follow these steps to access the Android SDK and AVD Manager tool:</p>
+follow these steps to access the Android SDK Manager tool:</p>
<ol>
<li>Open Eclipse</li>
-<li>Select <strong>Window</strong> &gt; <strong>Android SDK and AVD
+<li>Select <strong>Window</strong> &gt; <strong>Android SDK
Manager</strong>.</li>
</ol>
<h4>Launching from the SDK Manager script (Windows only)</h4>
-<p>For Windows only, the SDK includes a script that invokes the Android SDK and
-AVD Manager. To launch the tool using the script, double-click {@code SDK
+<p>For Windows only, the SDK includes a script that invokes the Android SDK Manager. To launch the
+tool using the script, double-click {@code SDK
Manager.exe} at the root of the the SDK directory.</p>
<h4>Launching from a command line</h4>
-<p>In all development environments, follow these steps to access the Android SDK
-and AVD Manager tool from the command line: </p>
+<p>In all development environments, follow these steps to access the Android SDK Manager tool from
+the command line: </p>
<ol>
<li>Navigate to the <code>&lt;<em>sdk</em>&gt;/tools/</code> directory.</li>
@@ -82,26 +82,26 @@ and AVD Manager tool from the command line: </p>
</ol>
-<h2 id="InstallingComponents">Installing SDK Components</h2>
+<h2 id="InstallingComponents">Installing SDK Packages</h2>
-<p class="caution"><strong>Caution:</strong> Before you install SDK components,
+<p class="caution"><strong>Caution:</strong> Before you install SDK packages,
we recommend that you disable any antivirus software that may be running on
your computer. There are cases in which antivirus software on Windows is known to interfere with the
installation process, so we suggest you disable your antivirus until installation is
complete.</p>
-<p>Follow these steps to install new SDK components in your environment:</p>
+<p>Follow these steps to install new SDK packages in your environment:</p>
<ol>
- <li>Launch the Android SDK and AVD Manager as described in the section above.</li>
+ <li>Launch the Android SDK Manager as described in the section above.</li>
<li>Select <strong>Available Packages</strong> in the left panel.
- This will reveal all of the components that are currently available for download
+ This will reveal all of the packages that are currently available for download
from the SDK repository.</li>
- <li>Select the component(s) you'd like to install and click <strong>Install
+ <li>Select the package(s) you'd like to install and click <strong>Install
Selected</strong>. (If you aren't sure which packages to select, read <a
- href="installing.html#which">Recommended Components</a>.)</li>
- <li>Verify and accept the components you want (ensure each one is selected with a green
-checkmark) and click <strong>Install</strong>. The components will now be installed into
+ href="installing.html#which">Recommended Packages</a>.)</li>
+ <li>Verify and accept the packages you want (ensure each one is selected with a green
+checkmark) and click <strong>Install</strong>. The packages will now be installed into
your existing Android SDK directories.</li>
</ol>
@@ -114,60 +114,60 @@ and new documentation is saved in the existing
<code>&lt;sdk&gt;/docs/</code> directory (old docs are replaced).</p>
-<h2 id="UpdatingComponents">Updating SDK Components</h2>
+<h2 id="UpdatingComponents">Updating SDK Packages</h2>
-<p>From time to time, new revisions of existing SDK components are released and
+<p>From time to time, new revisions of existing SDK packages are released and
made available to you through the SDK repository. In most cases, if you have those
-components installed in your environment, you will want
+packages installed in your environment, you will want
to download the new revisions as soon as possible. </p>
<p>You can learn about the release of new revisions in two ways: </p>
<ul>
<li>You can watch for updates listed in the "SDK" tab of the Android Developers
-site, in the "Downloadable SDK Components" section. </li>
+site, in the "Downloadable SDK Packages" section. </li>
<li>You can watch for updates listed in the <strong>Available Packages</strong>
-panel of the Android SDK and AVD Manager. </li>
+panel of the Android SDK Manager. </li>
</ul>
-<p>When you see that a new revision is available, you can use the Android SDK
-and AVD Manager to quickly download it to your environment. Follow the same
-procedure as given in <a href="#InstallingComponents">Installing SDK Components</a>, above. The new
-component is installed in place of the old, but without impacting your
+<p>When you see that a new revision is available, you can use the Android SDK Manager to quickly
+download it to your environment. Follow the same
+procedure as given in <a href="#InstallingComponents">Installing SDK Packages</a>, above. The new
+package is installed in place of the old, but without impacting your
applications. </p>
<p class="note"><strong>Tip:</strong>
-Use the "Display updates only" checkbox to show only the components
+Use the "Display updates only" checkbox to show only the packages
you do not have.</p>
-<h2 id="dependencies">SDK Component Dependencies</h2>
+<h2 id="dependencies">SDK Package Dependencies</h2>
-<p>In some cases, an SDK component may require a specific minimum revision of
-another component or SDK tool. Where such dependencies exist, they are
-documented in the revision notes for each component, available from the links in
-the "Downloadable SDK Components" section at left.</p>
+<p>In some cases, an SDK package may require a specific minimum revision of
+another package or SDK tool. Where such dependencies exist, they are
+documented in the revision notes for each package, available from the links in
+the "Downloadable SDK packages" section at left.</p>
<p>For example, there may be a dependency between the ADT Plugin for Eclipse and
-the SDK Tools component. When you install the SDK Tools
-component, you should also upgrade to the required version of ADT (if you
+the SDK Tools package. When you install the SDK Tools
+package, you should also upgrade to the required version of ADT (if you
are developing in Eclipse). In this case, the major version number for your ADT plugin should
always match the revision number of your SDK Tools (for example, ADT 8.x requires SDK Tools r8).
</p>
<p>Also make sure that, each time you install a new version of the Android platform, you have
-the latest version of the SDK Platform-tools component. The SDK Platform-tools contain
+the latest version of the SDK Platform-tools package. The SDK Platform-tools contain
tools that are backward compatible with all versions of the Android platform and are
often updated to support new features in the latest version of the Android platform.</p>
<p>The development tools will notify you with debug warnings if there is dependency that you need to
-address. The SDK and AVD Manager also enforces dependencies by requiring that you download any
-components that are needed by those you have selected.</p>
+address. The Android SDK Manager also enforces dependencies by requiring that you download any
+packages that are needed by those you have selected.</p>
<h2 id="AddingSites">Adding New Sites</h2>
-<p>By default, <strong>Available Packages</strong> displays components available from the
+<p>By default, <strong>Available Packages</strong> displays packages available from the
<em>Android Repository</em> and <em>Third party Add-ons</em>. You can add other sites that host
their own Android SDK add-ons, then download the SDK add-ons
from those sites.</p>
@@ -178,7 +178,7 @@ to develop using their libraries, you must install their Android SDK add-on, if
available under <em>Third party Add-ons</em>. </p>
<p>If a carrier or device manufacturer has hosted an SDK add-on repository file
-on their web site, follow these steps to add their site to the SDK and AVD
+on their web site, follow these steps to add their site to the Android SDK
Manager:</p>
<ol>
@@ -186,7 +186,7 @@ Manager:</p>
<li>Click <strong>Add Add-on Site</strong> and enter the URL of the
{@code repository.xml} file. Click <strong>OK</strong>.</li>
</ol>
-<p>Any SDK components available from the site will now be listed under a new item named
+<p>Any SDK packages available from the site will now be listed under a new item named
<strong>User Add-ons</strong>.</p>
@@ -194,12 +194,12 @@ Manager:</p>
<p><strong>Problems connecting to the SDK repository</strong></p>
-<p>If you are using the SDK and AVD Manager to download components and are encountering
+<p>If you are using the Android SDK Manager to download packages and are encountering
connection problems, try connecting over http, rather than https. To switch the
-protocol used by the SDK and AVD Manager, follow these steps: </p>
+protocol used by the Android SDK Manager, follow these steps: </p>
<ol>
- <li>With the Android SDK and AVD Manager window open, select "Settings" in the
+ <li>With the Android SDK Manager window open, select "Settings" in the
left pane. </li>
<li>On the right, in the "Misc" section, check the checkbox labeled "Force
https://... sources to be fetched using http://..." </li>
diff --git a/docs/html/sdk/android-3.0.jd b/docs/html/sdk/android-3.0.jd
index 39a234f..3acb358 100644
--- a/docs/html/sdk/android-3.0.jd
+++ b/docs/html/sdk/android-3.0.jd
@@ -25,7 +25,8 @@ Differences Report &raquo;</a> </li>
<h2>See Also</h2>
<ol>
- <li><a href="{@docRoot}guide/practices/optimizing-for-3.0.html">Optimizing Apps for Android 3.0</a></li>
+ <li><a href="{@docRoot}guide/practices/tablets-and-handsets.html">Supporting Tablets and
+Handsets</a></li>
</ol>
</div>
@@ -41,7 +42,7 @@ libraries.</p>
<p>To get started developing or testing against Android {@sdkPlatformVersion}, use the Android SDK
Manager to download the platform into your SDK. For more information, see <a
-href="{@docRoot}sdk/adding-components.html">Adding SDK Components</a>. If you are new to Android, <a
+href="{@docRoot}sdk/adding-components.html">Adding SDK Packages</a>. If you are new to Android, <a
href="{@docRoot}sdk/index.html">download the SDK Starter Package</a> first.</p>
<p>For a high-level introduction to Android {@sdkPlatformVersion}, see the <a
@@ -52,8 +53,8 @@ Highlights</a>.</p>
If you've already published an Android application, please test and optimize your application on
Android 3.0 as soon as possible. You should do so to be sure your application provides the best
experience possible on the latest Android-powered devices. For information about what you can do,
-read <a href="{@docRoot}guide/practices/optimizing-for-3.0.html">Optimizing Apps for Android
-3.0</a>.</p>
+read <a href="{@docRoot}guide/practices/tablets-and-handsets.html">Supporting Tablets and
+Handsets</a>.</p>
<h2 id="relnotes">Revisions</h2>
diff --git a/docs/html/sdk/compatibility-library.jd b/docs/html/sdk/compatibility-library.jd
index 30d807f..f81e8ae 100644
--- a/docs/html/sdk/compatibility-library.jd
+++ b/docs/html/sdk/compatibility-library.jd
@@ -330,13 +330,13 @@ Android 3.2 and higher (all other APIs in the v4 library are already available w
<h2 id="Downloading">Downloading the Support Package</h2>
-<p>The Support Package is provided as a downloadable package from the Android SDK and AVD
+<p>The Support Package is provided as a downloadable package from the Android SDK
Manager. To install:</p>
<ol>
- <li>Launch the SDK and AVD Manager.
+ <li>Launch the Android SDK Manager.
<p>From Eclipse, you can select <strong>Window</strong>
-&gt; <strong>Android SDK and AVD Manager</strong>. Or, launch {@code SDK Manager.exe} from
+&gt; <strong>Android SDK Manager</strong>. Or, launch {@code SDK Manager.exe} from
the {@code &lt;sdk&gt;/} directory (on Windows only) or {@code android} from the {@code
&lt;sdk&gt;/tools/} directory.</p></li>
<li>Expand the Android Repository, check <strong>Android Support package</strong>
diff --git a/docs/html/sdk/eclipse-adt.jd b/docs/html/sdk/eclipse-adt.jd
index 3019544..485f01e 100644
--- a/docs/html/sdk/eclipse-adt.jd
+++ b/docs/html/sdk/eclipse-adt.jd
@@ -36,7 +36,7 @@ that is designed to give you a powerful, integrated environment in which
to build Android applications.</p>
<p>ADT extends the capabilities of Eclipse to let you quickly set up new Android
-projects, create an application UI, add components based on the Android
+projects, create an application UI, add packages based on the Android
Framework API, debug your applications using the Android SDK tools, and even
export signed (or unsigned) {@code .apk} files in order to distribute your application.</p>
@@ -468,7 +468,7 @@ ADT 12.0.0</a> <em>(July 2011)</em>
<dd>ADT 12.0.0 is designed for use with <a href="{@docRoot}sdk/tools-notes.html">SDK Tools r12</a>. If you haven't
already installed SDK Tools r12 into your SDK, use
-the Android SDK and AVD Manager to do so.</dd>
+the Android SDK Manager to do so.</dd>
<dt>Visual Layout Editor:</dt>
<dd>
@@ -520,7 +520,7 @@ ADT 11.0.0</a> <em>(June 2011)</em>
<dt>Dependencies:</dt>
<dd>ADT 11.0.0 is designed for use with SDK Tools r11. If you haven't
-already installed SDK Tools r11 into your SDK, use the Android SDK and AVD Manager to do
+already installed SDK Tools r11 into your SDK, use the Android SDK Manager to do
so.</dd>
<dt>Visual Refactoring:</dt>
@@ -656,7 +656,7 @@ ADT 10.0.1</a> <em>(March 2011)</em>
<dt>Dependencies:</dt>
<dd>ADT 10.0.1 is designed for use with SDK Tools r10. If you haven't
-already installed SDK Tools r10 into your SDK, use the Android SDK and AVD Manager to do
+already installed SDK Tools r10 into your SDK, use the Android SDK Manager to do
so.</dd>
<dt>General notes:</dt>
@@ -686,7 +686,7 @@ ADT 10.0.0</a> <em>(February 2011)</em>
<dt>Dependencies:</dt>
<dd>ADT 10.0.0 is designed for use with SDK Tools r10. If you haven't
-already installed SDK Tools r10 into your SDK, use the Android SDK and AVD Manager to do
+already installed SDK Tools r10 into your SDK, use the Android SDK Manager to do
so.</dd>
<dt>General notes:</dt>
@@ -736,7 +736,7 @@ ADT 9.0.0</a> <em>(January 2011)</em>
<dt>Dependencies:</dt>
<dd>ADT 9.0.0 is designed for use with SDK Tools r9. If you haven't
-already installed SDK Tools r9 into your SDK, use the Android SDK and AVD Manager to do
+already installed SDK Tools r9 into your SDK, use the Android SDK Manager to do
so.</dd>
<dt>General notes:</dt>
@@ -845,7 +845,7 @@ ADT 8.0.1</a> <em>(December 2010)</em>
<dt>Dependencies:</dt>
<p><p>ADT 8.0.1 is designed for use with SDK Tools r8. If you haven't
-already installed SDK Tools r8 into your SDK, use the Android SDK and AVD Manager to do
+already installed SDK Tools r8 into your SDK, use the Android SDK Manager to do
so.</p></dd>
<dt>General notes:</dt>
@@ -874,7 +874,7 @@ ADT 8.0.0</a> <em>(December 2010)</em>
<dt>Dependencies:</dt>
<p><p>ADT 8.0.0 is designed for use with SDK Tools r8. If you haven't
-already installed SDK Tools r8 into your SDK, use the Android SDK and AVD Manager to do
+already installed SDK Tools r8 into your SDK, use the Android SDK Manager to do
so.</p></dd>
<dt>General notes:</dt>
@@ -1020,8 +1020,8 @@ ADT 0.9.6</a> <em>(March 2010)</em>
<dt>Dependencies:</dt>
<dd><p>ADT 0.9.6 is designed for use with SDK Tools r5 and later. Before
-updating to ADT 0.9.6, we highly recommend that you use the Android SDK and
-AVD Manager to install SDK Tools r5 into your SDK.</p></dd>
+updating to ADT 0.9.6, we highly recommend that you use the Android SDK Manager to install SDK
+Tools r5 into your SDK.</p></dd>
<dt>General Notes:</dt>
<dd>
@@ -1038,8 +1038,8 @@ first time with the SDK Usage panel.</li>
<dt>AVD/SDK Manager:</dt>
<dd>
<ul>
-<li>Adds support for platform samples components.</li>
-<li>Improves support for dependency between components.</li>
+<li>Adds support for platform samples packages.</li>
+<li>Improves support for dependency between packages.</li>
<li>AVDs now sorted by API level.</li>
<li>The AVD creation dialog now enforces a minimum SD card size of 9MB.</li>
<li>Prevents deletion of running AVDs.</li>
@@ -1087,9 +1087,9 @@ ADT 0.9.5</a> <em>(December 2009)</em>
<dt>Dependencies:</dt>
<dd><p>ADT 0.9.5 requires features provided in SDK Tools r4 or higher. If you install
-ADT 0.9.5, which is highly recommended, you should use the Android SDK and AVD
+ADT 0.9.5, which is highly recommended, you should use the Android SDK
Manager to download the latest SDK Tools into your SDK. For more information,
-see <a href="{@docRoot}sdk/adding-components.html">Adding SDK Components</a>.</p>
+see <a href="{@docRoot}sdk/adding-components.html">Adding SDK Packages</a>.</p>
</dd>
<dt>General notes:</dt>
@@ -1114,9 +1114,9 @@ ADT 0.9.4</a> <em>(October 2009)</em>
<dt>Dependencies:</dt>
<dd><p>ADT 0.9.4 requires features provided in SDK Tools r3 or higher. If you install
-ADT 0.9.4, which is highly recommended, you should use the Android SDK and AVD
+ADT 0.9.4, which is highly recommended, you should use the Android SDK
Manager to download the latest SDK Tools into your SDK. For more information,
-see <a href="{@docRoot}sdk/adding-components.html">Adding SDK Components</a>.</p>
+see <a href="{@docRoot}sdk/adding-components.html">Adding SDK Packages</a>.</p>
</dd>
<dt>Project Creation Wizard:</dt>
@@ -1318,9 +1318,9 @@ to follow these steps again instead of the default update instructions.</p>
<h4>Other install errors</h4>
<p>Note that there are features of ADT that require some optional
-Eclipse components (for example, WST). If you encounter an error when
-installing ADT, your Eclipse installion might not include these components.
-For information about how to quickly add the necessary components to your
+Eclipse packages (for example, WST). If you encounter an error when
+installing ADT, your Eclipse installion might not include these packages.
+For information about how to quickly add the necessary packages to your
Eclipse installation, see the troubleshooting topic
<a href="{@docRoot}resources/faq/troubleshooting.html#installeclipsecomponents">ADT
Installation Error: "requires plug-in org.eclipse.wst.sse.ui"</a>.</p>
@@ -1345,9 +1345,9 @@ you should update to it as soon as convenient. </p>
<p>In some cases, a new revision of ADT will have a dependency on a specific
revision of the Android SDK Tools. If such dependencies exist, you will need to
-update the SDK Tools component of the SDK after installing the new revision of
-ADT. To update the SDK Tools component, use the Android SDK and AVD Manager, as
-described in <a href="adding-components.html">Adding SDK Components</a>.</p>
+update the SDK Tools package of the SDK after installing the new revision of
+ADT. To update the SDK Tools package, use the Android SDK Manager, as
+described in <a href="adding-components.html">Adding SDK Packages</a>.</p>
<p>To learn about new features of each ADT revision and also any dependencies on
the SDK Tools, see the listings in the <a href="#notes">Revisions</a>
diff --git a/docs/html/sdk/index.jd b/docs/html/sdk/index.jd
index c09b3c2..175ab50 100644
--- a/docs/html/sdk/index.jd
+++ b/docs/html/sdk/index.jd
@@ -28,7 +28,7 @@ sdk.linux_checksum=14e99dfa8eb1a8fadd2f3557322245c4
<li>Install the SDK starter package from the table above. (If you're on Windows, download the
installer for help with the initial setup.)</li>
<li>Install the ADT Plugin for Eclipse (if you'll be developing in Eclipse).</li>
- <li>Add Android platforms and other components to your SDK.</li>
+ <li>Add Android platforms and other packages to your SDK.</li>
<li>Explore the contents of the Android SDK (optional).</li>
</ol>
diff --git a/docs/html/sdk/installing.jd b/docs/html/sdk/installing.jd
index 1dce483..7461eb0 100644
--- a/docs/html/sdk/installing.jd
+++ b/docs/html/sdk/installing.jd
@@ -53,10 +53,10 @@ function toggleDiv(link) {
<li><a href="#Preparing">1. Preparing Your Development Computer</a></li>
<li><a href="#Installing">2. Downloading the SDK Starter Package</a></li>
<li><a href="#InstallingADT">3. Installing the ADT Plugin for Eclipse</a></li>
- <li><a href="#AddingComponents">4. Adding Platforms and Other Components</a>
+ <li><a href="#AddingComponents">4. Adding Platforms and Other Packages</a>
<ol>
- <li><a href="#components">Available Components</a></li>
- <li><a href="#which">Recommended Components</a></li>
+ <li><a href="#components">Available Packages</a></li>
+ <li><a href="#which">Recommended Packages</a></li>
</ol></li>
<li><a href="#sdkContents">5. Exploring the SDK (Optional)</a></li>
<li><a href="#NextSteps">Next Steps</a></li>
@@ -66,7 +66,7 @@ function toggleDiv(link) {
<h2>See also</h2>
<ol>
<li><a href="{@docRoot}sdk/eclipse-adt.html">ADT Plugin for Eclipse</a></li>
- <li><a href="{@docRoot}sdk/adding-components.html">Adding SDK Components</a></li>
+ <li><a href="{@docRoot}sdk/adding-components.html">Adding SDK Packages</a></li>
</ol>
</div>
@@ -81,9 +81,9 @@ this page.</p>
<h4>Updating?</h4>
-<p>If you already have an Android SDK, use the Android SDK and AVD Manager tool to install
+<p>If you already have an Android SDK, use the Android SDK Manager tool to install
updated tools and new Android platforms into your existing environment. For information about how to
-do that, see <a href="{@docRoot}sdk/adding-components.html">Adding SDK Components</a>.</p>
+do that, see <a href="{@docRoot}sdk/adding-components.html">Adding SDK Packages</a>.</p>
<h2 id="Preparing">Step 1. Preparing Your Development Computer</h2>
@@ -111,7 +111,7 @@ RCP version of Eclipse is recommended.</p>
<p>The SDK starter package is not a full
development environment&mdash;it includes only the core SDK Tools, which you can
-use to download the rest of the SDK components (such as the latest Android platform).</p>
+use to download the rest of the SDK packages (such as the latest Android platform).</p>
<p>If you haven't already, get the latest version of the SDK starter package from the <a
href="{@docRoot}sdk/index.html">SDK download page</a>.</p>
@@ -154,53 +154,53 @@ developing in Eclipse or other IDEs.</p>
-<h2 id="AddingComponents">Step 4. Adding Platforms and Other Components</h2>
+<h2 id="AddingComponents">Step 4. Adding Platforms and Other Packages</h2>
-<p>The last step in setting up your SDK is using the Android SDK and AVD Manager (a
-tool included in the SDK starter package) to download essential SDK components into your development
+<p>The last step in setting up your SDK is using the Android SDK Manager (a
+tool included in the SDK starter package) to download essential SDK packages into your development
environment.</p>
<p>The SDK uses a modular structure that separates the major parts of the SDK&mdash;Android platform
versions, add-ons, tools, samples, and documentation&mdash;into a set of separately installable
-components. The SDK starter package, which you've already downloaded, includes only a single
-component: the latest version of the SDK Tools. To develop an Android application, you also need to
+packages. The SDK starter package, which you've already downloaded, includes only a single
+package: the latest version of the SDK Tools. To develop an Android application, you also need to
download at least one Android platform and the associated platform tools. You can add other
-components and platforms as well, which is highly recommended.</p>
+packages and platforms as well, which is highly recommended.</p>
<p>If you used the Windows installer, when you complete the installation wizard, it will launch the
-Android SDK and AVD Manager with a default set of platforms and other components selected
+Android SDK Manager with a default set of platforms and other packages selected
for you to install. Simply click <strong>Install</strong> to accept the recommended set of
-components and install them. You can then skip to <a href="#sdkContents">Step 5</a>, but we
-recommend you first read the section about the <a href="#components">Available Components</a> to
-better understand the components available from the Android SDK and AVD Manager.</p>
+packages and install them. You can then skip to <a href="#sdkContents">Step 5</a>, but we
+recommend you first read the section about the <a href="#components">Available Packages</a> to
+better understand the packages available from the Android SDK Manager.</p>
-<p>You can launch the Android SDK and AVD Manager in one of the following ways:</p>
+<p>You can launch the Android SDK Manager in one of the following ways:</p>
<ul>
- <li>From within Eclipse, select <strong>Window &gt; Android SDK and AVD Manager</strong>.</li>
+ <li>From within Eclipse, select <strong>Window &gt; Android SDK Manager</strong>.</li>
<li>On Windows, double-click the <code>SDK Manager.exe</code> file at the root of the Android
SDK directory.</li>
<li>On Mac or Linux, open a terminal and navigate to the <code>tools/</code> directory in the
Android SDK, then execute: <pre>android</pre> </li>
</ul>
-<p>To download components, use the graphical UI of the Android SDK and AVD
+<p>To download packages, use the graphical UI of the Android SDK
Manager to browse the SDK repository and select new or updated
-components (see figure 1). The Android SDK and AVD Manager installs the selected components in
-your SDK environment. For information about which components you should download, see <a
-href="#which">Recommended Components</a>.</p>
+packages (see figure 1). The Android SDK Manager installs the selected packages in
+your SDK environment. For information about which packages you should download, see <a
+href="#which">Recommended Packages</a>.</p>
<img src="/images/sdk_manager_packages.png" />
-<p class="img-caption"><strong>Figure 1.</strong> The Android SDK and AVD Manager's
-<strong>Available Packages</strong> panel, which shows the SDK components that are
+<p class="img-caption"><strong>Figure 1.</strong> The Android SDK Manager's
+<strong>Available Packages</strong> panel, which shows the SDK packages that are
available for you to download into your environment.</p>
-<h3 id="components">Available Components</h3>
+<h3 id="components">Available Packages</h3>
-<p>By default, there are two repositories of components for your SDK: <em>Android
+<p>By default, there are two repositories of packages for your SDK: <em>Android
Repository</em> and <em>Third party Add-ons</em>.</p>
-<p>The <em>Android Repository</em> offers these types of components:</p>
+<p>The <em>Android Repository</em> offers these types of packages:</p>
<ul>
<li><strong>SDK Tools</strong> &mdash; Contains tools for debugging and testing your application
@@ -219,9 +219,9 @@ developer guide.</li>
<li><strong>Android platforms</strong> &mdash; An SDK platform is
available for every production Android platform deployable to Android-powered devices. Each
-SDK platform component includes a fully compliant Android library, system image, sample code,
+SDK platform package includes a fully compliant Android library, system image, sample code,
and emulator skins. To learn more about a specific platform, see the list of platforms that appears
-under the section "Downloadable SDK Components" on the left part of this page.</li>
+under the section "Downloadable SDK Packages" on the left part of this page.</li>
<li><strong>USB Driver for Windows</strong> (Windows only) &mdash; Contains driver files
that you can install on your Windows computer, so that you can run and debug
@@ -243,16 +243,16 @@ tutorials. --></li>
multiversion documentation for the Android framework API. </li>
</ul>
-<p>The <em>Third party Add-ons</em> provide components that allow you to create a development
+<p>The <em>Third party Add-ons</em> provide packages that allow you to create a development
environment using a specific Android external library (such as the Google Maps library) or a
customized (but fully compliant) Android system image. You can add additional Add-on repositories by
clicking <strong>Add Add-on Site</strong>.</p>
-<h3 id="which">Recommended Components</h3>
+<h3 id="which">Recommended Packages</h3>
-<p>The SDK repository contains a range of components that you can download.
-Use the table below to determine which components you need, based on whether you
+<p>The SDK repository contains a range of packages that you can download.
+Use the table below to determine which packages you need, based on whether you
want to set up a basic, recommended, or full development environment:
</p>
@@ -260,7 +260,7 @@ want to set up a basic, recommended, or full development environment:
<tr>
<th>Environment</th>
-<th>SDK&nbsp;Component</th>
+<th>SDK&nbsp;Package</th>
<th>Comments</th>
</tr>
@@ -268,8 +268,8 @@ want to set up a basic, recommended, or full development environment:
<td rowspan="3" style="font-size:.9em;background-color:#FFE;">Basic</td>
<td style="font-size:.9em;background-color:#FFE;">SDK Tools</td>
<td style="font-size:.9em;background-color:#FFE;">If you've just installed
-the SDK starter package, then you already have the latest version of this component. The
-SDK Tools component is required to develop an Android application. Make sure you keep this up to
+the SDK starter package, then you already have the latest version of this package. The
+SDK Tools package is required to develop an Android application. Make sure you keep this up to
date.</td>
</tr>
@@ -300,21 +300,21 @@ style="border:none"></td>
<tr>
<td rowspan="3">Recommended<br/>(plus Basic)</td>
<td>Documentation</td>
-<td>The Documentation component is useful because it lets you work offline and
+<td>The Documentation package is useful because it lets you work offline and
also look up API reference information from inside Eclipse.</td>
</tr>
<tr>
<td>Samples</td>
-<td>The Samples components give you source code that you can use to learn about
+<td>The Samples packages give you source code that you can use to learn about
Android, load as a project and run, or reuse in your own app. Note that multiple
-samples components are available &mdash; one for each Android platform version. When
-you are choosing a samples component to download, select the one whose API Level
+samples packages are available &mdash; one for each Android platform version. When
+you are choosing a samples package to download, select the one whose API Level
matches the API Level of the Android platform that you plan to use.</td>
</tr>
<tr>
<td>Usb Driver</td>
-<td>The Usb Driver component is needed only if you are developing on Windows and
+<td>The Usb Driver package is needed only if you are developing on Windows and
have an Android-powered device on which you want to install your application for
debugging and testing. For Mac OS X and Linux platforms, no
special driver is needed.</td>
@@ -344,12 +344,12 @@ applications on different platforms by running in an Android Virtual Device
</table>
-<p>Once you've installed at least the basic configuration of SDK components, you're ready to start
+<p>Once you've installed at least the basic configuration of SDK packages, you're ready to start
developing Android apps. The next section describes the contents of the Android SDK to familiarize
-you with the components you've just installed.</p>
+you with the packages you've just installed.</p>
-<p>For more information about using the Android SDK and AVD Manager, see the <a
-href="{@docRoot}sdk/adding-components.html">Adding SDK Components</a> document. </p>
+<p>For more information about using the Android SDK Manager, see the <a
+href="{@docRoot}sdk/adding-components.html">Adding SDK Packages</a> document. </p>
<h2 id="sdkContents">Step 5. Exploring the SDK (Optional)</h2>
@@ -358,7 +358,7 @@ href="{@docRoot}sdk/adding-components.html">Adding SDK Components</a> document.
and add-ons that you need, we suggest that you open the SDK directory and take a look at what's
inside.</p>
-<p>The table below describes the full SDK directory contents, with components
+<p>The table below describes the full SDK directory contents, with packages
installed. </p>
<table>
@@ -405,20 +405,21 @@ platform version.</td>
<tr>
<td colspan="3"><code>tools/</code></td>
<td>Contains the set of development and profiling tools that are platform-independent, such
-as the emulator, the Android SDK and AVD Manager, <code>ddms</code>, <code>hierarchyviewer</code>
-and more. The tools in this directory may be updated at any time using the Android SDK and AVD
+as the emulator, the Android SDK Manager, the AVD Manager, <code>ddms</code>,
+<code>hierarchyviewer</code>
+and more. The tools in this directory may be updated at any time using the Android SDK
Manager and are independent of platform releases.</td>
</tr>
<tr>
<td colspan="3"><code>SDK Readme.txt</code></td>
<td>A file that explains how to perform the initial setup of your SDK,
-including how to launch the Android SDK and AVD Manager tool on all
+including how to launch the Android SDK Manager tool on all
platforms.</td>
</tr>
<tr>
<td colspan="3"><code>SDK Manager.exe</code></td>
-<td>Windows SDK only. A shortcut that launches the Android SDK and AVD
-Manager tool, which you use to add components to your SDK.</td>
+<td>Windows SDK only. A shortcut that launches the Android SDK
+Manager tool, which you use to add packages to your SDK.</td>
</tr>
<!--<tr>
<td colspan="3"><code>documentation.html</code></td>
@@ -531,7 +532,7 @@ second step in getting started with Android development. </p>
<li>The Android SDK includes sample code and applications for each platform
version. You can browse the samples in the <a
href="{@docRoot}resources/index.html">Resources</a> tab or download them
-into your SDK using the Android SDK and AVD Manager. Once you've downloaded the
+into your SDK using the Android SDK Manager. Once you've downloaded the
samples, you'll find them in
<code><em>&lt;sdk&gt;</em>/samples/<em>&lt;platform&gt;/</em></code>. </li>
</ul>
diff --git a/docs/html/sdk/oem-usb.jd b/docs/html/sdk/oem-usb.jd
index 818a0af..88d66dd 100644
--- a/docs/html/sdk/oem-usb.jd
+++ b/docs/html/sdk/oem-usb.jd
@@ -314,6 +314,10 @@ href="http://developer.sonyericsson.com/wportal/devworld/search-downloads/driver
href="http://www.teleepoch.com/android.html">http://www.teleepoch.com/android.html</a></td>
</tr>
+<tr><td>Yulong Coolpad</td> <td><a
+href="http://www.yulong.com/product/product/product/downloadList.html#downListUL">http://www.yulong.com/product/product/product/downloadList.html#downListUL</a></td>
+</tr>
+
<tr>
<td>ZTE</td> <td><a
href="http://support.zte.com.cn/support/news/NewsDetail.aspx?newsId=1000442">http://support.zte.com.cn/support/news/NewsDetail.aspx?newsId=1000442</a></td></tr>
diff --git a/docs/html/sdk/sdk_toc.cs b/docs/html/sdk/sdk_toc.cs
index 5a5517c..5413784 100644
--- a/docs/html/sdk/sdk_toc.cs
+++ b/docs/html/sdk/sdk_toc.cs
@@ -55,7 +55,7 @@ class="new">new!</span></li>
/if ?>
<li>
<span class="heading">
- <span class="en">Downloadable SDK Components</span>
+ <span class="en">Downloadable SDK Packages</span>
<span style="display:none" class="de"></span>
<span style="display:none" class="es"></span>
<span style="display:none" class="fr"></span>
@@ -66,7 +66,7 @@ class="new">new!</span></li>
</span>
<ul>
<li><a href="<?cs var:toroot ?>sdk/adding-components.html">
- <span class="en">Adding SDK Components</span>
+ <span class="en">Adding SDK Packages</span>
<span style="display:none" class="de"></span>
<span style="display:none" class="es"></span>
<span style="display:none" class="fr"></span>
diff --git a/docs/html/sdk/win-usb.jd b/docs/html/sdk/win-usb.jd
index 6869d74..3be0faf 100644
--- a/docs/html/sdk/win-usb.jd
+++ b/docs/html/sdk/win-usb.jd
@@ -12,7 +12,7 @@ page.title=Google USB Driver
<ol>
<li><a href="{@docRoot}sdk/oem-usb.html#InstallingDriver">Installing a USB Driver</a></li>
<li><a href="{@docRoot}guide/developing/device.html">Using Hardware Devices</a></li>
- <li><a href="{@docRoot}sdk/adding-components.html">Adding SDK Components</a></li>
+ <li><a href="{@docRoot}sdk/adding-components.html">Adding SDK Packages</a></li>
</ol>
</div>
</div>
@@ -51,7 +51,7 @@ for Windows. </p>
<p>The sections below provide notes about successive revisions of the USB Driver
for Windows, as denoted by revision number. To determine what revision of the
USB Driver for Windows you are using, refer to the "Installed Packages" listing
-in the Android SDK and AVD Manager.</p>
+in the Android SDK Manager.</p>
<script type="text/javascript">
function toggleDiv(link) {
@@ -149,7 +149,7 @@ for the T-Mobile G1 and myTouch 3G (and similar devices).</p></dt>
<div class="figure" style="width:536px;margin:0">
<img src="{@docRoot}images/developing/sdk-usb-driver.png" alt="" />
- <p class="img-caption"><strong>Figure 1.</strong> The SDK and AVD Manager
+ <p class="img-caption"><strong>Figure 1.</strong> The SDK Manager
with the Google USB Driver selected.</p>
</div>
@@ -158,10 +158,10 @@ component. You need the driver only if you are developing on Windows and
want to connect an Android-powered device (ADP, Nexus One, or Nexus S) to your
development environment over USB. </p>
-<p>To download the driver, use the Android SDK and AVD Manager tool that is
+<p>To download the driver, use the Android SDK Manager tool that is
included with the <a href="{@docRoot}sdk/index.html">Android SDK</a>:</p>
<ol>
- <li>Launch the SDK and AVD Manager by double-clicking <code>SDK Manager.exe</code>,
+ <li>Launch the Android SDK Manager by double-clicking <code>SDK Manager.exe</code>,
at the root of your SDK directory.</li>
<li>Expand <em>Extras</em>.</li>
<li>Check <strong>Google USB Driver package</strong> and click <strong>Install</strong>.</li>
diff --git a/docs/html/training/efficient-downloads/connectivity_patterns.jd b/docs/html/training/efficient-downloads/connectivity_patterns.jd
new file mode 100644
index 0000000..81f1540
--- /dev/null
+++ b/docs/html/training/efficient-downloads/connectivity_patterns.jd
@@ -0,0 +1,76 @@
+page.title=Modifying your Download Patterns Based on the Connectivity Type
+parent.title=Transferring Data Without Draining the Battery
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Redundant Downloads are Redundant
+previous.link=redundant_redundant.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#WiFi">Use Wi-Fi</a></li>
+ <li><a href="#Bandwidth">Use greater bandwidth to download more data less often</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+ <li><a href="{@docRoot}training/monitoring-device-state/index.html">Optimizing Battery Life</a></li>
+</ul>
+
+</div>
+</div>
+
+<p>When it comes to impact on battery life, not all connection types are created equal. Not only does the Wi-Fi radio use significantly less battery than its wireless radio counterparts, but the radios used in different wireless radio technologies have different battery implications.</p>
+
+<h2 id="WiFi">Use Wi-Fi</h2>
+
+<p>In most cases a Wi-Fi radio will offer greater bandwidth at a significantly lower battery cost. As a result, you should endeavor to perform data transfers when connected over Wi-Fi whenever possible.</p>
+
+<p>You can use a broadcast receiver to listen for connectivity changes that indicate when a Wi-Fi connection has been established to execute significant downloads, preempt scheduled updates, and potentially even temporarily increase the frequency of regular updates as described in <a href="{@docRoot}training/monitoring-device-state/index.html">Optimizing Battery Life</a> lesson <a href="{@docRoot}training/monitoring-device-state/connectivity-monitoring.html">Determining and Monitoring the Connectivity Status</a>.</p>
+
+<h2 id="Bandwidth">Use Greater Bandwidth to Download More Data Less Often</h2>
+
+<p>When connected over a wireless radio, higher bandwidth generally comes at the price of higher battery cost. Meaning that LTE typically consumes more energy than 3G, which is in turn more expensive than 2G.</p>
+
+<p>This means that while the underlying radio state machine varies based on the radio technology, generally speaking the relative battery impact of the state change tail-time is greater for higher bandwidth radios.</p>
+
+<p>At the same time, the higher bandwidth means you can prefetch more aggressively, downloading more data over the same time. Perhaps less intuitively, because the tail-time battery cost is relatively higher, it's also more efficient to keep the radio active for longer periods during each transfer session to reduce the frequency of updates.</p>
+
+<p>For example, if an LTE radio is has double the bandwidth and double the energy cost of 3G, you should download 4 times as much data during each session&mdash;or potentially as much as 10mb. When downloading this much data, it's important to consider the effect of your prefetching on the available local storage and flush your prefetch cache regularly.</p>
+
+<p>You can use the connectivity manager to determine the active wireless radio, and modify your prefetching routines accordingly:</p>
+
+<pre>ConnectivityManager cm =
+ (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
+
+TelephonyManager tm =
+ (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
+
+NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
+
+int PrefetchCacheSize = DEFAULT_PREFETCH_CACHE;
+
+switch (activeNetwork.getType()) {
+ case (ConnectivityManager.TYPE_WIFI):
+ PrefetchCacheSize = MAX_PREFETCH_CACHE; break;
+ case (ConnectivityManager.TYPE_MOBILE): {
+ switch (tm.getNetworkType()) {
+ case (TelephonyManager.NETWORK_TYPE_LTE |
+ TelephonyManager.NETWORK_TYPE_HSPAP):
+ PrefetchCacheSize *= 4;
+ break;
+ case (TelephonyManager.NETWORK_TYPE_EDGE |
+ TelephonyManager.NETWORK_TYPE_GPRS):
+ PrefetchCacheSize /= 2;
+ break;
+ default: break;
+ }
+ break;
+ }
+ default: break;
+}</pre> \ No newline at end of file
diff --git a/docs/html/training/efficient-downloads/efficient-network-access.jd b/docs/html/training/efficient-downloads/efficient-network-access.jd
new file mode 100644
index 0000000..0efad7d
--- /dev/null
+++ b/docs/html/training/efficient-downloads/efficient-network-access.jd
@@ -0,0 +1,170 @@
+page.title=Optimizing Downloads for Efficient Network Access
+parent.title=Transferring Data Without Draining the Battery
+parent.link=index.html
+
+trainingnavtop=true
+next.title=Minimizing the Effect of Regular Updates
+next.link=regular_updates.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#RadioStateMachine">Understand the radio state machine</a></li>
+ <li><a href="#AppsStateMachine">Understand how apps can impact the radio state machine</a></li>
+ <li><a href="#PrefetchData">Efficiently prefetch data</a></li>
+ <li><a href="#BatchTransfers">Batch transfers and connections</a></li>
+ <li><a href="#ReduceConnections">Reduce the number of connections you use</a></li>
+ <li><a href="#DDMSNetworkTraffic">Use the DDMS Network Traffic Tool to identify areas of concern</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+ <li><a href="{@docRoot}training/monitoring-device-state/index.html">Optimizing Battery Life</a></li>
+</ul>
+
+</div>
+</div>
+
+<p>Using the wireless radio to transfer data is potentially one of your app's most significant sources of battery drain. To minimize the battery drain associated with network activity, it's critical that you understand how your connectivity model will affect the underlying radio hardware.</p>
+
+<p>This lesson introduces the wireless radio state machine and explains how your app's connectivity model interacts with it. It goes on to propose ways to minimize your data connections, use prefetching, and bundle your transfers in order to minimize the battery drain associated with your data transfers.</p>
+
+<h2 id="RadioStateMachine">The Radio State Machine</h2>
+
+<p>A fully active wireless radio consumes significant power, so it transitions between different energy states in order to conserve power when not in use, while attempting to minimize latency associated with "powering up" the radio when it's required.</p>
+
+<p>The state machine for a typical 3G network radio consists of three energy states:
+<ol><li><b>Full power</b>: Used when a connection is active, allowing the device to transfer data at its highest possible rate.</li>
+<li><b>Low power</b>: An intermediate state that uses around 50% of the battery power at the full state.</li>
+<li><b>Standby</b>: The minimal energy state during which no network connection is active or required.</li>
+</ol></p>
+
+<p>While the low and idle states drain significantly less battery, they also introduce significant latency to network requests. Returning to full power from the low state takes around 1.5 seconds, while moving from idle to full can take over 2 seconds.</p>
+
+<p>To minimize latency, the state machine uses a delay to postpone the transition to lower energy states. Figure 1 uses AT&T's timings for a typical 3G radio.</p>
+
+<img src="{@docRoot}images/efficient-downloads/mobile_radio_state_machine.png" />
+<p class="img-caption"><strong>Figure 1.</strong> Typical 3G wireless radio state machine.</p>
+
+<p>The radio state machine on each device, particularly the associated transition delay ("tail time") and startup latency, will vary based on the wireless radio technology employed (2G, 3G, LTE, etc.) and is defined and configured by the carrier network over which the device is operating.</p>
+
+<p>This lesson describes a representative state machine for a typical 3G wireless radio, based on <a href="http://www.research.att.com/articles/featured_stories/2011_03/201102_Energy_efficient?fbid=1zObBOMOZSB">data provided by AT&T</a>. However, the general principles and resulting best practices are applicable for all wireless radio implementations.</p>
+
+<p>This approach is particularly effective for typical web browsing as it prevents unwelcome latency while users browse the web. The relatively low tail-time also ensures that once a browsing session has finished, the radio can move to a lower energy state.</p>
+
+<p>Unfortunately, this approach can lead to inefficient apps on modern smartphone OSs like Android, where apps run both in the foreground (where latency is important) and in the background (where battery life should be prioritized).</p>
+
+<h2 id="AppsStateMachine">How Apps Impact the Radio State Machine</h2>
+
+<p>Every time you create a new network connection, the radio transitions to the full power state. In the case of the typical 3G radio state machine described above, it will remain at full power for the duration of your transfer&mdash;plus an additional 5 seconds of tail time&mdash;followed by 12 seconds at the low energy state. So for a typical 3G device, every data transfer session will cause the radio to draw energy for almost 20 seconds.</p>
+
+<p>In practice, this means an app that transfers unbundled data for 1 second every 18 seconds will keep the wireless radio perpetually active, moving it back to high power just as it was about to become idle. As a result, every minute it will consume battery at the high power state for 18 seconds, and at the low power state for the remaining 42 seconds.</p>
+
+<p>By comparison, the same app that bundles transfers of 3 seconds of every minute will keep the radio in the high power state for only 8 seconds, and will keep it in the low power state for only an additional 12 seconds.</p>
+
+<p>The second example allows the radio to be idle for an additional 40 second every minute, resulting in a massive reduction in battery consumption.</p>
+
+<img src="{@docRoot}images/efficient-downloads/graphs.png" />
+<p class="img-caption"><strong>Figure 2.</strong> Relative wireless radio power use for bundled versus unbundled transfers.</p>
+
+<h2 id="PrefetchData">Prefetch Data</h2>
+
+<p>Prefetching data is an effective way to reduce the number of independent data transfer sessions. Prefetching allows you to download all the data you are likely to need for a given time period in a single burst, over a single connection, at full capacity.</p>
+
+<p>By front loading your transfers, you reduce the number of radio activations required to download the data. As a result you not only conserve battery life, but also improve the latency, lower the required bandwidth, and reduce download times.</p>
+
+<p>Prefetching also provides an improved user experience by minimizing in-app latency caused by waiting for downloads to complete before performing an action or viewing data.</p>
+
+<p>However, used too aggressively, prefetching introduces the risk of increasing battery drain and bandwidth use&mdash;as well as download quota&mdash;by downloading data that isn't used. It's also important to ensure that prefetching doesn't delay application startup while the app waits for the prefetch to complete. In practical terms that might mean processing data progressively, or initiating consecutive transfers prioritized such that the data required for application startup is downloaded and processed first.</p>
+
+<p>How aggressively you prefetch depends on the size of the data being downloaded and the likelihood of it being used. As a rough guide, based on the state machine described above, for data that has a 50% chance of being used within the current user session, you can typically prefetch for around 6 seconds (approximately 1-2 Mb) before the potential cost of downloading unused data matches the potential savings of not downloading that data to begin with.</p>
+
+<p>Generally speaking, it's good practice to prefetch data such that you will only need to initiate another download every 2 to 5 minutes, and in the order of 1 to 5 megabytes.</p>
+
+<p>Following this principle, large downloads&mdash;such as video files&mdash;should be downloaded in chunks at regular intervals (every 2 to 5 minutes), effectively prefetching only the video data likely to be viewed in the next few minutes.</p>
+
+<p>Note that further downloads should be bundled, as described in the next section, <a href="#BatchTransfers">Batch Transfers and Connections</a>, and that these approximations will vary based on the connection type and speed, as discussed in <a href="connectivity_patterns.html">Modify your Download Patterns Based on the Connectivity Type</a>.</p>
+
+<p>Let's look at some practical examples:</p>
+
+<p><b>A music player</b></p>
+
+<p>You could choose to prefetch an entire album, however should the user stop listening after the first song, you've wasted a significant amount of bandwidth and battery life.</p>
+
+<p>A better approach would be to maintain a buffer of one song in addition to the one being played. For streaming music, rather than maintaining a continuous stream that keeps the radio active at all times, consider using HTTP live streaming to transmit the audio stream in bursts, simulating the prefetching approach described above.</p>
+
+<p><b>A news reader</b></p>
+
+<p>Many news apps attempt to reduce bandwidth by downloading headlines only after a category has been selected, full articles only when the user wants to read them, and thumbnails just as they scroll into view.</p>
+
+<p>Using this approach, the radio will be forced to remain active for the majority of users' news-reading session as they scroll headlines, change categories, and read articles. Not only that, but the constant switching between energy states will result in significant latency when switching categories or reading articles.</p>
+
+<p>A better approach would be to prefetch a reasonable amount of data at startup, beginning with the first set of news headlines and thumbnails&mdash;ensuring a low latency startup time&mdash;and continuing with the remaining headlines and thumbnails, as well as the article text for each article available from at least the primary headline list.</p>
+
+<p>Another alternative is to prefetch every headline, thumbnail, article text, and possibly even full article pictures&mdash;typically in the background on a predetermined schedule. This approach risks spending significant bandwidth and battery life downloading content that's never used, so it should be implemented with caution.</p>
+
+<p>One solution is to schedule the full download to occur only when connected to Wi-Fi, and possibly only when the device is charging. This is investigated in more detail in <a href="connectivity_patterns.html">Modify your Download Patterns Based on the Connectivity Type</a>.</p>
+
+<h2 id="BatchTransfers">Batch Transfers and Connections</h2>
+
+Every time you initiate a connection&mdash;irrespective of the size of the associated data transfer&mdash;you potentially cause the radio to draw power for nearly 20 seconds when using a typical 3G wireless radio.</p>
+
+<p>An app that pings the server every 20 seconds, just to acknowledge that the app is running and visible to the user, will keep the radio powered on indefinitely, resulting in a significant battery cost for almost no actual data transfer.</p>
+
+<p>With that in mind it's important to bundle your data transfers and create a pending transfer queue. Done correctly, you can effectively phase-shift transfers that are due to occur within a similar time window, to make them all happen simultaneously&mdash;ensuring that the radio draws power for as short a duration as possible.</p>
+
+<p>The underlying philosophy of this approach is to transfer as much data as possible during each transfer session in an effort to limit the number of sessions you require.</p>
+
+<p>That means you should batch your transfers by queuing delay tolerant transfers, and preempting scheduled updates and prefetches, so that they are all executed when time-sensitive transfers are required. Similarly, your scheduled updates and regular prefetching should initiate the execution of your pending transfer queue.</p>
+
+<p>For a practical example, let's return to the earlier examples from <a href="#PrefetchData">Prefetch Data</a>.</p>
+
+<p>Take a news application that uses the prefetching routine described above. The news reader collects analytics information to understand the reading patterns of its users and to rank the most popular stories. To keep the news fresh, it checks for updates every hour. To conserve bandwidth, rather than download full photos for each article, it prefetches only thumbnails and downloads the full photos when they are selected.</p>
+
+<p>In this example, all the analytics information collected within the app should be bundled together and queued for download, rather than being transmitted as it's collected. The resulting bundle should be transferred when either a full-sized photo is being downloaded, or when an hourly update is being performed.</p>
+
+<p>Any time-sensitive or on-demand transfer&mdash;such as downloading a full-sized image&mdash;should preempt regularly scheduled updates. The planned update should be executed at the same time as the on-demand transfer, with the next update scheduled to occur after the set interval. This approach mitigates the cost of performing a regular update by piggy-backing on the necessary time-sensitive photo download.</p>
+
+<h2 id="ReduceConnections">Reduce Connections</h2>
+
+<p>It's generally more efficient to reuse existing network connections than to initiate new ones. Reusing connections also allows the network to more intelligently react to congestion and related network data issues.</p>
+
+<p>Rather than creating multiple simultaneous connections to download data, or chaining multiple consecutive GET requests, where possible you should bundle those requests into a single GET.</p>
+
+<p>For example, it would be more efficient to make a single request for every news article to be returned in a single request / response than to make multiple queries for several news categories.
+The wireless radio needs to become active in order to transmit the termination / termination acknowledgement packets associated with server and client timeout, so it's also good practice to close your connections when they aren't in use, rather than waiting for these timeouts.</p>
+
+<p>That said, closing a connection too early can prevent it from being reused, which then requires additional overhead for establishing a new connection. A useful compromise is not to close the connection immediately, but to still close it before the inherent timeout expires.</p>
+
+<h2 id="DDMSNetworkTraffic">Use the DDMS Network Traffic Tool to Identify Areas of Concern</h2>
+
+<p>The Android <a href="{@docRoot}guide/developing/debugging/ddms.html">DDMS (Dalvik Debug Monitor Server)</a> includes a Detailed Network Usage tab that makes it possible to track when your application is making network requests. Using this tool, you can monitor how and when your app transfers data and optimize the underlying code appropriately.</p>
+
+<p>Figure 3 shows a pattern of transferring small amounts of data roughly 15 seconds apart, suggesting that efficiency could be dramatically improved by prefetching each request or bundling the uploads.</p>
+
+<img src="{@docRoot}images/efficient-downloads/DDMS.png" />
+<p class="img-caption"><strong>Figure 3.</strong> Tracking network usage with DDMS.</p>
+
+<p>By monitoring the frequency of your data transfers, and the amount of data transferred during each connection, you can identify areas of your application that can be made more battery-efficient. Generally, you will be looking for short spikes that can be delayed, or that should cause a later transfer to be preempted.</p>
+
+<p>To better identify the cause of transfer spikes, the Traffic Stats API allows you to tag the data transfers occurring within a thread using the {@code TrafficStats.setThreadStatsTag()} method, followed by manually tagging (and untagging) individual sockets using {@code tagSocket()} and {@code untagSocket()}. For example:</p>
+
+<pre>TrafficStats.setThreadStatsTag(0xF00D);
+TrafficStats.tagSocket(outputSocket);
+// Transfer data using socket
+TrafficStats.untagSocket(outputSocket);</pre>
+
+<p>The Apache {@code HttpClient} and {@code URLConnection} libraries automatically tag sockets based on the current {@code getThreadStatsTag()} value. These libraries also tag and untag sockets when recycled through keep-alive pools.</p>
+
+<pre>TrafficStats.setThreadStatsTag(0xF00D);
+try {
+ // Make network request using HttpClient.execute()
+} finally {
+ TrafficStats.clearThreadStatsTag();
+}</pre>
+
+<p>Socket tagging is supported in Android 4.0, but real-time stats will only be displayed on devices running Android 4.0.3 or higher.</p> \ No newline at end of file
diff --git a/docs/html/training/efficient-downloads/index.jd b/docs/html/training/efficient-downloads/index.jd
new file mode 100644
index 0000000..a29be91
--- /dev/null
+++ b/docs/html/training/efficient-downloads/index.jd
@@ -0,0 +1,51 @@
+page.title=Transferring Data Without Draining the Battery
+
+trainingnavtop=true
+startpage=true
+next.title=Optimizing Downloads for Efficient Network Access
+next.link=efficient-network-access.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- Required platform, tools, add-ons, devices, knowledge, etc. -->
+<h2>Dependencies and prerequisites</h2>
+<ul>
+ <li>Android 2.0 (API Level 5) or higher</li>
+</ul>
+
+<!-- related docs (NOT javadocs) -->
+<h2>You should also read</h2>
+<ul>
+ <li><a href="{@docRoot}training/monitoring-device-state/index.html">Optimizing Battery Life</a></li>
+</ul>
+
+</div>
+</div>
+
+<p>In this class you will learn to minimize the battery life impact of downloads and network connections, particularly in relation to the wireless radio.</P
+
+<p>This class demonstrates the best practices for scheduling and executing downloads using techniques such as caching, polling, and prefetching. You will learn how the power-use profile of the wireless radio can affect your choices on when, what, and how to transfer data in order to minimize impact on battery life.</p>
+
+<h2>Lessons</h2>
+
+<!-- Create a list of the lessons in this class along with a short description of each lesson.
+These should be short and to the point. It should be clear from reading the summary whether someone
+will want to jump to a lesson or not.-->
+
+<dl>
+ <dt><b><a href="efficient-network-access.html">Optimizing Downloads for Efficient Network Access</a></b></dt>
+ <dd>This lesson introduces the wireless radio state machine, explains how your app’s connectivity model interacts with it, and how you can minimize your data connection and use prefetching and bundling to minimize the battery drain associated with your data transfers.</dd>
+
+ <dt><b><a href="regular_updates.html">Minimizing the Effect of Regular Updates</a></b></dt>
+ <dd>This lesson will examine how your refresh frequency can be varied to best mitigate the effect of background updates on the underlying wireless radio state machine.</dd>
+
+ <dt><b><a href="redundant_redundant.html">Redundant Downloads are Redundant</a></b></dt>
+ <dd>The most fundamental way to reduce your downloads is to download only what you need. This lesson introduces some best practices to eliminate redundant downloads.</dd>
+
+ <dt><b><a href="connectivity_patterns.html">Modifying your Download Patterns Based on the Connectivity Type</a></b></dt>
+ <dd>When it comes to impact on battery life, not all connection types are created equal. Not only does the Wi-Fi radio use significantly less battery than its wireless radio counterparts, but the radios used in different wireless radio technologies have different battery implications.</dd>
+
+</dl>
diff --git a/docs/html/training/efficient-downloads/redundant_redundant.jd b/docs/html/training/efficient-downloads/redundant_redundant.jd
new file mode 100644
index 0000000..4bf9af9
--- /dev/null
+++ b/docs/html/training/efficient-downloads/redundant_redundant.jd
@@ -0,0 +1,87 @@
+page.title=Redundant Downloads are Redundant
+parent.title=Transferring Data Without Draining the Battery
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Minimizing the Effect of Regular Updates
+previous.link=regular_updates.html
+next.title=Connectivity Based Download Patterns
+next.link=connectivity_patterns.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#LocalCache">Cache files locally</a></li>
+ <li><a href="#ResponseCache">Use the HttpURLConnection response cache</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+ <li><a href="{@docRoot}training/monitoring-device-state/index.html">Optimizing Battery Life</a></li>
+</ul>
+
+</div>
+</div>
+
+<p>The most fundamental way to reduce your downloads is to download only what you need. In terms of data, that means implementing REST APIs that allow you to specify query criteria that limit the returned data by using parameters such as the time of your last update.</p>
+
+<p>Similarly, when downloading images, it's good practice to reduce the size of the images server-side, rather than downloading full-sized images that are reduced on the client.</p>
+
+<h2 id="LocalCache">Cache Files Locally</h2>
+
+<p>Another important technique is to avoid downloading duplicate data. You can do this by aggressive caching. Always cache static resources, including on-demand downloads such as full size images, for as long as reasonably possible. On-demand resources should be stored separately to enable you to regularly flush your on-demand cache to manage its size.</p>
+
+<p>To ensure that your caching doesn't result in your app displaying stale data, be sure to extract the time at which the requested content was last updated, and when it expires, from within the HTTP response headers. This will allow you to determine when the associated content should be refreshed.</p>
+
+<pre>long currentTime = System.currentTimeMillis());
+
+HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+
+long expires = conn.getHeaderFieldDate("Expires", currentTime);
+long lastModified = conn.getHeaderFieldDate("Last-Modified", currentTime);
+
+setDataExpirationDate(expires);
+
+if (lastModified < lastUpdateTime) {
+ // Skip update
+} else {
+ // Parse update
+}</pre>
+
+<p>Using this approach, you can also effectively cache dynamic content while ensuring it doesn't result in your application displaying stale information.</p>
+
+<p>You can cache non-sensitive data can in the unmanaged external cache directory:</p>
+
+<pre>Context.getExternalCacheDir();</pre>
+
+<p>Alternatively, you can use the managed / secure application cache. Note that this internal cache may be flushed when the system is running low on available storage.</p>
+
+<pre>Context.getCache();</pre>
+
+<p>Files stored in either cache location will be erased when the application is uninstalled.</p>
+
+<h2 id="ResponseCache">Use the HttpURLConnection Response Cache</h2>
+
+<p>Android 4.0 added a response cache to {@code HttpURLConnection}. You can enable HTTP response caching on supported devices using reflection as follows:</p>
+
+<pre>private void enableHttpResponseCache() {
+ try {
+ long httpCacheSize = 10 * 1024 * 1024; // 10 MiB
+ File httpCacheDir = new File(getCacheDir(), "http");
+ Class.forName("android.net.http.HttpResponseCache")
+ .getMethod("install", File.class, long.class)
+ .invoke(null, httpCacheDir, httpCacheSize);
+ } catch (Exception httpResponseCacheNotAvailable) {
+ Log.d(TAG, "HTTP response cache is unavailable.");
+ }
+}</pre>
+
+<p>This sample code will turn on the response cache on Android 4.0+ devices without affecting earlier releases.</p>
+
+<p>With the cache installed, fully cached HTTP requests can be served directly from local storage, eliminating the need to open a network connection. Conditionally cached responses can validate their freshness from the server, eliminating the bandwidth cost associated with the download.</p>
+
+<p>Uncached responses get stored in the response cache for for future requests.</p> \ No newline at end of file
diff --git a/docs/html/training/efficient-downloads/regular_updates.jd b/docs/html/training/efficient-downloads/regular_updates.jd
new file mode 100644
index 0000000..feb7a8e
--- /dev/null
+++ b/docs/html/training/efficient-downloads/regular_updates.jd
@@ -0,0 +1,102 @@
+page.title=Minimizing the Effect of Regular Updates
+parent.title=Transferring Data Without Draining the Battery
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Optimizing Downloads for Efficient Network Access
+previous.link=efficient-network-access.html
+next.title=Redundant Downloads are Redundant
+next.link=redundant_redundant.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#C2DM">Use Cloud to Device Messaging as an alternative to polling</a></li>
+ <li><a href="#OptimizedPolling">Optimize polling with inexact repeating alarms and exponential back-offs</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+ <li><a href="{@docRoot}training/monitoring-device-state/index.html">Optimizing Battery Life</a></li>
+ <li><a href="http://code.google.com/android/c2dm/">Android Cloud to Device Messaging</a></li>
+</ul>
+
+</div>
+</div>
+
+<p>The optimal frequency of regular updates will vary based on device state, network connectivity, user behavior, and explicit user preferences.</p>
+
+<p><a href="{@docRoot}training/monitoring-device-state/index.html">Optimizing Battery Life</a> discusses how to build battery-efficient apps that modify their refresh frequency based on the state of the host device. That includes disabling background service updates when you lose connectivity and reducing the rate of updates when the battery level is low.</p>
+
+<p>This lesson will examine how your refresh frequency can be varied to best mitigate the effect of background updates on the underlying wireless radio state machine.</p>
+
+<h2 id="C2DM">Use Cloud to Device Messaging as an Alternative to Polling</h2>
+
+<p>Every time your app polls your server to check if an update is required, you activate the wireless radio, drawing power unnecessarily, for up to 20 seconds on a typical 3G connection.</p>
+
+<p><a href="http://code.google.com/android/c2dm/">Android Cloud to Device Messaging (C2DM)</a> is a lightweight mechanism used to transmit data from a server to a particular app instance. Using C2DM, your server can notify your app running on a particular device that there is new data available for it.</p>
+
+<p>Compared to polling, where your app must regularly ping the server to query for new data, this event-driven model allows your app to create a new connection only when it knows there is data to download.</p>
+
+<p>The result is a reduction in unnecessary connections, and a reduced latency for updated data within your application.</p>
+
+<p>C2DM is implemented using a persistent TCP/IP connection. While it's possible to implement your own push service, it's best practice to use C2DM. This minimizes the number of persistent connections and allows the platform to optimize bandwidth and minimize the associated impact on battery life.</p>
+
+<h2 id="OptimizedPolling">Optimize Polling with Inexact Repeating Alarms and Exponential Backoffs</h2>
+
+<p>Where polling is required, it's good practice to set the default data refresh frequency of your app as low as possible without detracting from the user experience.</p>
+
+<p>A simple approach is to offer preferences to allow users to explicitly set their required update rate, allowing them to define their own balance between data freshness and battery life.</p>
+
+<p>When scheduling updates, use inexact repeating alarms that allow the system to "phase shift" the exact moment each alarm triggers.</p>
+
+<pre>int alarmType = AlarmManager.ELAPSED_REALTIME;
+long interval = AlarmManager.INTERVAL_HOUR;
+long start = System.currentTimeMillis() + interval;
+
+alarmManager.setInexactRepeating(alarmType, start, interval, pi);</pre>
+
+<p>If several alarms are scheduled to trigger at similar times, this phase-shifting will cause them to be triggered simultaneously, allowing each update to piggyback on top of a single active radio state change.</p>
+
+<p>Wherever possible, set your alarm type to {@code ELAPSED_REALTIME} or {@code RTC} rather than to their {@code _WAKEUP} equivalents. This further reduces battery impact by waiting until the phone is no longer in standby mode before the alarm triggers.</p>
+
+<p>You can further reduce the impact of these scheduled alarms by opportunistically reducing their frequency based on how recently your app was used.</p>
+
+<p>One approach is to implement an exponential back-off pattern to reduce the frequency of your updates (and / or the degree of prefetching you perform) if the app hasn't been used since the previous update. It's often useful to assert a minimum update frequency and to reset the frequency whenever the app is used, for example:</p>
+
+<pre>SharedPreferences sp =
+ context.getSharedPreferences(PREFS, Context.MODE_WORLD_READABLE);
+
+boolean appUsed = sp.getBoolean(PREFS_APPUSED, false);
+long updateInterval = sp.getLong(PREFS_INTERVAL, DEFAULT_REFRESH_INTERVAL);
+
+if (!appUsed)
+ if ((updateInterval *= 2) > MAX_REFRESH_INTERVAL)
+ updateInterval = MAX_REFRESH_INTERVAL;
+
+Editor spEdit = sp.edit();
+spEdit.putBoolean(PREFS_APPUSED, false);
+spEdit.putLong(PREFS_INTERVAL, updateInterval);
+spEdit.apply();
+
+rescheduleUpdates(updateInterval);
+executeUpdateOrPrefetch();</pre>
+
+<p>You can use a similar exponential back-off pattern to reduce the effect of failed connections and download errors.</p>
+
+<p>The cost of initiating a network connection is the same whether you are able to contact your server and download data or not. For time-sensitive transfers where successful completion is important, an exponential back-off algorithm can be used to reduce the frequency of retries in order to minimize the associated battery impact, for example:</p>
+
+<pre>private void retryIn(long interval) {
+ boolean success = attemptTransfer();
+
+ if (!success) {
+ retryIn(interval*2 < MAX_RETRY_INTERVAL ?
+ interval*2 : MAX_RETRY_INTERVAL);
+ }
+}</pre>
+
+<p>Alternatively, for transfers that are failure tolerant (such as regular updates), you can simply ignore failed connection and transfer attempts.</p> \ No newline at end of file
diff --git a/include/cpustats/CentralTendencyStatistics.h b/include/cpustats/CentralTendencyStatistics.h
deleted file mode 100644
index 21b6981..0000000
--- a/include/cpustats/CentralTendencyStatistics.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _CENTRAL_TENDENCY_STATISTICS_H
-#define _CENTRAL_TENDENCY_STATISTICS_H
-
-#include <math.h>
-
-// Not multithread safe
-class CentralTendencyStatistics {
-
-public:
-
- CentralTendencyStatistics() :
- mMean(NAN), mMedian(NAN), mMinimum(INFINITY), mMaximum(-INFINITY), mN(0), mM2(0),
- mVariance(NAN), mVarianceKnownForN(0), mStddev(NAN), mStddevKnownForN(0) { }
-
- ~CentralTendencyStatistics() { }
-
- // add x to the set of samples
- void sample(double x);
-
- // return the arithmetic mean of all samples so far
- double mean() const { return mMean; }
-
- // return the minimum of all samples so far
- double minimum() const { return mMinimum; }
-
- // return the maximum of all samples so far
- double maximum() const { return mMaximum; }
-
- // return the variance of all samples so far
- double variance() const;
-
- // return the standard deviation of all samples so far
- double stddev() const;
-
- // return the number of samples added so far
- unsigned n() const { return mN; }
-
- // reset the set of samples to be empty
- void reset();
-
-private:
- double mMean;
- double mMedian;
- double mMinimum;
- double mMaximum;
- unsigned mN; // number of samples so far
- double mM2;
-
- // cached variance, and n at time of caching
- mutable double mVariance;
- mutable unsigned mVarianceKnownForN;
-
- // cached standard deviation, and n at time of caching
- mutable double mStddev;
- mutable unsigned mStddevKnownForN;
-
-};
-
-#endif // _CENTRAL_TENDENCY_STATISTICS_H
diff --git a/include/cpustats/README.txt b/include/cpustats/README.txt
deleted file mode 100644
index 14439f0..0000000
--- a/include/cpustats/README.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a static library of CPU usage statistics, originally written
-for audio but most are not actually specific to audio.
-
-Requirements to be here:
- * should be related to CPU usage statistics
- * should be portable to host; avoid Android OS dependencies without a conditional
diff --git a/include/cpustats/ThreadCpuUsage.h b/include/cpustats/ThreadCpuUsage.h
deleted file mode 100644
index 9cd93d8..0000000
--- a/include/cpustats/ThreadCpuUsage.h
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _THREAD_CPU_USAGE_H
-#define _THREAD_CPU_USAGE_H
-
-#include <fcntl.h>
-#include <pthread.h>
-
-namespace android {
-
-// Track CPU usage for the current thread.
-// Units are in per-thread CPU ns, as reported by
-// clock_gettime(CLOCK_THREAD_CPUTIME_ID). Simple usage: for cyclic
-// threads where you want to measure the execution time of the whole
-// cycle, just call sampleAndEnable() at the start of each cycle.
-// For acyclic threads, or for cyclic threads where you want to measure/track
-// only part of each cycle, call enable(), disable(), and/or setEnabled()
-// to demarcate the region(s) of interest, and then call sample() periodically.
-// This class is not thread-safe for concurrent calls from multiple threads;
-// the methods of this class may only be called by the current thread
-// which constructed the object.
-
-class ThreadCpuUsage
-{
-
-public:
- ThreadCpuUsage() :
- mIsEnabled(false),
- mWasEverEnabled(false),
- mAccumulator(0),
- // mPreviousTs
- // mMonotonicTs
- mMonotonicKnown(false)
- {
- (void) pthread_once(&sOnceControl, &init);
- for (int i = 0; i < sKernelMax; ++i) {
- mCurrentkHz[i] = (uint32_t) ~0; // unknown
- }
- }
-
- ~ThreadCpuUsage() { }
-
- // Return whether currently tracking CPU usage by current thread
- bool isEnabled() const { return mIsEnabled; }
-
- // Enable tracking of CPU usage by current thread;
- // any CPU used from this point forward will be tracked.
- // Returns the previous enabled status.
- bool enable() { return setEnabled(true); }
-
- // Disable tracking of CPU usage by current thread;
- // any CPU used from this point forward will be ignored.
- // Returns the previous enabled status.
- bool disable() { return setEnabled(false); }
-
- // Set the enabled status and return the previous enabled status.
- // This method is intended to be used for safe nested enable/disabling.
- bool setEnabled(bool isEnabled);
-
- // Add a sample point, and also enable tracking if needed.
- // If tracking has never been enabled, then this call enables tracking but
- // does _not_ add a sample -- it is not possible to add a sample the
- // first time because there is no previous point to subtract from.
- // Otherwise, if tracking is enabled,
- // then adds a sample for tracked CPU ns since the previous
- // sample, or since the first call to sampleAndEnable(), enable(), or
- // setEnabled(true). If there was a previous sample but tracking is
- // now disabled, then adds a sample for the tracked CPU ns accumulated
- // up until the most recent disable(), resets this accumulator, and then
- // enables tracking. Calling this method rather than enable() followed
- // by sample() avoids a race condition for the first sample.
- // Returns true if the sample 'ns' is valid, or false if invalid.
- // Note that 'ns' is an output parameter passed by reference.
- // The caller does not need to initialize this variable.
- // The units are CPU nanoseconds consumed by current thread.
- bool sampleAndEnable(double& ns);
-
- // Add a sample point, but do not
- // change the tracking enabled status. If tracking has either never been
- // enabled, or has never been enabled since the last sample, then log a warning
- // and don't add sample. Otherwise, adds a sample for tracked CPU ns since
- // the previous sample or since the first call to sampleAndEnable(),
- // enable(), or setEnabled(true) if no previous sample.
- // Returns true if the sample is valid, or false if invalid.
- // Note that 'ns' is an output parameter passed by reference.
- // The caller does not need to initialize this variable.
- // The units are CPU nanoseconds consumed by current thread.
- bool sample(double& ns);
-
- // Return the elapsed delta wall clock ns since initial enable or reset,
- // as reported by clock_gettime(CLOCK_MONOTONIC).
- long long elapsed() const;
-
- // Reset elapsed wall clock. Has no effect on tracking or accumulator.
- void resetElapsed();
-
- // Return current clock frequency for specified CPU, in kHz.
- // You can get your CPU number using sched_getcpu(2). Note that, unless CPU affinity
- // has been configured appropriately, the CPU number can change.
- // Also note that, unless the CPU governor has been configured appropriately,
- // the CPU frequency can change. And even if the CPU frequency is locked down
- // to a particular value, that the frequency might still be adjusted
- // to prevent thermal overload. Therefore you should poll for your thread's
- // current CPU number and clock frequency periodically.
- uint32_t getCpukHz(int cpuNum);
-
-private:
- bool mIsEnabled; // whether tracking is currently enabled
- bool mWasEverEnabled; // whether tracking was ever enabled
- long long mAccumulator; // accumulated thread CPU time since last sample, in ns
- struct timespec mPreviousTs; // most recent thread CPU time, valid only if mIsEnabled is true
- struct timespec mMonotonicTs; // most recent monotonic time
- bool mMonotonicKnown; // whether mMonotonicTs has been set
-
- static const int MAX_CPU = 8;
- static int sScalingFds[MAX_CPU];// file descriptor per CPU for reading scaling_cur_freq
- uint32_t mCurrentkHz[MAX_CPU]; // current CPU frequency in kHz, not static to avoid a race
- static pthread_once_t sOnceControl;
- static int sKernelMax; // like MAX_CPU, but determined at runtime == cpu/kernel_max + 1
- static void init();
-};
-
-} // namespace android
-
-#endif // _THREAD_CPU_USAGE_H
diff --git a/include/media/ICrypto.h b/include/media/ICrypto.h
new file mode 100644
index 0000000..916abe0
--- /dev/null
+++ b/include/media/ICrypto.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binder/IInterface.h>
+#include <media/stagefright/foundation/ABase.h>
+
+#ifndef ANDROID_ICRYPTO_H_
+
+#define ANDROID_ICRYPTO_H_
+
+namespace android {
+
+struct ICrypto : public IInterface {
+ DECLARE_META_INTERFACE(Crypto);
+
+ virtual status_t initialize() = 0;
+ virtual status_t terminate() = 0;
+
+ virtual status_t setEntitlementKey(
+ const void *key, size_t keyLength) = 0;
+
+ virtual status_t setEntitlementControlMessage(
+ const void *msg, size_t msgLength) = 0;
+
+ // "dstData" is in media_server's address space (but inaccessible).
+ virtual ssize_t decryptVideo(
+ const void *iv, size_t ivLength,
+ const void *srcData, size_t srcDataSize,
+ void *dstData, size_t dstDataOffset) = 0;
+
+ // "dstData" is in the calling process' address space.
+ virtual ssize_t decryptAudio(
+ const void *iv, size_t ivLength,
+ const void *srcData, size_t srcDataSize,
+ void *dstData, size_t dstDataSize) = 0;
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(ICrypto);
+};
+
+struct BnCrypto : public BnInterface<ICrypto> {
+ virtual status_t onTransact(
+ uint32_t code, const Parcel &data, Parcel *reply,
+ uint32_t flags = 0);
+};
+
+} // namespace android
+
+#endif // ANDROID_ICRYPTO_H_
+
diff --git a/include/media/IMediaPlayerService.h b/include/media/IMediaPlayerService.h
index 4f46fcd..76c45a0 100644
--- a/include/media/IMediaPlayerService.h
+++ b/include/media/IMediaPlayerService.h
@@ -31,6 +31,7 @@
namespace android {
+struct ICrypto;
class IMediaRecorder;
class IOMX;
struct IStreamSource;
@@ -47,6 +48,7 @@ public:
virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat) = 0;
virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat) = 0;
virtual sp<IOMX> getOMX() = 0;
+ virtual sp<ICrypto> makeCrypto() = 0;
// codecs and audio devices usage tracking for the battery app
enum BatteryDataBits {
diff --git a/include/media/JetPlayer.h b/include/media/JetPlayer.h
index 38a3e44..0616bf0 100644
--- a/include/media/JetPlayer.h
+++ b/include/media/JetPlayer.h
@@ -18,7 +18,6 @@
#define JETPLAYER_H_
#include <utils/threads.h>
-#include <nativehelper/jni.h>
#include <libsonivox/jet.h>
#include <libsonivox/eas_types.h>
@@ -40,7 +39,7 @@ public:
static const int JET_NUMQUEUEDSEGMENT_UPDATE = 3;
static const int JET_PAUSE_UPDATE = 4;
- JetPlayer(jobject javaJetPlayer,
+ JetPlayer(void *javaJetPlayer,
int maxTracks = 32,
int trackBufferSize = 1200);
~JetPlayer();
@@ -75,7 +74,7 @@ private:
jetevent_callback mEventCallback;
- jobject mJavaJetPlayerRef;
+ void* mJavaJetPlayerRef;
Mutex mMutex; // mutex to sync the render and playback thread with the JET calls
pid_t mTid;
Condition mCondition;
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index fa1a416..7d7af63 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -89,6 +89,10 @@ private:
kPortIndexOutput = 1
};
+ enum {
+ kFlagIsSecure = 1,
+ };
+
struct BufferInfo {
enum Status {
OWNED_BY_US,
@@ -118,6 +122,7 @@ private:
sp<FlushingState> mFlushingState;
AString mComponentName;
+ uint32_t mFlags;
uint32_t mQuirks;
sp<IOMX> mOMX;
IOMX::node_id mNode;
@@ -176,7 +181,8 @@ private:
status_t setupAACCodec(
bool encoder,
- int32_t numChannels, int32_t sampleRate, int32_t bitRate);
+ int32_t numChannels, int32_t sampleRate, int32_t bitRate,
+ bool isADTS);
status_t selectAudioPortFormat(
OMX_U32 portIndex, OMX_AUDIO_CODINGTYPE desiredFormat);
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index 72ac56a..0fc88e1 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -27,18 +27,21 @@ namespace android {
struct ABuffer;
struct ACodec;
struct AMessage;
+struct ICrypto;
struct SoftwareRenderer;
struct SurfaceTextureClient;
struct MediaCodec : public AHandler {
enum ConfigureFlags {
CONFIGURE_FLAG_ENCODE = 1,
+ CONFIGURE_FLAG_SECURE = 2,
};
enum BufferFlags {
BUFFER_FLAG_SYNCFRAME = 1,
BUFFER_FLAG_CODECCONFIG = 2,
BUFFER_FLAG_EOS = 4,
+ BUFFER_FLAG_ENCRYPTED = 8,
};
static sp<MediaCodec> CreateByType(
@@ -137,11 +140,13 @@ private:
kFlagStickyError = 8,
kFlagDequeueInputPending = 16,
kFlagDequeueOutputPending = 32,
+ kFlagIsSecure = 64,
};
struct BufferInfo {
void *mBufferID;
sp<ABuffer> mData;
+ sp<ABuffer> mEncryptedData;
sp<AMessage> mNotify;
bool mOwnedByClient;
};
@@ -165,6 +170,8 @@ private:
int32_t mDequeueOutputTimeoutGeneration;
uint32_t mDequeueOutputReplyID;
+ sp<ICrypto> mCrypto;
+
MediaCodec(const sp<ALooper> &looper);
static status_t PostAndAwaitResponse(
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index c3ccb56..639446e 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -128,6 +128,12 @@ enum {
kKeyTextFormatData = 'text', // raw data
kKeyRequiresSecureBuffers = 'secu', // bool (int32_t)
+
+ kKeyScrambling = 'scrm', // int32_t
+ kKeyEMM = 'emm ', // raw data
+ kKeyECM = 'ecm ', // raw data
+
+ kKeyIsADTS = 'adts', // bool (int32_t)
};
enum {
diff --git a/include/media/stagefright/NuMediaExtractor.h b/include/media/stagefright/NuMediaExtractor.h
index 96efdff..07c7be5 100644
--- a/include/media/stagefright/NuMediaExtractor.h
+++ b/include/media/stagefright/NuMediaExtractor.h
@@ -31,6 +31,11 @@ struct MediaExtractor;
struct MediaSource;
struct NuMediaExtractor : public RefBase {
+ enum SampleFlags {
+ SAMPLE_FLAG_SYNC = 1,
+ SAMPLE_FLAG_ENCRYPTED = 2,
+ };
+
NuMediaExtractor();
status_t setDataSource(const char *path);
@@ -46,6 +51,7 @@ struct NuMediaExtractor : public RefBase {
status_t readSampleData(const sp<ABuffer> &buffer);
status_t getSampleTrackIndex(size_t *trackIndex);
status_t getSampleTime(int64_t *sampleTimeUs);
+ status_t getSampleFlags(uint32_t *sampleFlags);
protected:
virtual ~NuMediaExtractor();
@@ -61,7 +67,9 @@ private:
status_t mFinalResult;
MediaBuffer *mSample;
int64_t mSampleTimeUs;
- uint32_t mFlags; // bitmask of "TrackFlags"
+ uint32_t mSampleFlags;
+
+ uint32_t mTrackFlags; // bitmask of "TrackFlags"
};
sp<MediaExtractor> mImpl;
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index 7c612ba..7d51dee 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -238,7 +238,11 @@ private:
void setComponentRole();
void setAMRFormat(bool isWAMR, int32_t bitRate);
- status_t setAACFormat(int32_t numChannels, int32_t sampleRate, int32_t bitRate);
+
+ status_t setAACFormat(
+ int32_t numChannels, int32_t sampleRate, int32_t bitRate,
+ bool isADTS);
+
void setG711Format(int32_t numChannels);
status_t setVideoPortFormatType(
diff --git a/include/media/stagefright/foundation/AString.h b/include/media/stagefright/foundation/AString.h
index 55ade64..0f8f1e1 100644
--- a/include/media/stagefright/foundation/AString.h
+++ b/include/media/stagefright/foundation/AString.h
@@ -73,6 +73,7 @@ struct AString {
int compare(const AString &other) const;
bool startsWith(const char *prefix) const;
+ bool endsWith(const char *suffix) const;
void tolower();
diff --git a/include/private/hwui/DrawGlInfo.h b/include/private/hwui/DrawGlInfo.h
index 1e9912b..abcf41d 100644
--- a/include/private/hwui/DrawGlInfo.h
+++ b/include/private/hwui/DrawGlInfo.h
@@ -42,6 +42,23 @@ struct DrawGlInfo {
float dirtyTop;
float dirtyRight;
float dirtyBottom;
+
+ /**
+ * Values used by OpenGL functors to tell the framework
+ * what to do next.
+ */
+ enum Status {
+ // The functor is done
+ kStatusDone,
+ // The functor is requesting a redraw (the clip rect
+ // used by the redraw is specified by DrawGlInfo.)
+ // The rest of the UI might redraw too.
+ kStatusDraw,
+ // The functor needs to be invoked again but will
+ // not redraw. Only the functor is invoked again
+ // (unless another functor requests a redraw.)
+ kStatusInvoke
+ };
}; // struct DrawGlInfo
}; // namespace uirenderer
diff --git a/libs/cpustats/Android.mk b/libs/cpustats/Android.mk
deleted file mode 100644
index 21bacbb..0000000
--- a/libs/cpustats/Android.mk
+++ /dev/null
@@ -1,21 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- CentralTendencyStatistics.cpp \
- ThreadCpuUsage.cpp
-
-LOCAL_MODULE := libcpustats
-
-include $(BUILD_STATIC_LIBRARY)
-
-#include $(CLEAR_VARS)
-#
-#LOCAL_SRC_FILES := \
-# CentralTendencyStatistics.cpp \
-# ThreadCpuUsage.cpp
-#
-#LOCAL_MODULE := libcpustats
-#
-#include $(BUILD_HOST_STATIC_LIBRARY)
diff --git a/libs/cpustats/CentralTendencyStatistics.cpp b/libs/cpustats/CentralTendencyStatistics.cpp
deleted file mode 100644
index 42ab62b..0000000
--- a/libs/cpustats/CentralTendencyStatistics.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdlib.h>
-
-#include <cpustats/CentralTendencyStatistics.h>
-
-void CentralTendencyStatistics::sample(double x)
-{
- // update min and max
- if (x < mMinimum)
- mMinimum = x;
- if (x > mMaximum)
- mMaximum = x;
- // Knuth
- if (mN == 0) {
- mMean = 0;
- }
- ++mN;
- double delta = x - mMean;
- mMean += delta / mN;
- mM2 += delta * (x - mMean);
-}
-
-void CentralTendencyStatistics::reset()
-{
- mMean = NAN;
- mMedian = NAN;
- mMinimum = INFINITY;
- mMaximum = -INFINITY;
- mN = 0;
- mM2 = 0;
- mVariance = NAN;
- mVarianceKnownForN = 0;
- mStddev = NAN;
- mStddevKnownForN = 0;
-}
-
-double CentralTendencyStatistics::variance() const
-{
- double variance;
- if (mVarianceKnownForN != mN) {
- if (mN > 1) {
- // double variance_n = M2/n;
- variance = mM2 / (mN - 1);
- } else {
- variance = NAN;
- }
- mVariance = variance;
- mVarianceKnownForN = mN;
- } else {
- variance = mVariance;
- }
- return variance;
-}
-
-double CentralTendencyStatistics::stddev() const
-{
- double stddev;
- if (mStddevKnownForN != mN) {
- stddev = sqrt(variance());
- mStddev = stddev;
- mStddevKnownForN = mN;
- } else {
- stddev = mStddev;
- }
- return stddev;
-}
diff --git a/libs/cpustats/ThreadCpuUsage.cpp b/libs/cpustats/ThreadCpuUsage.cpp
deleted file mode 100644
index 99b4c83..0000000
--- a/libs/cpustats/ThreadCpuUsage.cpp
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "ThreadCpuUsage"
-//#define LOG_NDEBUG 0
-
-#include <errno.h>
-#include <stdlib.h>
-#include <time.h>
-
-#include <utils/Debug.h>
-#include <utils/Log.h>
-
-#include <cpustats/ThreadCpuUsage.h>
-
-namespace android {
-
-bool ThreadCpuUsage::setEnabled(bool isEnabled)
-{
- bool wasEnabled = mIsEnabled;
- // only do something if there is a change
- if (isEnabled != wasEnabled) {
- ALOGV("setEnabled(%d)", isEnabled);
- int rc;
- // enabling
- if (isEnabled) {
- rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &mPreviousTs);
- if (rc) {
- ALOGE("clock_gettime(CLOCK_THREAD_CPUTIME_ID) errno=%d", errno);
- isEnabled = false;
- } else {
- mWasEverEnabled = true;
- // record wall clock time at first enable
- if (!mMonotonicKnown) {
- rc = clock_gettime(CLOCK_MONOTONIC, &mMonotonicTs);
- if (rc) {
- ALOGE("clock_gettime(CLOCK_MONOTONIC) errno=%d", errno);
- } else {
- mMonotonicKnown = true;
- }
- }
- }
- // disabling
- } else {
- struct timespec ts;
- rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
- if (rc) {
- ALOGE("clock_gettime(CLOCK_THREAD_CPUTIME_ID) errno=%d", errno);
- } else {
- long long delta = (ts.tv_sec - mPreviousTs.tv_sec) * 1000000000LL +
- (ts.tv_nsec - mPreviousTs.tv_nsec);
- mAccumulator += delta;
-#if 0
- mPreviousTs = ts;
-#endif
- }
- }
- mIsEnabled = isEnabled;
- }
- return wasEnabled;
-}
-
-bool ThreadCpuUsage::sampleAndEnable(double& ns)
-{
- bool ret;
- bool wasEverEnabled = mWasEverEnabled;
- if (enable()) {
- // already enabled, so add a new sample relative to previous
- return sample(ns);
- } else if (wasEverEnabled) {
- // was disabled, but add sample for accumulated time while enabled
- ns = (double) mAccumulator;
- mAccumulator = 0;
- ALOGV("sampleAndEnable %.0f", ns);
- return true;
- } else {
- // first time called
- ns = 0.0;
- ALOGV("sampleAndEnable false");
- return false;
- }
-}
-
-bool ThreadCpuUsage::sample(double &ns)
-{
- if (mWasEverEnabled) {
- if (mIsEnabled) {
- struct timespec ts;
- int rc;
- rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
- if (rc) {
- ALOGE("clock_gettime(CLOCK_THREAD_CPUTIME_ID) errno=%d", errno);
- ns = 0.0;
- return false;
- } else {
- long long delta = (ts.tv_sec - mPreviousTs.tv_sec) * 1000000000LL +
- (ts.tv_nsec - mPreviousTs.tv_nsec);
- mAccumulator += delta;
- mPreviousTs = ts;
- }
- } else {
- mWasEverEnabled = false;
- }
- ns = (double) mAccumulator;
- ALOGV("sample %.0f", ns);
- mAccumulator = 0;
- return true;
- } else {
- ALOGW("Can't add sample because measurements have never been enabled");
- ns = 0.0;
- return false;
- }
-}
-
-long long ThreadCpuUsage::elapsed() const
-{
- long long elapsed;
- if (mMonotonicKnown) {
- struct timespec ts;
- int rc;
- rc = clock_gettime(CLOCK_MONOTONIC, &ts);
- if (rc) {
- ALOGE("clock_gettime(CLOCK_MONOTONIC) errno=%d", errno);
- elapsed = 0;
- } else {
- // mMonotonicTs is updated only at first enable and resetStatistics
- elapsed = (ts.tv_sec - mMonotonicTs.tv_sec) * 1000000000LL +
- (ts.tv_nsec - mMonotonicTs.tv_nsec);
- }
- } else {
- ALOGW("Can't compute elapsed time because measurements have never been enabled");
- elapsed = 0;
- }
- ALOGV("elapsed %lld", elapsed);
- return elapsed;
-}
-
-void ThreadCpuUsage::resetElapsed()
-{
- ALOGV("resetElapsed");
- if (mMonotonicKnown) {
- int rc;
- rc = clock_gettime(CLOCK_MONOTONIC, &mMonotonicTs);
- if (rc) {
- ALOGE("clock_gettime(CLOCK_MONOTONIC) errno=%d", errno);
- mMonotonicKnown = false;
- }
- }
-}
-
-/*static*/
-int ThreadCpuUsage::sScalingFds[ThreadCpuUsage::MAX_CPU];
-pthread_once_t ThreadCpuUsage::sOnceControl = PTHREAD_ONCE_INIT;
-int ThreadCpuUsage::sKernelMax;
-
-/*static*/
-void ThreadCpuUsage::init()
-{
- // read the number of CPUs
- sKernelMax = 1;
- int fd = open("/sys/devices/system/cpu/kernel_max", O_RDONLY);
- if (fd >= 0) {
-#define KERNEL_MAX_SIZE 12
- char kernelMax[KERNEL_MAX_SIZE];
- ssize_t actual = read(fd, kernelMax, sizeof(kernelMax));
- if (actual >= 2 && kernelMax[actual-1] == '\n') {
- sKernelMax = atoi(kernelMax);
- if (sKernelMax >= MAX_CPU - 1) {
- ALOGW("kernel_max %d but MAX_CPU %d", sKernelMax, MAX_CPU);
- sKernelMax = MAX_CPU;
- } else if (sKernelMax < 0) {
- ALOGW("kernel_max invalid %d", sKernelMax);
- sKernelMax = 1;
- } else {
- ++sKernelMax;
- ALOGV("number of CPUs %d", sKernelMax);
- }
- } else {
- ALOGW("Can't read number of CPUs");
- }
- (void) close(fd);
- } else {
- ALOGW("Can't open number of CPUs");
- }
-
- // open fd to each frequency per CPU
-#define FREQ_SIZE 64
- char freq_path[FREQ_SIZE];
-#define FREQ_DIGIT 27
- COMPILE_TIME_ASSERT_FUNCTION_SCOPE(MAX_CPU <= 10);
- strlcpy(freq_path, "/sys/devices/system/cpu/cpu?/cpufreq/scaling_cur_freq", sizeof(freq_path));
- int i;
- for (i = 0; i < MAX_CPU; ++i) {
- sScalingFds[i] = -1;
- }
- for (i = 0; i < sKernelMax; ++i) {
- freq_path[FREQ_DIGIT] = i + '0';
- fd = open(freq_path, O_RDONLY);
- if (fd >= 0) {
- // keep this fd until process exit
- sScalingFds[i] = fd;
- } else {
- ALOGW("Can't open CPU %d", i);
- }
- }
-}
-
-uint32_t ThreadCpuUsage::getCpukHz(int cpuNum)
-{
- if (cpuNum < 0 || cpuNum >= MAX_CPU) {
- ALOGW("getCpukHz called with invalid CPU %d", cpuNum);
- return 0;
- }
- int fd = sScalingFds[cpuNum];
- if (fd < 0) {
- ALOGW("getCpukHz called for unopened CPU %d", cpuNum);
- return 0;
- }
-#define KHZ_SIZE 12
- char kHz[KHZ_SIZE]; // kHz base 10
- ssize_t actual = pread(fd, kHz, sizeof(kHz), (off_t) 0);
- uint32_t ret;
- if (actual >= 2 && kHz[actual-1] == '\n') {
- ret = atoi(kHz);
- } else {
- ret = 0;
- }
- if (ret != mCurrentkHz[cpuNum]) {
- if (ret > 0) {
- ALOGV("CPU %d frequency %u kHz", cpuNum, ret);
- } else {
- ALOGW("Can't read CPU %d frequency", cpuNum);
- }
- mCurrentkHz[cpuNum] = ret;
- }
- return ret;
-}
-
-} // namespace android
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 118608d..3a3f8a5 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -18,6 +18,8 @@
#include <SkCamera.h>
+#include <private/hwui/DrawGlInfo.h>
+
#include "DisplayListLogBuffer.h"
#include "DisplayListRenderer.h"
#include "Caches.h"
@@ -104,7 +106,7 @@ DisplayList::~DisplayList() {
void DisplayList::initProperties() {
mLeft = 0;
mTop = 0;
- mTop = 0;
+ mRight = 0;
mBottom = 0;
mApplicationScale = -1;
mClipChildren = true;
@@ -119,6 +121,7 @@ void DisplayList::initProperties() {
mScaleY = 1;
mPivotX = 0;
mPivotY = 0;
+ mCameraDistance = 0;
mMatrixDirty = false;
mMatrixFlags = 0;
mPrevWidth = -1;
@@ -684,7 +687,7 @@ void DisplayList::outputViewProperties(OpenGLRenderer& renderer, char* indent) {
mTransformMatrix->get(8));
}
}
- if (mAlpha < 1) {
+ if (mAlpha < 1 && !mCaching) {
// TODO: should be able to store the size of a DL at record time and not
// have to pass it into this call. In fact, this information might be in the
// location/size info that we store with the new native transform data.
@@ -763,42 +766,14 @@ void DisplayList::setViewProperties(OpenGLRenderer& renderer, uint32_t width, ui
}
}
-void DisplayList::transformRect(float left, float top, float right, float bottom, Rect& result) {
- result.left = left + mLeft;
- result.top = top + mTop;
- result.right = right + mLeft;
- result.bottom = bottom + mTop;
- if (mMatrixFlags != 0) {
- if (mMatrixFlags == TRANSLATION) {
- result.left += mTranslationX;
- result.top += mTranslationY;
- result.right += mTranslationX;
- result.bottom += mTranslationY;
- } else {
- updateMatrix();
- SkRect r;
- r.fLeft = result.left;
- r.fTop = result.top;
- r.fRight = result.right;
- r.fBottom = result.bottom;
- mTransformMatrix->mapRect(&r);
- result.left = r.fLeft;
- result.top = r.fTop;
- result.right = r.fRight;
- result.bottom = r.fBottom;
- }
- }
-}
-
-
/**
* Changes to replay(), specifically those involving opcode or parameter changes, should be mimicked
* in the output() function, since that function processes the same list of opcodes for the
* purposes of logging display list info for a given view.
*/
-bool DisplayList::replay(OpenGLRenderer& renderer, uint32_t width,
+status_t DisplayList::replay(OpenGLRenderer& renderer, uint32_t width,
uint32_t height, Rect& dirty, int32_t flags, uint32_t level) {
- bool needsInvalidate = false;
+ status_t drawGlStatus = 0;
TextContainer text;
mReader.rewind();
@@ -820,6 +795,12 @@ bool DisplayList::replay(OpenGLRenderer& renderer, uint32_t width,
restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
}
setViewProperties(renderer, width, height, level);
+ if (USE_DISPLAY_LIST_PROPERTIES && renderer.quickReject(0, 0, width, height)) {
+ DISPLAY_LIST_LOGD("%s%s %d", (char*) indent, "RestoreToCount", restoreTo);
+ renderer.restoreToCount(restoreTo);
+ renderer.endMark();
+ return false;
+ }
DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
int saveCount = renderer.getSaveCount() - 1;
@@ -843,7 +824,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, uint32_t width,
Functor *functor = (Functor *) getInt();
DISPLAY_LIST_LOGD("%s%s %p", (char*) indent, OP_NAMES[op], functor);
renderer.startMark("GL functor");
- needsInvalidate |= renderer.callDrawGLFunction(functor, dirty);
+ drawGlStatus |= renderer.callDrawGLFunction(functor, dirty);
renderer.endMark();
}
break;
@@ -950,7 +931,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, uint32_t width,
int32_t flags = getInt();
DISPLAY_LIST_LOGD("%s%s %p, %dx%d, 0x%x %d", (char*) indent, OP_NAMES[op],
displayList, width, height, flags, level + 1);
- needsInvalidate |= renderer.drawDisplayList(displayList, width,
+ drawGlStatus |= renderer.drawDisplayList(displayList, width,
height, dirty, flags, level + 1);
}
break;
@@ -972,6 +953,9 @@ bool DisplayList::replay(OpenGLRenderer& renderer, uint32_t width,
float x = getFloat();
float y = getFloat();
SkPaint* paint = getPaint(renderer);
+ if (mCaching && mMultipliedAlpha < 255) {
+ paint->setAlpha(mMultipliedAlpha);
+ }
DISPLAY_LIST_LOGD("%s%s %p, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op],
bitmap, x, y, paint);
renderer.drawBitmap(bitmap, x, y, paint);
@@ -1233,8 +1217,8 @@ bool DisplayList::replay(OpenGLRenderer& renderer, uint32_t width,
renderer.endMark();
DISPLAY_LIST_LOGD("%sDone (%p, %s), returning %d", (char*) indent + 2, this, mName.string(),
- needsInvalidate);
- return needsInvalidate;
+ drawGlStatus);
+ return drawGlStatus;
}
///////////////////////////////////////////////////////////////////////////////
@@ -1321,11 +1305,11 @@ void DisplayListRenderer::interrupt() {
void DisplayListRenderer::resume() {
}
-bool DisplayListRenderer::callDrawGLFunction(Functor *functor, Rect& dirty) {
+status_t DisplayListRenderer::callDrawGLFunction(Functor *functor, Rect& dirty) {
// Ignore dirty during recording, it matters only when we replay
addOp(DisplayList::DrawGLFunction);
addInt((int) functor);
- return false; // No invalidate needed at record-time
+ return DrawGlInfo::kStatusDone; // No invalidate needed at record-time
}
int DisplayListRenderer::save(int flags) {
@@ -1415,29 +1399,16 @@ bool DisplayListRenderer::clipRect(float left, float top, float right, float bot
return OpenGLRenderer::clipRect(left, top, right, bottom, op);
}
-bool DisplayListRenderer::drawDisplayList(DisplayList* displayList,
+status_t DisplayListRenderer::drawDisplayList(DisplayList* displayList,
uint32_t width, uint32_t height, Rect& dirty, int32_t flags, uint32_t level) {
// dirty is an out parameter and should not be recorded,
// it matters only when replaying the display list
- float top = 0;
- float left = 0;
- float right = width;
- float bottom = height;
- if (USE_DISPLAY_LIST_PROPERTIES) {
- Rect transformedRect;
- displayList->transformRect(left, top, right, bottom, transformedRect);
- left = transformedRect.left;
- top = transformedRect.top;
- right = transformedRect.right;
- bottom = transformedRect.bottom;
- }
- const bool reject = quickReject(left, top, right, bottom);
- uint32_t* location = addOp(DisplayList::DrawDisplayList, reject);
+
+ addOp(DisplayList::DrawDisplayList);
addDisplayList(displayList);
addSize(width, height);
addInt(flags);
- addSkip(location);
- return false;
+ return DrawGlInfo::kStatusDone;
}
void DisplayListRenderer::drawLayer(Layer* layer, float x, float y, SkPaint* paint) {
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 4bbb04f..4a9886b 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -51,7 +51,7 @@ namespace uirenderer {
// Set to 1 to enable native processing of View properties. 0 by default. Eventually this
// will go away and we will always use this approach for accelerated apps.
-#define USE_DISPLAY_LIST_PROPERTIES 0
+#define USE_DISPLAY_LIST_PROPERTIES 1
#define TRANSLATION 0x0001
#define ROTATION 0x0002
@@ -137,7 +137,7 @@ public:
void initFromDisplayListRenderer(const DisplayListRenderer& recorder, bool reusing = false);
- bool replay(OpenGLRenderer& renderer, uint32_t width, uint32_t height,
+ status_t replay(OpenGLRenderer& renderer, uint32_t width, uint32_t height,
Rect& dirty, int32_t flags, uint32_t level = 0);
void output(OpenGLRenderer& renderer, uint32_t level = 0);
@@ -379,8 +379,6 @@ public:
mCaching = caching;
}
- void transformRect(float left, float top, float right, float bottom, Rect& result);
-
private:
void init();
@@ -525,7 +523,7 @@ public:
virtual void prepareDirty(float left, float top, float right, float bottom, bool opaque);
virtual void finish();
- virtual bool callDrawGLFunction(Functor *functor, Rect& dirty);
+ virtual status_t callDrawGLFunction(Functor *functor, Rect& dirty);
virtual void interrupt();
virtual void resume();
@@ -549,7 +547,7 @@ public:
virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
- virtual bool drawDisplayList(DisplayList* displayList, uint32_t width, uint32_t height,
+ virtual status_t drawDisplayList(DisplayList* displayList, uint32_t width, uint32_t height,
Rect& dirty, int32_t flags, uint32_t level = 0);
virtual void drawLayer(Layer* layer, float x, float y, SkPaint* paint);
virtual void drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index d7937c7..9e7fbb5 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -518,6 +518,8 @@ FontRenderer::~FontRenderer() {
mCacheLines.clear();
if (mInitialized) {
+ // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
+ Caches::getInstance().unbindIndicesBuffer();
glDeleteBuffers(1, &mIndexBufferID);
delete[] mTextMeshPtr;
@@ -777,7 +779,7 @@ void FontRenderer::checkInit() {
// We store a string with letters in a rough frequency of occurrence
mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq ");
mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ");
- mLatinPrecache += String16(",.?!()-+@;:`'");
+ mLatinPrecache += String16(",.?!()-+@;:'");
mLatinPrecache += String16("0123456789");
mInitialized = true;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 685fddc..eb4b83b 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -236,7 +236,7 @@ void OpenGLRenderer::resume() {
glBlendEquation(GL_FUNC_ADD);
}
-bool OpenGLRenderer::callDrawGLFunction(Functor *functor, Rect& dirty) {
+status_t OpenGLRenderer::callDrawGLFunction(Functor *functor, Rect& dirty) {
interrupt();
if (mDirtyClip) {
setScissorFromClip();
@@ -269,7 +269,7 @@ bool OpenGLRenderer::callDrawGLFunction(Functor *functor, Rect& dirty) {
}
resume();
- return result != 0;
+ return result;
}
///////////////////////////////////////////////////////////////////////////////
@@ -1321,21 +1321,10 @@ void OpenGLRenderer::finishDrawTexture() {
// Drawing
///////////////////////////////////////////////////////////////////////////////
-bool OpenGLRenderer::drawDisplayList(DisplayList* displayList, uint32_t width, uint32_t height,
+status_t OpenGLRenderer::drawDisplayList(DisplayList* displayList, uint32_t width, uint32_t height,
Rect& dirty, int32_t flags, uint32_t level) {
- float top = 0;
- float left = 0;
- float right = width;
- float bottom = height;
- if (USE_DISPLAY_LIST_PROPERTIES) {
- Rect transformedRect;
- displayList->transformRect(left, top, right, bottom, transformedRect);
- left = transformedRect.left;
- top = transformedRect.top;
- right = transformedRect.right;
- bottom = transformedRect.bottom;
- }
- if (quickReject(left, top, right, bottom)) {
+
+ if (!USE_DISPLAY_LIST_PROPERTIES && quickReject(0, 0, width, height)) {
return false;
}
@@ -1345,7 +1334,7 @@ bool OpenGLRenderer::drawDisplayList(DisplayList* displayList, uint32_t width, u
return displayList->replay(*this, width, height, dirty, flags, level);
}
- return false;
+ return DrawGlInfo::kStatusDone;
}
void OpenGLRenderer::outputDisplayList(DisplayList* displayList, uint32_t level) {
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 4d7a491..3ba6202 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -72,7 +72,7 @@ public:
virtual void interrupt();
virtual void resume();
- virtual bool callDrawGLFunction(Functor *functor, Rect& dirty);
+ virtual status_t callDrawGLFunction(Functor *functor, Rect& dirty);
ANDROID_API int getSaveCount() const;
virtual int save(int flags);
@@ -97,7 +97,7 @@ public:
ANDROID_API bool quickReject(float left, float top, float right, float bottom);
virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
- virtual bool drawDisplayList(DisplayList* displayList, uint32_t width, uint32_t height,
+ virtual status_t drawDisplayList(DisplayList* displayList, uint32_t width, uint32_t height,
Rect& dirty, int32_t flags, uint32_t level = 0);
virtual void outputDisplayList(DisplayList* displayList, uint32_t level = 0);
virtual void drawLayer(Layer* layer, float x, float y, SkPaint* paint);
diff --git a/libs/rs/driver/rsdGL.cpp b/libs/rs/driver/rsdGL.cpp
index fae602c..0860417 100644
--- a/libs/rs/driver/rsdGL.cpp
+++ b/libs/rs/driver/rsdGL.cpp
@@ -43,6 +43,7 @@
#include "rsdFrameBufferObj.h"
#include <gui/SurfaceTextureClient.h>
+#include <gui/DummyConsumer.h>
using namespace android;
using namespace android::renderscript;
@@ -326,8 +327,11 @@ bool rsdGLInit(const Context *rsc) {
}
gGLContextCount++;
- sp<SurfaceTexture> st(new SurfaceTexture(123));
- sp<SurfaceTextureClient> stc(new SurfaceTextureClient(st));
+ // Create a BufferQueue with a fake consumer
+ sp<BufferQueue> bq = new BufferQueue();
+ sp<DummyConsumer> dummy = new DummyConsumer(bq);
+ sp<SurfaceTextureClient> stc(new SurfaceTextureClient(static_cast<sp<ISurfaceTexture> >(bq)));
+
dc->gl.egl.surfaceDefault = eglCreateWindowSurface(dc->gl.egl.display, dc->gl.egl.config,
static_cast<ANativeWindow*>(stc.get()),
NULL);
diff --git a/libs/rs/driver/rsdProgram.cpp b/libs/rs/driver/rsdProgram.cpp
index 30a4c5f..a96a5f9 100644
--- a/libs/rs/driver/rsdProgram.cpp
+++ b/libs/rs/driver/rsdProgram.cpp
@@ -71,10 +71,10 @@ void rsdProgramVertexDestroy(const Context *rsc, const ProgramVertex *pv) {
if(pv->mHal.drv) {
drv = (RsdShader*)pv->mHal.drv;
if (rsc->props.mLogShaders) {
- ALOGV("Destroying vertex shader with ID %u", drv->getShaderID());
+ ALOGV("Destroying vertex shader with ID %u", (uint32_t)pv);
}
- if (drv->getShaderID()) {
- dc->gl.shaderCache->cleanupVertex(drv->getShaderID());
+ if (drv->getStateBasedIDCount()) {
+ dc->gl.shaderCache->cleanupVertex(drv);
}
delete drv;
}
@@ -105,10 +105,10 @@ void rsdProgramFragmentDestroy(const Context *rsc, const ProgramFragment *pf) {
if(pf->mHal.drv) {
drv = (RsdShader*)pf->mHal.drv;
if (rsc->props.mLogShaders) {
- ALOGV("Destroying fragment shader with ID %u", drv->getShaderID());
+ ALOGV("Destroying fragment shader with ID %u", (uint32_t)pf);
}
- if (drv->getShaderID()) {
- dc->gl.shaderCache->cleanupFragment(drv->getShaderID());
+ if (drv->getStateBasedIDCount()) {
+ dc->gl.shaderCache->cleanupFragment(drv);
}
delete drv;
}
diff --git a/libs/rs/driver/rsdShader.cpp b/libs/rs/driver/rsdShader.cpp
index a386735..6d9fa90 100644
--- a/libs/rs/driver/rsdShader.cpp
+++ b/libs/rs/driver/rsdShader.cpp
@@ -46,30 +46,74 @@ RsdShader::RsdShader(const Program *p, uint32_t type,
}
RsdShader::~RsdShader() {
- if (mShaderID) {
- glDeleteShader(mShaderID);
+ for (uint32_t i = 0; i < mStateBasedShaders.size(); i ++) {
+ StateBasedKey *state = mStateBasedShaders.itemAt(i);
+ if (state->mShaderID) {
+ glDeleteShader(state->mShaderID);
+ }
+ delete state;
}
delete[] mAttribNames;
delete[] mUniformNames;
delete[] mUniformArraySizes;
- delete[] mTextureTargets;
}
void RsdShader::initMemberVars() {
mDirty = true;
- mShaderID = 0;
mAttribCount = 0;
mUniformCount = 0;
mAttribNames = NULL;
mUniformNames = NULL;
mUniformArraySizes = NULL;
- mTextureTargets = NULL;
+ mCurrentState = NULL;
mIsValid = false;
}
+RsdShader::StateBasedKey *RsdShader::getExistingState() {
+ RsdShader::StateBasedKey *returnKey = NULL;
+
+ for (uint32_t i = 0; i < mStateBasedShaders.size(); i ++) {
+ returnKey = mStateBasedShaders.itemAt(i);
+
+ for (uint32_t ct = 0; ct < mRSProgram->mHal.state.texturesCount; ct ++) {
+ uint32_t texType = 0;
+ if (mRSProgram->mHal.state.textureTargets[ct] == RS_TEXTURE_2D) {
+ Allocation *a = mRSProgram->mHal.state.textures[ct];
+ if (a && a->mHal.state.surfaceTextureID) {
+ texType = GL_TEXTURE_EXTERNAL_OES;
+ } else {
+ texType = GL_TEXTURE_2D;
+ }
+ } else {
+ texType = GL_TEXTURE_CUBE_MAP;
+ }
+ if (texType != returnKey->mTextureTargets[ct]) {
+ returnKey = NULL;
+ break;
+ }
+ }
+ }
+ return returnKey;
+}
+
+uint32_t RsdShader::getStateBasedShaderID(const Context *rsc) {
+ StateBasedKey *state = getExistingState();
+ if (state != NULL) {
+ mCurrentState = state;
+ return mCurrentState->mShaderID;
+ }
+ // We have not created a shader for this particular state yet
+ state = new StateBasedKey(mTextureCount);
+ mCurrentState = state;
+ mStateBasedShaders.add(state);
+ createShader();
+ loadShader(rsc);
+ return mCurrentState->mShaderID;
+}
+
void RsdShader::init(const char** textureNames, size_t textureNamesCount,
const size_t *textureNamesLength) {
uint32_t attribCount = 0;
@@ -155,14 +199,14 @@ void RsdShader::appendTextures() {
appendUsing = false;
}
mShader.append("uniform samplerExternalOES UNI_");
- mTextureTargets[ct] = GL_TEXTURE_EXTERNAL_OES;
+ mCurrentState->mTextureTargets[ct] = GL_TEXTURE_EXTERNAL_OES;
} else {
mShader.append("uniform sampler2D UNI_");
- mTextureTargets[ct] = GL_TEXTURE_2D;
+ mCurrentState->mTextureTargets[ct] = GL_TEXTURE_2D;
}
} else {
mShader.append("uniform samplerCube UNI_");
- mTextureTargets[ct] = GL_TEXTURE_CUBE_MAP;
+ mCurrentState->mTextureTargets[ct] = GL_TEXTURE_CUBE_MAP;
}
mShader.append(mTextureNames[ct]);
@@ -171,6 +215,7 @@ void RsdShader::appendTextures() {
}
bool RsdShader::createShader() {
+ mShader.clear();
if (mType == GL_FRAGMENT_SHADER) {
mShader.append("precision mediump float;\n");
}
@@ -183,37 +228,37 @@ bool RsdShader::createShader() {
}
bool RsdShader::loadShader(const Context *rsc) {
- mShaderID = glCreateShader(mType);
- rsAssert(mShaderID);
+ mCurrentState->mShaderID = glCreateShader(mType);
+ rsAssert(mCurrentState->mShaderID);
if(!mShader.length()) {
createShader();
}
if (rsc->props.mLogShaders) {
- ALOGV("Loading shader type %x, ID %i", mType, mShaderID);
+ ALOGV("Loading shader type %x, ID %i", mType, mCurrentState->mShaderID);
ALOGV("%s", mShader.string());
}
- if (mShaderID) {
+ if (mCurrentState->mShaderID) {
const char * ss = mShader.string();
- RSD_CALL_GL(glShaderSource, mShaderID, 1, &ss, NULL);
- RSD_CALL_GL(glCompileShader, mShaderID);
+ RSD_CALL_GL(glShaderSource, mCurrentState->mShaderID, 1, &ss, NULL);
+ RSD_CALL_GL(glCompileShader, mCurrentState->mShaderID);
GLint compiled = 0;
- RSD_CALL_GL(glGetShaderiv, mShaderID, GL_COMPILE_STATUS, &compiled);
+ RSD_CALL_GL(glGetShaderiv, mCurrentState->mShaderID, GL_COMPILE_STATUS, &compiled);
if (!compiled) {
GLint infoLen = 0;
- RSD_CALL_GL(glGetShaderiv, mShaderID, GL_INFO_LOG_LENGTH, &infoLen);
+ RSD_CALL_GL(glGetShaderiv, mCurrentState->mShaderID, GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen) {
char* buf = (char*) malloc(infoLen);
if (buf) {
- RSD_CALL_GL(glGetShaderInfoLog, mShaderID, infoLen, NULL, buf);
+ RSD_CALL_GL(glGetShaderInfoLog, mCurrentState->mShaderID, infoLen, NULL, buf);
rsc->setError(RS_ERROR_FATAL_PROGRAM_LINK, buf);
free(buf);
}
- RSD_CALL_GL(glDeleteShader, mShaderID);
- mShaderID = 0;
+ RSD_CALL_GL(glDeleteShader, mCurrentState->mShaderID);
+ mCurrentState->mShaderID = 0;
return false;
}
}
@@ -430,7 +475,7 @@ void RsdShader::setupTextures(const Context *rsc, RsdShaderCache *sc) {
if (!mRSProgram->mHal.state.textures[ct]) {
// if nothing is bound, reset to default GL texture
- RSD_CALL_GL(glBindTexture, mTextureTargets[ct], 0);
+ RSD_CALL_GL(glBindTexture, mCurrentState->mTextureTargets[ct], 0);
continue;
}
@@ -537,9 +582,6 @@ void RsdShader::initAttribAndUniformArray() {
}
mTextureCount = mRSProgram->mHal.state.texturesCount;
- if (mTextureCount) {
- mTextureTargets = new uint32_t[mTextureCount];
- }
}
void RsdShader::initAddUserElement(const Element *e, String8 *names, uint32_t *arrayLengths,
diff --git a/libs/rs/driver/rsdShader.h b/libs/rs/driver/rsdShader.h
index 6c0b616..2680b3e 100644
--- a/libs/rs/driver/rsdShader.h
+++ b/libs/rs/driver/rsdShader.h
@@ -44,9 +44,13 @@ public:
const size_t *textureNamesLength);
virtual ~RsdShader();
- bool createShader();
+ uint32_t getStateBasedShaderID(const android::renderscript::Context *);
- uint32_t getShaderID() const {return mShaderID;}
+ // Add ability to get all ID's to clean up the cached program objects
+ uint32_t getStateBasedIDCount() const { return mStateBasedShaders.size(); }
+ uint32_t getStateBasedID(uint32_t index) const {
+ return mStateBasedShaders.itemAt(index)->mShaderID;
+ }
uint32_t getAttribCount() const {return mAttribCount;}
uint32_t getUniformCount() const {return mUniformCount;}
@@ -64,6 +68,21 @@ public:
protected:
+ class StateBasedKey {
+ public:
+ StateBasedKey(uint32_t texCount) : mShaderID(0) {
+ mTextureTargets = new uint32_t[texCount];
+ }
+ ~StateBasedKey() {
+ delete[] mTextureTargets;
+ }
+ uint32_t mShaderID;
+ uint32_t *mTextureTargets;
+ };
+
+ bool createShader();
+ StateBasedKey *getExistingState();
+
const android::renderscript::Program *mRSProgram;
bool mIsValid;
@@ -87,11 +106,10 @@ protected:
mutable bool mDirty;
android::String8 mShader;
android::String8 mUserShader;
- uint32_t mShaderID;
uint32_t mType;
uint32_t mTextureCount;
- uint32_t *mTextureTargets;
+ StateBasedKey *mCurrentState;
uint32_t mAttribCount;
uint32_t mUniformCount;
android::String8 *mAttribNames;
@@ -100,6 +118,8 @@ protected:
android::Vector<android::String8> mTextureNames;
+ android::Vector<StateBasedKey*> mStateBasedShaders;
+
int32_t mTextureUniformIndexStart;
void logUniform(const android::renderscript::Element *field,
diff --git a/libs/rs/driver/rsdShaderCache.cpp b/libs/rs/driver/rsdShaderCache.cpp
index 50cb9f9..69b43fc 100644
--- a/libs/rs/driver/rsdShaderCache.cpp
+++ b/libs/rs/driver/rsdShaderCache.cpp
@@ -108,21 +108,17 @@ bool RsdShaderCache::link(const Context *rsc) {
RsdShader *vtx = mVertex;
RsdShader *frag = mFragment;
- if (!vtx->getShaderID()) {
- vtx->loadShader(rsc);
- }
- if (!frag->getShaderID()) {
- frag->loadShader(rsc);
- }
+
+ uint32_t vID = vtx->getStateBasedShaderID(rsc);
+ uint32_t fID = frag->getStateBasedShaderID(rsc);
// Don't try to cache if shaders failed to load
- if (!vtx->getShaderID() || !frag->getShaderID()) {
+ if (!vID || !fID) {
return false;
}
uint32_t entryCount = mEntries.size();
for (uint32_t ct = 0; ct < entryCount; ct ++) {
- if ((mEntries[ct]->vtx == vtx->getShaderID()) &&
- (mEntries[ct]->frag == frag->getShaderID())) {
+ if ((mEntries[ct]->vtx == vID) && (mEntries[ct]->frag == fID)) {
//ALOGV("SC using program %i", mEntries[ct]->program);
glUseProgram(mEntries[ct]->program);
@@ -138,14 +134,14 @@ bool RsdShaderCache::link(const Context *rsc) {
frag->getUniformCount());
mEntries.push(e);
mCurrent = e;
- e->vtx = vtx->getShaderID();
- e->frag = frag->getShaderID();
+ e->vtx = vID;
+ e->frag = fID;
e->program = glCreateProgram();
if (e->program) {
GLuint pgm = e->program;
- glAttachShader(pgm, vtx->getShaderID());
+ glAttachShader(pgm, vID);
//ALOGE("e1 %x", glGetError());
- glAttachShader(pgm, frag->getShaderID());
+ glAttachShader(pgm, fID);
glBindAttribLocation(pgm, 0, "ATTRIB_position");
glBindAttribLocation(pgm, 1, "ATTRIB_color");
@@ -241,30 +237,38 @@ int32_t RsdShaderCache::vtxAttribSlot(const String8 &attrName) const {
return -1;
}
-void RsdShaderCache::cleanupVertex(uint32_t id) {
+void RsdShaderCache::cleanupVertex(RsdShader *s) {
int32_t numEntries = (int32_t)mEntries.size();
- for (int32_t ct = 0; ct < numEntries; ct ++) {
- if (mEntries[ct]->vtx == id) {
- glDeleteProgram(mEntries[ct]->program);
-
- delete mEntries[ct];
- mEntries.removeAt(ct);
- numEntries = (int32_t)mEntries.size();
- ct --;
+ uint32_t numShaderIDs = s->getStateBasedIDCount();
+ for (uint32_t sId = 0; sId < numShaderIDs; sId ++) {
+ uint32_t id = s->getStateBasedID(sId);
+ for (int32_t ct = 0; ct < numEntries; ct ++) {
+ if (mEntries[ct]->vtx == id) {
+ glDeleteProgram(mEntries[ct]->program);
+
+ delete mEntries[ct];
+ mEntries.removeAt(ct);
+ numEntries = (int32_t)mEntries.size();
+ ct --;
+ }
}
}
}
-void RsdShaderCache::cleanupFragment(uint32_t id) {
+void RsdShaderCache::cleanupFragment(RsdShader *s) {
int32_t numEntries = (int32_t)mEntries.size();
- for (int32_t ct = 0; ct < numEntries; ct ++) {
- if (mEntries[ct]->frag == id) {
- glDeleteProgram(mEntries[ct]->program);
-
- delete mEntries[ct];
- mEntries.removeAt(ct);
- numEntries = (int32_t)mEntries.size();
- ct --;
+ uint32_t numShaderIDs = s->getStateBasedIDCount();
+ for (uint32_t sId = 0; sId < numShaderIDs; sId ++) {
+ uint32_t id = s->getStateBasedID(sId);
+ for (int32_t ct = 0; ct < numEntries; ct ++) {
+ if (mEntries[ct]->frag == id) {
+ glDeleteProgram(mEntries[ct]->program);
+
+ delete mEntries[ct];
+ mEntries.removeAt(ct);
+ numEntries = (int32_t)mEntries.size();
+ ct --;
+ }
}
}
}
diff --git a/libs/rs/driver/rsdShaderCache.h b/libs/rs/driver/rsdShaderCache.h
index 1192916..88aa32d 100644
--- a/libs/rs/driver/rsdShaderCache.h
+++ b/libs/rs/driver/rsdShaderCache.h
@@ -49,8 +49,8 @@ public:
bool setup(const android::renderscript::Context *rsc);
- void cleanupVertex(uint32_t id);
- void cleanupFragment(uint32_t id);
+ void cleanupVertex(RsdShader *s);
+ void cleanupFragment(RsdShader *s);
void cleanupAll();
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index bccf1f9..d06e302 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -48,6 +48,7 @@ public class MediaCodec
public static int FLAG_SYNCFRAME = 1;
public static int FLAG_CODECCONFIG = 2;
public static int FLAG_EOS = 4;
+ public static int FLAG_ENCRYPTED = 8;
/** Instantiate a codec component by mime type. For decoder components
this is the mime type of media that this decoder should be able to
@@ -82,6 +83,7 @@ public class MediaCodec
public native final void release();
public static int CONFIGURE_FLAG_ENCODE = 1;
+ public static int CONFIGURE_FLAG_SECURE = 2;
/** Configures a component.
* @param format A map of string/value pairs describing the input format
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 5732c72..9ea3d0e 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -65,6 +65,13 @@ public class MediaExtractor
// Returns the current sample's presentation time in microseconds.
public native long getSampleTime();
+ // Keep these in sync with their equivalents in NuMediaExtractor.h
+ public static final int SAMPLE_FLAG_SYNC = 1;
+ public static final int SAMPLE_FLAG_ENCRYPTED = 2;
+
+ // Returns the current sample's flags.
+ public native int getSampleFlags();
+
private static native final void native_init();
private native final void native_setup(String path);
private native final void native_finalize();
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 0c86fc2..8c661b7 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -160,6 +160,10 @@ status_t JMediaExtractor::getSampleTime(int64_t *sampleTimeUs) {
return mImpl->getSampleTime(sampleTimeUs);
}
+status_t JMediaExtractor::getSampleFlags(uint32_t *sampleFlags) {
+ return mImpl->getSampleFlags(sampleFlags);
+}
+
} // namespace android
////////////////////////////////////////////////////////////////////////////////
@@ -343,6 +347,28 @@ static jlong android_media_MediaExtractor_getSampleTime(
return sampleTimeUs;
}
+static jint android_media_MediaExtractor_getSampleFlags(
+ JNIEnv *env, jobject thiz) {
+ sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+
+ if (extractor == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return -1ll;
+ }
+
+ uint32_t sampleFlags;
+ status_t err = extractor->getSampleFlags(&sampleFlags);
+
+ if (err == ERROR_END_OF_STREAM) {
+ return -1ll;
+ } else if (err != OK) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return false;
+ }
+
+ return sampleFlags;
+}
+
static void android_media_MediaExtractor_native_init(JNIEnv *env) {
jclass clazz = env->FindClass("android/media/MediaExtractor");
CHECK(clazz != NULL);
@@ -412,6 +438,9 @@ static JNINativeMethod gMethods[] = {
{ "getSampleTime", "()J",
(void *)android_media_MediaExtractor_getSampleTime },
+ { "getSampleFlags", "()I",
+ (void *)android_media_MediaExtractor_getSampleFlags },
+
{ "native_init", "()V", (void *)android_media_MediaExtractor_native_init },
{ "native_setup", "(Ljava/lang/String;)V",
diff --git a/media/jni/android_media_MediaExtractor.h b/media/jni/android_media_MediaExtractor.h
index 70e58c6..49a64d6 100644
--- a/media/jni/android_media_MediaExtractor.h
+++ b/media/jni/android_media_MediaExtractor.h
@@ -43,6 +43,7 @@ struct JMediaExtractor : public RefBase {
status_t readSampleData(jobject byteBuf, size_t offset, size_t *sampleSize);
status_t getSampleTrackIndex(size_t *trackIndex);
status_t getSampleTime(int64_t *sampleTimeUs);
+ status_t getSampleFlags(uint32_t *sampleFlags);
protected:
virtual ~JMediaExtractor();
diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp
index 8b2321c..1190448 100644
--- a/media/jni/android_media_Utils.cpp
+++ b/media/jni/android_media_Utils.cpp
@@ -324,7 +324,7 @@ status_t ConvertKeyValueArraysToMessage(
env->DeleteLocalRef(byteArray); byteArray = NULL;
}
- msg->setObject(key.c_str(), buffer);
+ msg->setBuffer(key.c_str(), buffer);
}
}
diff --git a/media/jni/soundpool/Android.mk b/media/jni/soundpool/Android.mk
index 98d6449..70dbfb3 100644
--- a/media/jni/soundpool/Android.mk
+++ b/media/jni/soundpool/Android.mk
@@ -2,14 +2,14 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- android_media_SoundPool.cpp \
- SoundPool.cpp \
- SoundPoolThread.cpp
+ android_media_SoundPool.cpp
+
+LOCAL_C_INCLUDES := \
+ frameworks/base/media/libmedia
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
- libbinder \
libandroid_runtime \
libnativehelper \
libmedia \
diff --git a/media/jni/soundpool/android_media_SoundPool.cpp b/media/jni/soundpool/android_media_SoundPool.cpp
index da3af9d..c6dee06 100644
--- a/media/jni/soundpool/android_media_SoundPool.cpp
+++ b/media/jni/soundpool/android_media_SoundPool.cpp
@@ -23,7 +23,7 @@
#include <nativehelper/jni.h>
#include <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
-#include "SoundPool.h"
+#include <SoundPool.h>
using namespace android;
diff --git a/media/libeffects/visualizer/EffectVisualizer.cpp b/media/libeffects/visualizer/EffectVisualizer.cpp
index 51c8b68..bdb1a1c 100644
--- a/media/libeffects/visualizer/EffectVisualizer.cpp
+++ b/media/libeffects/visualizer/EffectVisualizer.cpp
@@ -21,6 +21,7 @@
#include <stdlib.h>
#include <string.h>
#include <new>
+#include <time.h>
#include <audio_effects/effect_visualizer.h>
@@ -47,9 +48,9 @@ enum visualizer_state_e {
VISUALIZER_STATE_ACTIVE,
};
-// maximum number of reads from same buffer before resetting capture buffer. This means
+// maximum time since last capture buffer update before resetting capture buffer. This means
// that the framework has stopped playing audio and we must start returning silence
-#define MAX_STALL_COUNT 10
+#define MAX_STALL_TIME_MS 1000
struct VisualizerContext {
const struct effect_interface_s *mItfe;
@@ -59,7 +60,7 @@ struct VisualizerContext {
uint8_t mState;
uint8_t mCurrentBuf;
uint8_t mLastBuf;
- uint8_t mStallCount;
+ struct timespec mBufferUpdateTime;
uint8_t mCaptureBuf[2][VISUALIZER_CAPTURE_SIZE_MAX];
};
@@ -72,7 +73,7 @@ void Visualizer_reset(VisualizerContext *pContext)
pContext->mCaptureIdx = 0;
pContext->mCurrentBuf = 0;
pContext->mLastBuf = 1;
- pContext->mStallCount = 0;
+ pContext->mBufferUpdateTime.tv_sec = 0;
memset(pContext->mCaptureBuf[0], 0x80, VISUALIZER_CAPTURE_SIZE_MAX);
memset(pContext->mCaptureBuf[1], 0x80, VISUALIZER_CAPTURE_SIZE_MAX);
}
@@ -321,6 +322,11 @@ int Visualizer_process(
if (pContext->mCaptureIdx == pContext->mCaptureSize) {
pContext->mCurrentBuf ^= 1;
pContext->mCaptureIdx = 0;
+
+ // update last buffer update time stamp
+ if (clock_gettime(CLOCK_MONOTONIC, &pContext->mBufferUpdateTime) < 0) {
+ pContext->mBufferUpdateTime.tv_sec = 0;
+ }
}
if (inBuffer->raw != outBuffer->raw) {
@@ -453,16 +459,25 @@ int Visualizer_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
pContext->mCaptureSize);
// if audio framework has stopped playing audio although the effect is still
// active we must clear the capture buffer to return silence
- if (pContext->mLastBuf == pContext->mCurrentBuf) {
- if (pContext->mStallCount < MAX_STALL_COUNT) {
- if (++pContext->mStallCount == MAX_STALL_COUNT) {
+ if ((pContext->mLastBuf == pContext->mCurrentBuf) &&
+ (pContext->mBufferUpdateTime.tv_sec != 0)) {
+ struct timespec ts;
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
+ time_t secs = ts.tv_sec - pContext->mBufferUpdateTime.tv_sec;
+ long nsec = ts.tv_nsec - pContext->mBufferUpdateTime.tv_nsec;
+ if (nsec < 0) {
+ --secs;
+ nsec += 1000000000;
+ }
+ uint32_t deltaMs = secs * 1000 + nsec / 1000000;
+ if (deltaMs > MAX_STALL_TIME_MS) {
+ ALOGV("capture going to idle");
+ pContext->mBufferUpdateTime.tv_sec = 0;
memset(pContext->mCaptureBuf[pContext->mCurrentBuf ^ 1],
0x80,
pContext->mCaptureSize);
}
}
- } else {
- pContext->mStallCount = 0;
}
pContext->mLastBuf = pContext->mCurrentBuf;
} else {
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index c34e23b..c8e1dc7 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -17,6 +17,7 @@ LOCAL_SRC_FILES:= \
IAudioFlingerClient.cpp \
IAudioTrack.cpp \
IAudioRecord.cpp \
+ ICrypto.cpp \
AudioRecord.cpp \
AudioSystem.cpp \
mediaplayer.cpp \
@@ -43,7 +44,9 @@ LOCAL_SRC_FILES:= \
IEffectClient.cpp \
AudioEffect.cpp \
Visualizer.cpp \
- MemoryLeakTrackUtil.cpp
+ MemoryLeakTrackUtil.cpp \
+ SoundPool.cpp \
+ SoundPoolThread.cpp
LOCAL_SHARED_LIBRARIES := \
libui libcutils libutils libbinder libsonivox libicuuc libexpat \
diff --git a/media/libmedia/ICrypto.cpp b/media/libmedia/ICrypto.cpp
new file mode 100644
index 0000000..827d7af
--- /dev/null
+++ b/media/libmedia/ICrypto.cpp
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ICrypto"
+#include <utils/Log.h>
+
+#include <binder/Parcel.h>
+#include <media/ICrypto.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+enum {
+ INITIALIZE = IBinder::FIRST_CALL_TRANSACTION,
+ TERMINATE,
+ SET_ENTITLEMENT_KEY,
+ SET_ECM,
+ DECRYPT_VIDEO,
+ DECRYPT_AUDIO,
+};
+
+struct BpCrypto : public BpInterface<ICrypto> {
+ BpCrypto(const sp<IBinder> &impl)
+ : BpInterface<ICrypto>(impl) {
+ }
+
+ virtual status_t initialize() {
+ Parcel data, reply;
+ data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
+ remote()->transact(INITIALIZE, data, &reply);
+
+ return reply.readInt32();
+ }
+
+ virtual status_t terminate() {
+ Parcel data, reply;
+ data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
+ remote()->transact(TERMINATE, data, &reply);
+
+ return reply.readInt32();
+ }
+
+ virtual status_t setEntitlementKey(
+ const void *key, size_t keyLength) {
+ Parcel data, reply;
+ data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
+ data.writeInt32(keyLength);
+ data.write(key, keyLength);
+ remote()->transact(SET_ENTITLEMENT_KEY, data, &reply);
+
+ return reply.readInt32();
+ }
+
+ virtual status_t setEntitlementControlMessage(
+ const void *msg, size_t msgLength) {
+ Parcel data, reply;
+ data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
+ data.writeInt32(msgLength);
+ data.write(msg, msgLength);
+ remote()->transact(SET_ECM, data, &reply);
+
+ return reply.readInt32();
+ }
+
+ virtual ssize_t decryptVideo(
+ const void *iv, size_t ivLength,
+ const void *srcData, size_t srcDataSize,
+ void *dstData, size_t dstDataOffset) {
+ Parcel data, reply;
+ data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
+ if (iv == NULL) {
+ if (ivLength > 0) {
+ return -EINVAL;
+ }
+
+ data.writeInt32(-1);
+ } else {
+ data.writeInt32(ivLength);
+ data.write(iv, ivLength);
+ }
+
+ data.writeInt32(srcDataSize);
+ data.write(srcData, srcDataSize);
+
+ data.writeIntPtr((intptr_t)dstData);
+ data.writeInt32(dstDataOffset);
+
+ remote()->transact(DECRYPT_VIDEO, data, &reply);
+
+ return reply.readInt32();
+ }
+
+ virtual ssize_t decryptAudio(
+ const void *iv, size_t ivLength,
+ const void *srcData, size_t srcDataSize,
+ void *dstData, size_t dstDataSize) {
+ Parcel data, reply;
+ data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
+ if (iv == NULL) {
+ if (ivLength > 0) {
+ return -EINVAL;
+ }
+
+ data.writeInt32(-1);
+ } else {
+ data.writeInt32(ivLength);
+ data.write(iv, ivLength);
+ }
+
+ data.writeInt32(srcDataSize);
+ data.write(srcData, srcDataSize);
+ data.writeInt32(dstDataSize);
+
+ remote()->transact(DECRYPT_AUDIO, data, &reply);
+
+ ssize_t res = reply.readInt32();
+
+ if (res <= 0) {
+ return res;
+ }
+
+ reply.read(dstData, res);
+
+ return res;
+ }
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(BpCrypto);
+};
+
+IMPLEMENT_META_INTERFACE(Crypto, "android.hardware.ICrypto");
+
+////////////////////////////////////////////////////////////////////////////////
+
+status_t BnCrypto::onTransact(
+ uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
+ switch (code) {
+ case INITIALIZE:
+ {
+ CHECK_INTERFACE(ICrypto, data, reply);
+ reply->writeInt32(initialize());
+
+ return OK;
+ }
+
+ case TERMINATE:
+ {
+ CHECK_INTERFACE(ICrypto, data, reply);
+ reply->writeInt32(terminate());
+
+ return OK;
+ }
+
+ case SET_ENTITLEMENT_KEY:
+ {
+ CHECK_INTERFACE(ICrypto, data, reply);
+
+ size_t keyLength = data.readInt32();
+ void *key = malloc(keyLength);
+ data.read(key, keyLength);
+
+ reply->writeInt32(setEntitlementKey(key, keyLength));
+
+ free(key);
+ key = NULL;
+
+ return OK;
+ }
+
+ case SET_ECM:
+ {
+ CHECK_INTERFACE(ICrypto, data, reply);
+
+ size_t msgLength = data.readInt32();
+ void *msg = malloc(msgLength);
+ data.read(msg, msgLength);
+
+ reply->writeInt32(setEntitlementControlMessage(msg, msgLength));
+
+ free(msg);
+ msg = NULL;
+
+ return OK;
+ }
+
+ case DECRYPT_VIDEO:
+ {
+ CHECK_INTERFACE(ICrypto, data, reply);
+
+ void *iv = NULL;
+
+ int32_t ivLength = data.readInt32();
+ if (ivLength >= 0) {
+ iv = malloc(ivLength);
+ data.read(iv, ivLength);
+ }
+
+ size_t srcDataSize = data.readInt32();
+ void *srcData = malloc(srcDataSize);
+ data.read(srcData, srcDataSize);
+
+ void *dstData = (void *)data.readIntPtr();
+ size_t dstDataOffset = data.readInt32();
+
+ reply->writeInt32(
+ decryptVideo(
+ iv,
+ ivLength < 0 ? 0 : ivLength,
+ srcData,
+ srcDataSize,
+ dstData,
+ dstDataOffset));
+
+ free(srcData);
+ srcData = NULL;
+
+ if (iv != NULL) {
+ free(iv);
+ iv = NULL;
+ }
+
+ return OK;
+ }
+
+ case DECRYPT_AUDIO:
+ {
+ CHECK_INTERFACE(ICrypto, data, reply);
+
+ void *iv = NULL;
+
+ int32_t ivLength = data.readInt32();
+ if (ivLength >= 0) {
+ iv = malloc(ivLength);
+ data.read(iv, ivLength);
+ }
+
+ size_t srcDataSize = data.readInt32();
+ void *srcData = malloc(srcDataSize);
+ data.read(srcData, srcDataSize);
+
+ size_t dstDataSize = data.readInt32();
+ void *dstData = malloc(dstDataSize);
+
+ ssize_t res =
+ decryptAudio(
+ iv,
+ ivLength < 0 ? 0 : ivLength,
+ srcData,
+ srcDataSize,
+ dstData,
+ dstDataSize);
+
+ reply->writeInt32(res);
+
+ if (res > 0) {
+ reply->write(dstData, res);
+ }
+
+ free(dstData);
+ dstData = NULL;
+
+ free(srcData);
+ srcData = NULL;
+
+ if (iv != NULL) {
+ free(iv);
+ iv = NULL;
+ }
+
+ return OK;
+ }
+
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+} // namespace android
+
diff --git a/media/libmedia/IMediaMetadataRetriever.cpp b/media/libmedia/IMediaMetadataRetriever.cpp
index 9b8d7c3..7e6d54b 100644
--- a/media/libmedia/IMediaMetadataRetriever.cpp
+++ b/media/libmedia/IMediaMetadataRetriever.cpp
@@ -18,7 +18,6 @@
#include <stdint.h>
#include <sys/types.h>
#include <binder/Parcel.h>
-#include <SkBitmap.h>
#include <media/IMediaMetadataRetriever.h>
#include <utils/String8.h>
diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp
index f5fccef..9120617 100644
--- a/media/libmedia/IMediaPlayerService.cpp
+++ b/media/libmedia/IMediaPlayerService.cpp
@@ -20,6 +20,7 @@
#include <binder/Parcel.h>
#include <binder/IMemory.h>
+#include <media/ICrypto.h>
#include <media/IMediaPlayerService.h>
#include <media/IMediaRecorder.h>
#include <media/IOMX.h>
@@ -36,6 +37,7 @@ enum {
CREATE_MEDIA_RECORDER,
CREATE_METADATA_RETRIEVER,
GET_OMX,
+ MAKE_CRYPTO,
ADD_BATTERY_DATA,
PULL_BATTERY_DATA
};
@@ -111,6 +113,13 @@ public:
return interface_cast<IOMX>(reply.readStrongBinder());
}
+ virtual sp<ICrypto> makeCrypto() {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
+ remote()->transact(MAKE_CRYPTO, data, &reply);
+ return interface_cast<ICrypto>(reply.readStrongBinder());
+ }
+
virtual void addBatteryData(uint32_t params) {
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
@@ -191,6 +200,12 @@ status_t BnMediaPlayerService::onTransact(
reply->writeStrongBinder(omx->asBinder());
return NO_ERROR;
} break;
+ case MAKE_CRYPTO: {
+ CHECK_INTERFACE(IMediaPlayerService, data, reply);
+ sp<ICrypto> crypto = makeCrypto();
+ reply->writeStrongBinder(crypto->asBinder());
+ return NO_ERROR;
+ } break;
case ADD_BATTERY_DATA: {
CHECK_INTERFACE(IMediaPlayerService, data, reply);
uint32_t params = data.readInt32();
diff --git a/media/libmedia/JetPlayer.cpp b/media/libmedia/JetPlayer.cpp
index 7fa6bb7..52aee49 100644
--- a/media/libmedia/JetPlayer.cpp
+++ b/media/libmedia/JetPlayer.cpp
@@ -30,7 +30,7 @@ static const int MIX_NUM_BUFFERS = 4;
static const S_EAS_LIB_CONFIG* pLibConfig = NULL;
//-------------------------------------------------------------------------------------------------
-JetPlayer::JetPlayer(jobject javaJetPlayer, int maxTracks, int trackBufferSize) :
+JetPlayer::JetPlayer(void *javaJetPlayer, int maxTracks, int trackBufferSize) :
mEventCallback(NULL),
mJavaJetPlayerRef(javaJetPlayer),
mTid(-1),
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/libmedia/SoundPool.cpp
index 5aed8a1..306c57d 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/libmedia/SoundPool.cpp
@@ -685,7 +685,7 @@ void SoundChannel::nextEvent()
void SoundChannel::callback(int event, void* user, void *info)
{
SoundChannel* channel = static_cast<SoundChannel*>((void *)((unsigned long)user & ~1));
-
+
channel->process(event, info, (unsigned long)user & 1);
}
diff --git a/media/jni/soundpool/SoundPool.h b/media/libmedia/SoundPool.h
index 002b045..002b045 100644
--- a/media/jni/soundpool/SoundPool.h
+++ b/media/libmedia/SoundPool.h
diff --git a/media/jni/soundpool/SoundPoolThread.cpp b/media/libmedia/SoundPoolThread.cpp
index ba3b482..ba3b482 100644
--- a/media/jni/soundpool/SoundPoolThread.cpp
+++ b/media/libmedia/SoundPoolThread.cpp
diff --git a/media/jni/soundpool/SoundPoolThread.h b/media/libmedia/SoundPoolThread.h
index d388388..d388388 100644
--- a/media/jni/soundpool/SoundPoolThread.h
+++ b/media/libmedia/SoundPoolThread.h
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index 4c6e0bd..675c563 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -7,6 +7,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
+ Crypto.cpp \
MediaRecorderClient.cpp \
MediaPlayerService.cpp \
MetadataRetrieverClient.cpp \
@@ -42,7 +43,7 @@ LOCAL_C_INCLUDES := \
$(TOP)/frameworks/base/media/libstagefright/include \
$(TOP)/frameworks/base/media/libstagefright/rtsp \
$(TOP)/frameworks/native/include/media/openmax \
- $(TOP)/external/tremolo/Tremolo
+ $(TOP)/external/tremolo/Tremolo \
LOCAL_MODULE:= libmediaplayerservice
diff --git a/media/libmediaplayerservice/Crypto.cpp b/media/libmediaplayerservice/Crypto.cpp
new file mode 100644
index 0000000..e02035f
--- /dev/null
+++ b/media/libmediaplayerservice/Crypto.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Crypto"
+#include <utils/Log.h>
+
+#include "Crypto.h"
+
+#include <media/stagefright/MediaErrors.h>
+
+namespace android {
+
+Crypto::Crypto() {
+}
+
+Crypto::~Crypto() {
+}
+
+status_t Crypto::initialize() {
+ return ERROR_UNSUPPORTED;
+}
+
+status_t Crypto::terminate() {
+ return ERROR_UNSUPPORTED;
+}
+
+status_t Crypto::setEntitlementKey(
+ const void *key, size_t keyLength) {
+ return ERROR_UNSUPPORTED;
+}
+
+status_t Crypto::setEntitlementControlMessage(
+ const void *msg, size_t msgLength) {
+ return ERROR_UNSUPPORTED;
+}
+
+ssize_t Crypto::decryptVideo(
+ const void *iv, size_t ivLength,
+ const void *srcData, size_t srcDataSize,
+ void *dstData, size_t dstDataOffset) {
+ return ERROR_UNSUPPORTED;
+}
+
+ssize_t Crypto::decryptAudio(
+ const void *iv, size_t ivLength,
+ const void *srcData, size_t srcDataSize,
+ void *dstData, size_t dstDataSize) {
+ return ERROR_UNSUPPORTED;
+}
+
+} // namespace android
diff --git a/media/libmediaplayerservice/Crypto.h b/media/libmediaplayerservice/Crypto.h
new file mode 100644
index 0000000..9855496
--- /dev/null
+++ b/media/libmediaplayerservice/Crypto.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CRYPTO_H_
+
+#define CRYPTO_H_
+
+#include <media/ICrypto.h>
+#include <utils/threads.h>
+
+namespace android {
+
+struct Crypto : public BnCrypto {
+ Crypto();
+
+ virtual status_t initialize();
+ virtual status_t terminate();
+
+ virtual status_t setEntitlementKey(
+ const void *key, size_t keyLength);
+
+ virtual status_t setEntitlementControlMessage(
+ const void *msg, size_t msgLength);
+
+ virtual ssize_t decryptVideo(
+ const void *iv, size_t ivLength,
+ const void *srcData, size_t srcDataSize,
+ void *dstData, size_t dstDataOffset);
+
+ virtual ssize_t decryptAudio(
+ const void *iv, size_t ivLength,
+ const void *srcData, size_t srcDataSize,
+ void *dstData, size_t dstDataSize);
+
+protected:
+ virtual ~Crypto();
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(Crypto);
+};
+
+} // namespace android
+
+#endif // CRYPTO_H_
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 840e475..123d07f 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -70,6 +70,8 @@
#include <OMX.h>
+#include "Crypto.h"
+
namespace android {
sp<MediaPlayerBase> createAAH_TXPlayer();
sp<MediaPlayerBase> createAAH_RXPlayer();
@@ -292,6 +294,16 @@ sp<IOMX> MediaPlayerService::getOMX() {
return mOMX;
}
+sp<ICrypto> MediaPlayerService::makeCrypto() {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mCrypto == NULL) {
+ mCrypto = new Crypto;
+ }
+
+ return mCrypto;
+}
+
status_t MediaPlayerService::AudioCache::dump(int fd, const Vector<String16>& args) const
{
const size_t SIZE = 256;
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index d4e0eb1..b08dd6c 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -240,6 +240,7 @@ public:
virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat);
virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, audio_format_t* pFormat);
virtual sp<IOMX> getOMX();
+ virtual sp<ICrypto> makeCrypto();
virtual status_t dump(int fd, const Vector<String16>& args);
@@ -419,6 +420,7 @@ private:
SortedVector< wp<MediaRecorderClient> > mMediaRecorderClients;
int32_t mNextConnId;
sp<IOMX> mOMX;
+ sp<ICrypto> mCrypto;
};
// ----------------------------------------------------------------------------
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 1600141..5733229 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -125,6 +125,11 @@ sp<AMessage> NuPlayer::Decoder::makeFormat(const sp<MetaData> &meta) {
msg->setInt32("channel-count", numChannels);
msg->setInt32("sample-rate", sampleRate);
+
+ int32_t isADTS;
+ if (meta->findInt32(kKeyIsADTS, &isADTS) && isADTS != 0) {
+ msg->setInt32("is-adts", true);
+ }
}
int32_t maxInputSize;
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index e5ad4b7..db2beda 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -427,24 +427,34 @@ status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) {
sp<IMemory> mem = mDealer[portIndex]->allocate(def.nBufferSize);
CHECK(mem.get() != NULL);
- IOMX::buffer_id buffer;
+ BufferInfo info;
+ info.mStatus = BufferInfo::OWNED_BY_US;
uint32_t requiresAllocateBufferBit =
(portIndex == kPortIndexInput)
? OMXCodec::kRequiresAllocateBufferOnInputPorts
: OMXCodec::kRequiresAllocateBufferOnOutputPorts;
- if (mQuirks & requiresAllocateBufferBit) {
+ if (portIndex == kPortIndexInput && (mFlags & kFlagIsSecure)) {
+ mem.clear();
+
+ void *ptr;
+ err = mOMX->allocateBuffer(
+ mNode, portIndex, def.nBufferSize, &info.mBufferID,
+ &ptr);
+
+ info.mData = new ABuffer(ptr, def.nBufferSize);
+ } else if (mQuirks & requiresAllocateBufferBit) {
err = mOMX->allocateBufferWithBackup(
- mNode, portIndex, mem, &buffer);
+ mNode, portIndex, mem, &info.mBufferID);
} else {
- err = mOMX->useBuffer(mNode, portIndex, mem, &buffer);
+ err = mOMX->useBuffer(mNode, portIndex, mem, &info.mBufferID);
+ }
+
+ if (mem != NULL) {
+ info.mData = new ABuffer(mem->pointer(), def.nBufferSize);
}
- BufferInfo info;
- info.mBufferID = buffer;
- info.mStatus = BufferInfo::OWNED_BY_US;
- info.mData = new ABuffer(mem->pointer(), def.nBufferSize);
mBuffers[portIndex].push(info);
}
}
@@ -840,7 +850,13 @@ status_t ACodec::configureCodec(
|| !msg->findInt32("sample-rate", &sampleRate)) {
err = INVALID_OPERATION;
} else {
- err = setupAACCodec(encoder, numChannels, sampleRate, bitRate);
+ int32_t isADTS;
+ if (!msg->findInt32("is-adts", &isADTS)) {
+ isADTS = 0;
+ }
+
+ err = setupAACCodec(
+ encoder, numChannels, sampleRate, bitRate, isADTS != 0);
}
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)) {
err = setupAMRCodec(encoder, false /* isWAMR */, bitRate);
@@ -934,7 +950,11 @@ status_t ACodec::selectAudioPortFormat(
status_t ACodec::setupAACCodec(
bool encoder,
- int32_t numChannels, int32_t sampleRate, int32_t bitRate) {
+ int32_t numChannels, int32_t sampleRate, int32_t bitRate, bool isADTS) {
+ if (encoder && isADTS) {
+ return -EINVAL;
+ }
+
status_t err = setupRawAudioFormat(
encoder ? kPortIndexInput : kPortIndexOutput,
sampleRate,
@@ -1021,7 +1041,11 @@ status_t ACodec::setupAACCodec(
profile.nChannels = numChannels;
profile.nSampleRate = sampleRate;
- profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4ADTS;
+
+ profile.eAACStreamFormat =
+ isADTS
+ ? OMX_AUDIO_AACStreamFormatMP4ADTS
+ : OMX_AUDIO_AACStreamFormatMP4FF;
return mOMX->setParameter(
mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
@@ -2653,6 +2677,12 @@ bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) {
observer->setNotificationMessage(notify);
mCodec->mComponentName = componentName;
+ mCodec->mFlags = 0;
+
+ if (componentName.endsWith(".secure")) {
+ mCodec->mFlags |= kFlagIsSecure;
+ }
+
mCodec->mQuirks = quirks;
mCodec->mOMX = omx;
mCodec->mNode = node;
@@ -2701,6 +2731,7 @@ void ACodec::LoadedState::onShutdown(bool keepComponentAllocated) {
mCodec->mNode = NULL;
mCodec->mOMX.clear();
mCodec->mQuirks = 0;
+ mCodec->mFlags = 0;
mCodec->mComponentName.clear();
mCodec->changeState(mCodec->mUninitializedState);
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 65a947a..8948abb 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -68,7 +68,6 @@ LOCAL_C_INCLUDES:= \
LOCAL_SHARED_LIBRARIES := \
libbinder \
libcamera_client \
- libchromium_net \
libcrypto \
libcutils \
libdl \
@@ -101,14 +100,18 @@ LOCAL_STATIC_LIBRARIES := \
libstagefright_httplive \
libstagefright_id3 \
libFLAC \
- libstagefright_chromium_http \
+
+ifneq ($(TARGET_BUILD_PDK), true)
+LOCAL_STATIC_LIBRARIES += \
+ libstagefright_chromium_http
+LOCAL_SHARED_LIBRARIES += \
+ libchromium_net
+LOCAL_CPPFLAGS += -DCHROMIUM_AVAILABLE=1
+endif
LOCAL_SHARED_LIBRARIES += libstlport
include external/stlport/libstlport.mk
-# TODO: Chromium is always available, so this flag can be removed.
-LOCAL_CPPFLAGS += -DCHROMIUM_AVAILABLE=1
-
LOCAL_SHARED_LIBRARIES += \
libstagefright_enc_common \
libstagefright_avc_common \
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index a9e7f360..42b5c7e 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -22,10 +22,14 @@
#include "include/SoftwareRenderer.h"
+#include <binder/IServiceManager.h>
#include <gui/SurfaceTextureClient.h>
+#include <media/ICrypto.h>
+#include <media/IMediaPlayerService.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/ACodec.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
@@ -528,6 +532,12 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
info.mOwnedByClient = false;
CHECK(msg->findBuffer(name.c_str(), &info.mData));
+ if (portIndex == kPortIndexInput
+ && (mFlags & kFlagIsSecure)) {
+ info.mEncryptedData =
+ new ABuffer(info.mData->capacity());
+ }
+
buffers->push_back(info);
}
@@ -742,6 +752,59 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
format->setInt32("encoder", true);
}
+ if (flags & CONFIGURE_FLAG_SECURE) {
+ mFlags |= kFlagIsSecure;
+
+ sp<IServiceManager> sm = defaultServiceManager();
+
+ sp<IBinder> binder =
+ sm->getService(String16("media.player"));
+
+ sp<IMediaPlayerService> service =
+ interface_cast<IMediaPlayerService>(binder);
+
+ CHECK(service != NULL);
+
+ mCrypto = service->makeCrypto();
+
+ status_t err = mCrypto->initialize();
+
+ if (err == OK) {
+ sp<ABuffer> emm;
+ if (format->findBuffer("emm", &emm)) {
+ err = mCrypto->setEntitlementKey(
+ emm->data(), emm->size());
+ }
+ }
+
+ if (err == OK) {
+ sp<ABuffer> ecm;
+ if (format->findBuffer("ecm", &ecm)) {
+ CHECK_EQ(ecm->size(), 80u);
+
+ // bytes 16..47 of the original ecm stream data.
+ err = mCrypto->setEntitlementControlMessage(
+ ecm->data() + 16, 32);
+ }
+ }
+
+ if (err != OK) {
+ ALOGE("failed to instantiate crypto service.");
+
+ mCrypto.clear();
+
+ setState(INITIALIZED);
+
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", UNKNOWN_ERROR);
+
+ response->postReply(mReplyID);
+ break;
+ }
+ } else {
+ mFlags &= ~kFlagIsSecure;
+ }
+
mCodec->initiateConfigureComponent(format);
break;
}
@@ -983,7 +1046,10 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
for (size_t i = 0; i < srcBuffers.size(); ++i) {
const BufferInfo &info = srcBuffers.itemAt(i);
- dstBuffers->push_back(info.mData);
+ dstBuffers->push_back(
+ (portIndex == kPortIndexInput
+ && (mFlags & kFlagIsSecure))
+ ? info.mEncryptedData : info.mData);
}
(new AMessage)->postReply(replyID);
@@ -1037,10 +1103,15 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
}
void MediaCodec::setState(State newState) {
- if (newState == UNINITIALIZED) {
+ if (newState == INITIALIZED) {
delete mSoftRenderer;
mSoftRenderer = NULL;
+ if (mCrypto != NULL) {
+ mCrypto->terminate();
+ mCrypto.clear();
+ }
+
mNativeWindow.clear();
mOutputFormat.clear();
@@ -1150,6 +1221,43 @@ status_t MediaCodec::onQueueInputBuffer(const sp<AMessage> &msg) {
info->mData->meta()->setInt32("csd", true);
}
+ if (mFlags & kFlagIsSecure) {
+ uint8_t iv[16];
+ memset(iv, 0, sizeof(iv));
+
+ ssize_t outLength;
+
+ if (mFlags & kFlagIsSoftwareCodec) {
+ outLength = mCrypto->decryptAudio(
+ (flags & BUFFER_FLAG_ENCRYPTED) ? iv : NULL,
+ (flags & BUFFER_FLAG_ENCRYPTED) ? sizeof(iv) : 0,
+ info->mEncryptedData->base() + offset,
+ size,
+ info->mData->base(),
+ info->mData->capacity());
+ } else {
+ outLength = mCrypto->decryptVideo(
+ (flags & BUFFER_FLAG_ENCRYPTED) ? iv : NULL,
+ (flags & BUFFER_FLAG_ENCRYPTED) ? sizeof(iv) : 0,
+ info->mEncryptedData->base() + offset,
+ size,
+ info->mData->base(),
+ 0 /* offset */);
+ }
+
+ if (outLength < 0) {
+ return outLength;
+ }
+
+ if ((size_t)outLength > info->mEncryptedData->capacity()) {
+ return -ERANGE;
+ }
+
+ info->mData->setRange(0, outLength);
+ } else if (flags & BUFFER_FLAG_ENCRYPTED) {
+ return -EINVAL;
+ }
+
reply->setBuffer("buffer", info->mData);
reply->post();
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index afd4763..224ec33 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -107,6 +107,11 @@ status_t NuMediaExtractor::getTrackFormat(
msg->setInt32("channel-count", numChannels);
msg->setInt32("sample-rate", sampleRate);
+
+ int32_t isADTS;
+ if (meta->findInt32(kKeyIsADTS, &isADTS)) {
+ msg->setInt32("is-adts", true);
+ }
}
int32_t maxInputSize;
@@ -232,6 +237,20 @@ status_t NuMediaExtractor::getTrackFormat(
msg->setBuffer("csd-1", buffer);
}
+ if (meta->findData(kKeyEMM, &type, &data, &size)) {
+ sp<ABuffer> emm = new ABuffer(size);
+ memcpy(emm->data(), data, size);
+
+ msg->setBuffer("emm", emm);
+ }
+
+ if (meta->findData(kKeyECM, &type, &data, &size)) {
+ sp<ABuffer> ecm = new ABuffer(size);
+ memcpy(ecm->data(), data, size);
+
+ msg->setBuffer("ecm", ecm);
+ }
+
*format = msg;
return OK;
@@ -267,13 +286,14 @@ status_t NuMediaExtractor::selectTrack(size_t index) {
info->mFinalResult = OK;
info->mSample = NULL;
info->mSampleTimeUs = -1ll;
- info->mFlags = 0;
+ info->mSampleFlags = 0;
+ info->mTrackFlags = 0;
const char *mime;
CHECK(source->getFormat()->findCString(kKeyMIMEType, &mime));
if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
- info->mFlags |= kIsVorbis;
+ info->mTrackFlags |= kIsVorbis;
}
return OK;
@@ -288,6 +308,7 @@ void NuMediaExtractor::releaseTrackSamples() {
info->mSample = NULL;
info->mSampleTimeUs = -1ll;
+ info->mSampleFlags = 0;
}
}
}
@@ -306,6 +327,7 @@ ssize_t NuMediaExtractor::fetchTrackSamples(int64_t seekTimeUs) {
info->mSample->release();
info->mSample = NULL;
info->mSampleTimeUs = -1ll;
+ info->mSampleFlags = 0;
}
} else if (info->mFinalResult != OK) {
continue;
@@ -323,11 +345,25 @@ ssize_t NuMediaExtractor::fetchTrackSamples(int64_t seekTimeUs) {
info->mFinalResult = err;
info->mSampleTimeUs = -1ll;
+ info->mSampleFlags = 0;
continue;
} else {
CHECK(info->mSample != NULL);
CHECK(info->mSample->meta_data()->findInt64(
kKeyTime, &info->mSampleTimeUs));
+
+ info->mSampleFlags = 0;
+
+ int32_t val;
+ if (info->mSample->meta_data()->findInt32(
+ kKeyIsSyncFrame, &val) && val != 0) {
+ info->mSampleFlags |= SAMPLE_FLAG_SYNC;
+ }
+
+ if (info->mSample->meta_data()->findInt32(
+ kKeyScrambling, &val) && val != 0) {
+ info->mSampleFlags |= SAMPLE_FLAG_ENCRYPTED;
+ }
}
}
@@ -371,7 +407,7 @@ status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) {
size_t sampleSize = info->mSample->range_length();
- if (info->mFlags & kIsVorbis) {
+ if (info->mTrackFlags & kIsVorbis) {
// Each sample's data is suffixed by the number of page samples
// or -1 if not available.
sampleSize += sizeof(int32_t);
@@ -387,7 +423,7 @@ status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) {
memcpy((uint8_t *)buffer->data(), src, info->mSample->range_length());
- if (info->mFlags & kIsVorbis) {
+ if (info->mTrackFlags & kIsVorbis) {
int32_t numPageSamples;
if (!info->mSample->meta_data()->findInt32(
kKeyValidSamples, &numPageSamples)) {
@@ -430,4 +466,17 @@ status_t NuMediaExtractor::getSampleTime(int64_t *sampleTimeUs) {
return OK;
}
+status_t NuMediaExtractor::getSampleFlags(uint32_t *sampleFlags) {
+ ssize_t minIndex = fetchTrackSamples();
+
+ if (minIndex < 0) {
+ return ERROR_END_OF_STREAM;
+ }
+
+ TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
+ *sampleFlags = info->mSampleFlags;
+
+ return OK;
+}
+
} // namespace android
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 8b6e9d5..9769f21 100755
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -515,7 +515,12 @@ status_t OMXCodec::configureCodec(const sp<MetaData> &meta) {
CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
- status_t err = setAACFormat(numChannels, sampleRate, bitRate);
+ int32_t isADTS;
+ if (!meta->findInt32(kKeyIsADTS, &isADTS)) {
+ isADTS = false;
+ }
+
+ status_t err = setAACFormat(numChannels, sampleRate, bitRate, isADTS);
if (err != OK) {
CODEC_LOGE("setAACFormat() failed (err = %d)", err);
return err;
@@ -3386,11 +3391,17 @@ void OMXCodec::setAMRFormat(bool isWAMR, int32_t bitRate) {
}
}
-status_t OMXCodec::setAACFormat(int32_t numChannels, int32_t sampleRate, int32_t bitRate) {
- if (numChannels > 2)
+status_t OMXCodec::setAACFormat(
+ int32_t numChannels, int32_t sampleRate, int32_t bitRate, bool isADTS) {
+ if (numChannels > 2) {
ALOGW("Number of channels: (%d) \n", numChannels);
+ }
if (mIsEncoder) {
+ if (isADTS) {
+ return -EINVAL;
+ }
+
//////////////// input port ////////////////////
setRawAudioFormat(kPortIndexInput, sampleRate, numChannels);
@@ -3445,7 +3456,9 @@ status_t OMXCodec::setAACFormat(int32_t numChannels, int32_t sampleRate, int32_t
&profile, sizeof(profile));
if (err != OK) {
- CODEC_LOGE("setParameter('OMX_IndexParamAudioAac') failed (err = %d)", err);
+ CODEC_LOGE("setParameter('OMX_IndexParamAudioAac') failed "
+ "(err = %d)",
+ err);
return err;
}
} else {
@@ -3459,13 +3472,19 @@ status_t OMXCodec::setAACFormat(int32_t numChannels, int32_t sampleRate, int32_t
profile.nChannels = numChannels;
profile.nSampleRate = sampleRate;
- profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4ADTS;
+
+ profile.eAACStreamFormat =
+ isADTS
+ ? OMX_AUDIO_AACStreamFormatMP4ADTS
+ : OMX_AUDIO_AACStreamFormatMP4FF;
err = mOMX->setParameter(
mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
if (err != OK) {
- CODEC_LOGE("setParameter('OMX_IndexParamAudioAac') failed (err = %d)", err);
+ CODEC_LOGE("setParameter('OMX_IndexParamAudioAac') failed "
+ "(err = %d)",
+ err);
return err;
}
}
diff --git a/media/libstagefright/TimedEventQueue.cpp b/media/libstagefright/TimedEventQueue.cpp
index 6d345bb..9df15eb 100644
--- a/media/libstagefright/TimedEventQueue.cpp
+++ b/media/libstagefright/TimedEventQueue.cpp
@@ -31,10 +31,6 @@
#include <media/stagefright/foundation/ADebug.h>
-#ifdef ANDROID_SIMULATOR
-#include <jni.h>
-#endif
-
namespace android {
TimedEventQueue::TimedEventQueue()
@@ -193,27 +189,10 @@ int64_t TimedEventQueue::getRealTimeUs() {
// static
void *TimedEventQueue::ThreadWrapper(void *me) {
-#ifdef ANDROID_SIMULATOR
- // The simulator runs everything as one process, so any
- // Binder calls happen on this thread instead of a thread
- // in another process. We therefore need to make sure that
- // this thread can do calls into interpreted code.
- // On the device this is not an issue because the remote
- // thread will already be set up correctly for this.
- JavaVM *vm;
- int numvms;
- JNI_GetCreatedJavaVMs(&vm, 1, &numvms);
- JNIEnv *env;
- vm->AttachCurrentThread(&env, NULL);
-#endif
-
androidSetThreadPriority(0, ANDROID_PRIORITY_FOREGROUND);
static_cast<TimedEventQueue *>(me)->threadEntry();
-#ifdef ANDROID_SIMULATOR
- vm->DetachCurrentThread();
-#endif
return NULL;
}
diff --git a/media/libstagefright/chromium_http/Android.mk b/media/libstagefright/chromium_http/Android.mk
index 6d5dcfb..d595686 100644
--- a/media/libstagefright/chromium_http/Android.mk
+++ b/media/libstagefright/chromium_http/Android.mk
@@ -1,5 +1,6 @@
LOCAL_PATH:= $(call my-dir)
+ifneq ($(TARGET_BUILD_PDK), true)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
@@ -21,3 +22,4 @@ include external/stlport/libstlport.mk
LOCAL_MODULE:= libstagefright_chromium_http
include $(BUILD_STATIC_LIBRARY)
+endif
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC.cpp b/media/libstagefright/codecs/aacdec/SoftAAC.cpp
index ea6c360..90f96c6 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC.cpp
+++ b/media/libstagefright/codecs/aacdec/SoftAAC.cpp
@@ -23,6 +23,7 @@
#include "pvmp4audiodecoder_api.h"
#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/hexdump.h>
namespace android {
@@ -42,6 +43,7 @@ SoftAAC::SoftAAC(
OMX_COMPONENTTYPE **component)
: SimpleSoftOMXComponent(name, callbacks, appData, component),
mConfig(new tPVMP4AudioDecoderExternal),
+ mIsADTS(false),
mDecoderBuf(NULL),
mInputBufferCount(0),
mUpsamplingFactor(2),
@@ -140,7 +142,12 @@ OMX_ERRORTYPE SoftAAC::internalGetParameter(
aacParams->nAACtools = 0;
aacParams->nAACERtools = 0;
aacParams->eAACProfile = OMX_AUDIO_AACObjectMain;
- aacParams->eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4FF;
+
+ aacParams->eAACStreamFormat =
+ mIsADTS
+ ? OMX_AUDIO_AACStreamFormatMP4ADTS
+ : OMX_AUDIO_AACStreamFormatMP4FF;
+
aacParams->eChannelMode = OMX_AUDIO_ChannelModeStereo;
if (!isConfigured()) {
@@ -215,6 +222,15 @@ OMX_ERRORTYPE SoftAAC::internalSetParameter(
return OMX_ErrorUndefined;
}
+ if (aacParams->eAACStreamFormat == OMX_AUDIO_AACStreamFormatMP4FF) {
+ mIsADTS = false;
+ } else if (aacParams->eAACStreamFormat
+ == OMX_AUDIO_AACStreamFormatMP4ADTS) {
+ mIsADTS = true;
+ } else {
+ return OMX_ErrorUndefined;
+ }
+
return OMX_ErrorNone;
}
@@ -299,8 +315,35 @@ void SoftAAC::onQueueFilled(OMX_U32 portIndex) {
mNumSamplesOutput = 0;
}
- mConfig->pInputBuffer = inHeader->pBuffer + inHeader->nOffset;
- mConfig->inputBufferCurrentLength = inHeader->nFilledLen;
+ if (mIsADTS) {
+ // skip 30 bits, aac_frame_length follows.
+ // ssssssss ssssiiip ppffffPc ccohCCll llllllll lll?????
+
+ const uint8_t *adtsHeader = inHeader->pBuffer + inHeader->nOffset;
+
+ CHECK_GE(inHeader->nFilledLen, 7);
+
+ bool protectionAbsent = (adtsHeader[1] & 1);
+
+ unsigned aac_frame_length =
+ ((adtsHeader[3] & 3) << 11)
+ | (adtsHeader[4] << 3)
+ | (adtsHeader[5] >> 5);
+
+ CHECK_GE(inHeader->nFilledLen, aac_frame_length);
+
+ size_t headerSize = (protectionAbsent ? 7 : 9);
+
+ mConfig->pInputBuffer = (UChar *)adtsHeader + headerSize;
+ mConfig->inputBufferCurrentLength = aac_frame_length - headerSize;
+
+ inHeader->nOffset += headerSize;
+ inHeader->nFilledLen -= headerSize;
+ } else {
+ mConfig->pInputBuffer = inHeader->pBuffer + inHeader->nOffset;
+ mConfig->inputBufferCurrentLength = inHeader->nFilledLen;
+ }
+
mConfig->inputBufferMaxLength = 0;
mConfig->inputBufferUsedLength = 0;
mConfig->remainderBits = 0;
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC.h b/media/libstagefright/codecs/aacdec/SoftAAC.h
index 963fd27..da0b8ed 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC.h
+++ b/media/libstagefright/codecs/aacdec/SoftAAC.h
@@ -49,6 +49,7 @@ private:
};
tPVMP4AudioDecoderExternal *mConfig;
+ bool mIsADTS;
void *mDecoderBuf;
size_t mInputBufferCount;
diff --git a/media/libstagefright/foundation/AString.cpp b/media/libstagefright/foundation/AString.cpp
index 61b76cf..dee786d 100644
--- a/media/libstagefright/foundation/AString.cpp
+++ b/media/libstagefright/foundation/AString.cpp
@@ -310,6 +310,16 @@ bool AString::startsWith(const char *prefix) const {
return !strncmp(mData, prefix, strlen(prefix));
}
+bool AString::endsWith(const char *suffix) const {
+ size_t suffixLen = strlen(suffix);
+
+ if (mSize < suffixLen) {
+ return false;
+ }
+
+ return !strcmp(mData + mSize - suffixLen, suffix);
+}
+
AString StringPrintf(const char *format, ...) {
va_list ap;
va_start(ap, format);
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index d708ba6..e1ac53c 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -117,6 +117,12 @@ status_t AnotherPacketSource::read(
mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs);
+ int32_t scrambling;
+ if (buffer->meta()->findInt32("scrambling", &scrambling)
+ && scrambling != 0) {
+ mediaBuffer->meta_data()->setInt32(kKeyScrambling, scrambling);
+ }
+
*out = mediaBuffer;
return OK;
}
diff --git a/media/mca/Android.mk b/media/mca/Android.mk
new file mode 100644
index 0000000..b1ce91e
--- /dev/null
+++ b/media/mca/Android.mk
@@ -0,0 +1,21 @@
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# Build all native libraries
+#
+include $(call all-subdir-makefiles)
+
+
diff --git a/media/mca/effect/java/android/media/effect/Effect.java b/media/mca/effect/java/android/media/effect/Effect.java
new file mode 100644
index 0000000..b2b4427
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/Effect.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media.effect;
+
+
+/**
+ * <p>Effects are high-performance transformations that can be applied to image frames. These are
+ * passed in the form of OpenGL ES 2.0 texture names. Typical frames could be images loaded from
+ * disk, or frames from the camera or other video streams.</p>
+ *
+ * <p>To create an Effect you must first create an EffectContext. You can obtain an instance of the
+ * context's EffectFactory by calling
+ * {@link android.media.effect.EffectContext#getFactory() getFactory()}. The EffectFactory allows
+ * you to instantiate specific Effects.</p>
+ *
+ * <p>The application is responsible for creating an EGL context, and making it current before
+ * applying an effect. An effect is bound to a single EffectContext, which in turn is bound to a
+ * single EGL context. If your EGL context is destroyed, the EffectContext becomes invalid and any
+ * effects bound to this context can no longer be used.</p>
+ *
+ */
+public abstract class Effect {
+
+ /**
+ * Get the effect name.
+ *
+ * Returns the unique name of the effect, which matches the name used for instantiating this
+ * effect by the EffectFactory.
+ *
+ * @return The name of the effect.
+ */
+ public abstract String getName();
+
+ /**
+ * Apply an effect to GL textures.
+ *
+ * <p>Apply the Effect on the specified input GL texture, and write the result into the
+ * output GL texture. The texture names passed must be valid in the current GL context.</p>
+ *
+ * <p>The input texture must be a valid texture name with the given width and height and must be
+ * bound to a GL_TEXTURE_2D texture image (usually done by calling the glTexImage2D() function).
+ * Multiple mipmap levels may be provided.</p>
+ *
+ * <p>If the output texture has not been bound to a texture image, it will be automatically
+ * bound by the effect as a GL_TEXTURE_2D. It will contain one mipmap level (0), which will have
+ * the same size as the input. No other mipmap levels are defined. If the output texture was
+ * bound already, and its size does not match the input texture size, the result may be clipped
+ * or only partially fill the texture.</p>
+ *
+ * <p>Note, that regardless of whether a texture image was originally provided or not, both the
+ * input and output textures are owned by the caller. That is, the caller is responsible for
+ * calling glDeleteTextures() to deallocate the input and output textures.</p>
+ *
+ * @param inputTexId The GL texture name of a valid and bound input texture.
+ * @param width The width of the input texture in pixels.
+ * @param height The height of the input texture in pixels.
+ * @param outputTexId The GL texture name of the output texture.
+ */
+ public abstract void apply(int inputTexId, int width, int height, int outputTexId);
+
+ /**
+ * Set a filter parameter.
+ *
+ * Consult the effect documentation for a list of supported parameter keys for each effect.
+ *
+ * @param parameterKey The name of the parameter to adjust.
+ * @param value The new value to set the parameter to.
+ * @throws InvalidArgumentException if parameterName is not a recognized name, or the value is
+ * not a valid value for this parameter.
+ */
+ public abstract void setParameter(String parameterKey, Object value);
+
+ /**
+ * Set an effect listener.
+ *
+ * Some effects may report state changes back to the host, if a listener is set. Consult the
+ * individual effect documentation for more details.
+ *
+ * @param listener The listener to receive update callbacks on.
+ */
+ public void setUpdateListener(EffectUpdateListener listener) {
+ }
+
+ /**
+ * Release an effect.
+ *
+ * <p>Releases the effect and any resources associated with it. You may call this if you need to
+ * make sure acquired resources are no longer held by the effect. Releasing an effect makes it
+ * invalid for reuse.</p>
+ *
+ * <p>Note that this method must be called with the EffectContext and EGL context current, as
+ * the effect may release internal GL resources.</p>
+ */
+ public abstract void release();
+}
+
diff --git a/media/mca/effect/java/android/media/effect/EffectContext.java b/media/mca/effect/java/android/media/effect/EffectContext.java
new file mode 100644
index 0000000..ef03229
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/EffectContext.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media.effect;
+
+import android.filterfw.core.CachedFrameManager;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.FilterFactory;
+import android.filterfw.core.GLEnvironment;
+import android.filterfw.core.GLFrame;
+import android.filterfw.core.FrameManager;
+import android.opengl.GLES20;
+
+/**
+ * <p>An EffectContext keeps all necessary state information to run Effects within a Open GL ES 2.0
+ * context.</p>
+ *
+ * <p>Every EffectContext is bound to one GL context. The application is responsible for creating
+ * this EGL context, and making it current before applying any effect. If your EGL context is
+ * destroyed, the EffectContext becomes invalid and any effects bound to this context can no longer
+ * be used. If you switch to another EGL context, you must create a new EffectContext. Each Effect
+ * is bound to a single EffectContext, and can only be executed in that context.</p>
+ */
+public class EffectContext {
+
+ private final int GL_STATE_FBO = 0;
+ private final int GL_STATE_PROGRAM = 1;
+ private final int GL_STATE_ARRAYBUFFER = 2;
+ private final int GL_STATE_COUNT = 3;
+
+ FilterContext mFilterContext;
+
+ private EffectFactory mFactory;
+
+ private int[] mOldState = new int[GL_STATE_COUNT];
+
+ /**
+ * Creates a context within the current GL context.
+ *
+ * <p>Binds the EffectContext to the current OpenGL context. All subsequent calls to the
+ * EffectContext must be made in the GL context that was active during creation.
+ * When you have finished using a context, you must call {@link #release()}. to dispose of all
+ * resources associated with this context.</p>
+ */
+ public static EffectContext createWithCurrentGlContext() {
+ EffectContext result = new EffectContext();
+ result.initInCurrentGlContext();
+ return result;
+ }
+
+ /**
+ * Returns the EffectFactory for this context.
+ *
+ * <p>The EffectFactory returned from this method allows instantiating new effects within this
+ * context.</p>
+ *
+ * @return The EffectFactory instance for this context.
+ */
+ public EffectFactory getFactory() {
+ return mFactory;
+ }
+
+ /**
+ * Releases the context.
+ *
+ * <p>Releases all the resources and effects associated with the EffectContext. This renders the
+ * context and all the effects bound to this context invalid. You must no longer use the context
+ * or any of its bound effects after calling release().</p>
+ *
+ * <p>Note that this method must be called with the proper EGL context made current, as the
+ * EffectContext and its effects may release internal GL resources.</p>
+ */
+ public void release() {
+ mFilterContext.tearDown();
+ mFilterContext = null;
+ }
+
+ private EffectContext() {
+ mFilterContext = new FilterContext();
+ mFilterContext.setFrameManager(new CachedFrameManager());
+ mFactory = new EffectFactory(this);
+ }
+
+ private void initInCurrentGlContext() {
+ if (!GLEnvironment.isAnyContextActive()) {
+ throw new RuntimeException("Attempting to initialize EffectContext with no active "
+ + "GL context!");
+ }
+ GLEnvironment glEnvironment = new GLEnvironment();
+ glEnvironment.initWithCurrentContext();
+ mFilterContext.initGLEnvironment(glEnvironment);
+ }
+
+ final void assertValidGLState() {
+ GLEnvironment glEnv = mFilterContext.getGLEnvironment();
+ if (glEnv == null || !glEnv.isContextActive()) {
+ if (GLEnvironment.isAnyContextActive()) {
+ throw new RuntimeException("Applying effect in wrong GL context!");
+ } else {
+ throw new RuntimeException("Attempting to apply effect without valid GL context!");
+ }
+ }
+ }
+
+ final void saveGLState() {
+ GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING, mOldState, GL_STATE_FBO);
+ GLES20.glGetIntegerv(GLES20.GL_CURRENT_PROGRAM, mOldState, GL_STATE_PROGRAM);
+ GLES20.glGetIntegerv(GLES20.GL_ARRAY_BUFFER_BINDING, mOldState, GL_STATE_ARRAYBUFFER);
+ }
+
+ final void restoreGLState() {
+ GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mOldState[GL_STATE_FBO]);
+ GLES20.glUseProgram(mOldState[GL_STATE_PROGRAM]);
+ GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mOldState[GL_STATE_ARRAYBUFFER]);
+ }
+}
+
diff --git a/media/mca/effect/java/android/media/effect/EffectFactory.java b/media/mca/effect/java/android/media/effect/EffectFactory.java
new file mode 100644
index 0000000..4330279
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/EffectFactory.java
@@ -0,0 +1,517 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media.effect;
+
+import java.lang.reflect.Constructor;
+import java.util.HashMap;
+
+/**
+ * <p>The EffectFactory class defines the list of available Effects, and provides functionality to
+ * inspect and instantiate them. Some effects may not be available on all platforms, so before
+ * creating a certain effect, the application should confirm that the effect is supported on this
+ * platform by calling {@link #isEffectSupported(String)}.</p>
+ */
+public class EffectFactory {
+
+ private EffectContext mEffectContext;
+
+ private final static String[] EFFECT_PACKAGES = {
+ "android.media.effect.effects.", // Default effect package
+ "" // Allows specifying full class path
+ };
+
+ /** List of Effects */
+ /**
+ * <p>Copies the input texture to the output.</p>
+ * <p>Available parameters: None</p>
+ * @hide
+ */
+ public final static String EFFECT_IDENTITY = "IdentityEffect";
+
+ /**
+ * <p>Adjusts the brightness of the image.</p>
+ * <p>Available parameters:</p>
+ * <table>
+ * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
+ * <tr><td><code>brightness</code></td>
+ * <td>The brightness multiplier.</td>
+ * <td>Positive float. 1.0 means no change;
+ larger values will increase brightness.</td>
+ * </tr>
+ * </table>
+ */
+ public final static String EFFECT_BRIGHTNESS =
+ "android.media.effect.effects.BrightnessEffect";
+
+ /**
+ * <p>Adjusts the contrast of the image.</p>
+ * <p>Available parameters:</p>
+ * <table>
+ * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
+ * <tr><td><code>contrast</code></td>
+ * <td>The contrast multiplier.</td>
+ * <td>Float. 1.0 means no change;
+ larger values will increase contrast.</td>
+ * </tr>
+ * </table>
+ */
+ public final static String EFFECT_CONTRAST =
+ "android.media.effect.effects.ContrastEffect";
+
+ /**
+ * <p>Applies a fisheye lens distortion to the image.</p>
+ * <p>Available parameters:</p>
+ * <table>
+ * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
+ * <tr><td><code>scale</code></td>
+ * <td>The scale of the distortion.</td>
+ * <td>Float, between 0 and 1. Zero means no distortion.</td>
+ * </tr>
+ * </table>
+ */
+ public final static String EFFECT_FISHEYE =
+ "android.media.effect.effects.FisheyeEffect";
+
+ /**
+ * <p>Replaces the background of the input frames with frames from a
+ * selected video. Requires an initial learning period with only the
+ * background visible before the effect becomes active. The effect will wait
+ * until it does not see any motion in the scene before learning the
+ * background and starting the effect.</p>
+ *
+ * <p>Available parameters:</p>
+ * <table>
+ * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
+ * <tr><td><code>source</code></td>
+ * <td>A URI for the background video to use. This parameter must be
+ * supplied before calling apply() for the first time.</td>
+ * <td>String, such as from
+ * {@link android.net.Uri#toString Uri.toString()}</td>
+ * </tr>
+ * </table>
+ *
+ * <p>If the update listener is set for this effect using
+ * {@link Effect#setUpdateListener}, it will be called when the effect has
+ * finished learning the background, with a null value for the info
+ * parameter.</p>
+ */
+ public final static String EFFECT_BACKDROPPER =
+ "android.media.effect.effects.BackDropperEffect";
+
+ /**
+ * <p>Attempts to auto-fix the image based on histogram equalization.</p>
+ * <p>Available parameters:</p>
+ * <table>
+ * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
+ * <tr><td><code>scale</code></td>
+ * <td>The scale of the adjustment.</td>
+ * <td>Float, between 0 and 1. Zero means no adjustment, while 1 indicates the maximum
+ * amount of adjustment.</td>
+ * </tr>
+ * </table>
+ */
+ public final static String EFFECT_AUTOFIX =
+ "android.media.effect.effects.AutoFixEffect";
+
+ /**
+ * <p>Adjusts the range of minimal and maximal color pixel intensities.</p>
+ * <p>Available parameters:</p>
+ * <table>
+ * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
+ * <tr><td><code>black</code></td>
+ * <td>The value of the minimal pixel.</td>
+ * <td>Float, between 0 and 1.</td>
+ * </tr>
+ * <tr><td><code>white</code></td>
+ * <td>The value of the maximal pixel.</td>
+ * <td>Float, between 0 and 1.</td>
+ * </tr>
+ * </table>
+ */
+ public final static String EFFECT_BLACKWHITE =
+ "android.media.effect.effects.BlackWhiteEffect";
+
+ /**
+ * <p>Crops an upright rectangular area from the image. If the crop region falls outside of
+ * the image bounds, the results are undefined.</p>
+ * <p>Available parameters:</p>
+ * <table>
+ * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
+ * <tr><td><code>xorigin</code></td>
+ * <td>The origin's x-value.</td>
+ * <td>Integer, between 0 and width of the image.</td>
+ * </tr>
+ * <tr><td><code>yorigin</code></td>
+ * <td>The origin's y-value.</td>
+ * <td>Integer, between 0 and height of the image.</td>
+ * </tr>
+ * <tr><td><code>width</code></td>
+ * <td>The width of the cropped image.</td>
+ * <td>Integer, between 1 and the width of the image minus xorigin.</td>
+ * </tr>
+ * <tr><td><code>height</code></td>
+ * <td>The height of the cropped image.</td>
+ * <td>Integer, between 1 and the height of the image minus yorigin.</td>
+ * </tr>
+ * </table>
+ */
+ public final static String EFFECT_CROP =
+ "android.media.effect.effects.CropEffect";
+
+ /**
+ * <p>Applies a cross process effect on image, in which the red and green channels are
+ * enhanced while the blue channel is restricted.</p>
+ * <p>Available parameters: None</p>
+ */
+ public final static String EFFECT_CROSSPROCESS =
+ "android.media.effect.effects.CrossProcessEffect";
+
+ /**
+ * <p>Applies black and white documentary style effect on image..</p>
+ * <p>Available parameters: None</p>
+ */
+ public final static String EFFECT_DOCUMENTARY =
+ "android.media.effect.effects.DocumentaryEffect";
+
+
+ /**
+ * <p>Overlays a bitmap (with premultiplied alpha channel) onto the input image. The bitmap
+ * is stretched to fit the input image.</p>
+ * <p>Available parameters:</p>
+ * <table>
+ * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
+ * <tr><td><code>bitmap</code></td>
+ * <td>The overlay bitmap.</td>
+ * <td>A non-null Bitmap instance.</td>
+ * </tr>
+ * </table>
+ */
+ public final static String EFFECT_BITMAPOVERLAY =
+ "android.media.effect.effects.BitmapOverlayEffect";
+
+ /**
+ * <p>Representation of photo using only two color tones.</p>
+ * <p>Available parameters:</p>
+ * <table>
+ * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
+ * <tr><td><code>first_color</code></td>
+ * <td>The first color tone.</td>
+ * <td>Integer, representing an ARGB color with 8 bits per channel. May be created using
+ * {@link android.graphics.Color Color} class.</td>
+ * </tr>
+ * <tr><td><code>second_color</code></td>
+ * <td>The second color tone.</td>
+ * <td>Integer, representing an ARGB color with 8 bits per channel. May be created using
+ * {@link android.graphics.Color Color} class.</td>
+ * </tr>
+ * </table>
+ */
+ public final static String EFFECT_DUOTONE =
+ "android.media.effect.effects.DuotoneEffect";
+
+ /**
+ * <p>Applies back-light filling to the image.</p>
+ * <p>Available parameters:</p>
+ * <table>
+ * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
+ * <tr><td><code>strength</code></td>
+ * <td>The strength of the backlight.</td>
+ * <td>Float, between 0 and 1. Zero means no change.</td>
+ * </tr>
+ * </table>
+ */
+ public final static String EFFECT_FILLLIGHT =
+ "android.media.effect.effects.FillLightEffect";
+
+ /**
+ * <p>Flips image vertically and/or horizontally.</p>
+ * <p>Available parameters:</p>
+ * <table>
+ * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
+ * <tr><td><code>vertical</code></td>
+ * <td>Whether to flip image vertically.</td>
+ * <td>Boolean</td>
+ * </tr>
+ * <tr><td><code>horizontal</code></td>
+ * <td>Whether to flip image horizontally.</td>
+ * <td>Boolean</td>
+ * </tr>
+ * </table>
+ */
+ public final static String EFFECT_FLIP =
+ "android.media.effect.effects.FlipEffect";
+
+ /**
+ * <p>Applies film grain effect to image.</p>
+ * <p>Available parameters:</p>
+ * <table>
+ * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
+ * <tr><td><code>strength</code></td>
+ * <td>The strength of the grain effect.</td>
+ * <td>Float, between 0 and 1. Zero means no change.</td>
+ * </tr>
+ * </table>
+ */
+ public final static String EFFECT_GRAIN =
+ "android.media.effect.effects.GrainEffect";
+
+ /**
+ * <p>Converts image to grayscale.</p>
+ * <p>Available parameters: None</p>
+ */
+ public final static String EFFECT_GRAYSCALE =
+ "android.media.effect.effects.GrayscaleEffect";
+
+ /**
+ * <p>Applies lomo-camera style effect to image.</p>
+ * <p>Available parameters: None</p>
+ */
+ public final static String EFFECT_LOMOISH =
+ "android.media.effect.effects.LomoishEffect";
+
+ /**
+ * <p>Inverts the image colors.</p>
+ * <p>Available parameters: None</p>
+ */
+ public final static String EFFECT_NEGATIVE =
+ "android.media.effect.effects.NegativeEffect";
+
+ /**
+ * <p>Applies posterization effect to image.</p>
+ * <p>Available parameters: None</p>
+ */
+ public final static String EFFECT_POSTERIZE =
+ "android.media.effect.effects.PosterizeEffect";
+
+ /**
+ * <p>Removes red eyes on specified region.</p>
+ * <p>Available parameters:</p>
+ * <table>
+ * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
+ * <tr><td><code>centers</code></td>
+ * <td>Multiple center points (x, y) of the red eye regions.</td>
+ * <td>An array of floats, where (f[2*i], f[2*i+1]) specifies the center of the i'th eye.
+ * Coordinate values are expected to be normalized between 0 and 1.</td>
+ * </tr>
+ * </table>
+ */
+ public final static String EFFECT_REDEYE =
+ "android.media.effect.effects.RedEyeEffect";
+
+ /**
+ * <p>Rotates the image. The output frame size must be able to fit the rotated version of
+ * the input image. Note that the rotation snaps to a the closest multiple of 90 degrees.</p>
+ * <p>Available parameters:</p>
+ * <table>
+ * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
+ * <tr><td><code>angle</code></td>
+ * <td>The angle of rotation in degrees.</td>
+ * <td>Integer value. This will be rounded to the nearest multiple of 90.</td>
+ * </tr>
+ * </table>
+ */
+ public final static String EFFECT_ROTATE =
+ "android.media.effect.effects.RotateEffect";
+
+ /**
+ * <p>Adjusts color saturation of image.</p>
+ * <p>Available parameters:</p>
+ * <table>
+ * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
+ * <tr><td><code>scale</code></td>
+ * <td>The scale of color saturation.</td>
+ * <td>Float, between -1 and 1. 0 means no change, while -1 indicates full desaturation,
+ * i.e. grayscale.</td>
+ * </tr>
+ * </table>
+ */
+ public final static String EFFECT_SATURATE =
+ "android.media.effect.effects.SaturateEffect";
+
+ /**
+ * <p>Converts image to sepia tone.</p>
+ * <p>Available parameters: None</p>
+ */
+ public final static String EFFECT_SEPIA =
+ "android.media.effect.effects.SepiaEffect";
+
+ /**
+ * <p>Sharpens the image.</p>
+ * <p>Available parameters:</p>
+ * <table>
+ * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
+ * <tr><td><code>scale</code></td>
+ * <td>The degree of sharpening.</td>
+ * <td>Float, between 0 and 1. 0 means no change.</td>
+ * </tr>
+ * </table>
+ */
+ public final static String EFFECT_SHARPEN =
+ "android.media.effect.effects.SharpenEffect";
+
+ /**
+ * <p>Rotates the image according to the specified angle, and crops the image so that no
+ * non-image portions are visible.</p>
+ * <p>Available parameters:</p>
+ * <table>
+ * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
+ * <tr><td><code>angle</code></td>
+ * <td>The angle of rotation.</td>
+ * <td>Float, between -45 and +45.</td>
+ * </tr>
+ * </table>
+ */
+ public final static String EFFECT_STRAIGHTEN =
+ "android.media.effect.effects.StraightenEffect";
+
+ /**
+ * <p>Adjusts color temperature of the image.</p>
+ * <p>Available parameters:</p>
+ * <table>
+ * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
+ * <tr><td><code>scale</code></td>
+ * <td>The value of color temperature.</td>
+ * <td>Float, between 0 and 1, with 0 indicating cool, and 1 indicating warm. A value of
+ * of 0.5 indicates no change.</td>
+ * </tr>
+ * </table>
+ */
+ public final static String EFFECT_TEMPERATURE =
+ "android.media.effect.effects.ColorTemperatureEffect";
+
+ /**
+ * <p>Tints the photo with specified color.</p>
+ * <p>Available parameters:</p>
+ * <table>
+ * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
+ * <tr><td><code>tint</code></td>
+ * <td>The color of the tint.</td>
+ * <td>Integer, representing an ARGB color with 8 bits per channel. May be created using
+ * {@link android.graphics.Color Color} class.</td>
+ * </tr>
+ * </table>
+ */
+ public final static String EFFECT_TINT =
+ "android.media.effect.effects.TintEffect";
+
+ /**
+ * <p>Adds a vignette effect to image, i.e. fades away the outer image edges.</p>
+ * <p>Available parameters:</p>
+ * <table>
+ * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr>
+ * <tr><td><code>scale</code></td>
+ * <td>The scale of vignetting.</td>
+ * <td>Float, between 0 and 1. 0 means no change.</td>
+ * </tr>
+ * </table>
+ */
+ public final static String EFFECT_VIGNETTE =
+ "android.media.effect.effects.VignetteEffect";
+
+ EffectFactory(EffectContext effectContext) {
+ mEffectContext = effectContext;
+ }
+
+ /**
+ * Instantiate a new effect with the given effect name.
+ *
+ * <p>The effect's parameters will be set to their default values.</p>
+ *
+ * <p>Note that the EGL context associated with the current EffectContext need not be made
+ * current when creating an effect. This allows the host application to instantiate effects
+ * before any EGL context has become current.</p>
+ *
+ * @param effectName The name of the effect to create.
+ * @return A new Effect instance.
+ * @throws IllegalArgumentException if the effect with the specified name is not supported or
+ * not known.
+ */
+ public Effect createEffect(String effectName) {
+ Class effectClass = getEffectClassByName(effectName);
+ if (effectClass == null) {
+ throw new IllegalArgumentException("Cannot instantiate unknown effect '" +
+ effectName + "'!");
+ }
+ return instantiateEffect(effectClass, effectName);
+ }
+
+ /**
+ * Check if an effect is supported on this platform.
+ *
+ * <p>Some effects may only be available on certain platforms. Use this method before
+ * instantiating an effect to make sure it is supported.</p>
+ *
+ * @param effectName The name of the effect.
+ * @return true, if the effect is supported on this platform.
+ * @throws IllegalArgumentException if the effect name is not known.
+ */
+ public static boolean isEffectSupported(String effectName) {
+ return getEffectClassByName(effectName) != null;
+ }
+
+ private static Class getEffectClassByName(String className) {
+ Class effectClass = null;
+
+ // Get context's classloader; otherwise cannot load non-framework effects
+ ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+
+ // Look for the class in the imported packages
+ for (String packageName : EFFECT_PACKAGES) {
+ try {
+ effectClass = contextClassLoader.loadClass(packageName + className);
+ } catch (ClassNotFoundException e) {
+ continue;
+ }
+ // Exit loop if class was found.
+ if (effectClass != null) {
+ break;
+ }
+ }
+ return effectClass;
+ }
+
+ private Effect instantiateEffect(Class effectClass, String name) {
+ // Make sure this is an Effect subclass
+ try {
+ effectClass.asSubclass(Effect.class);
+ } catch (ClassCastException e) {
+ throw new IllegalArgumentException("Attempting to allocate effect '" + effectClass
+ + "' which is not a subclass of Effect!", e);
+ }
+
+ // Look for the correct constructor
+ Constructor effectConstructor = null;
+ try {
+ effectConstructor = effectClass.getConstructor(EffectContext.class, String.class);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException("The effect class '" + effectClass + "' does not have "
+ + "the required constructor.", e);
+ }
+
+ // Construct the effect
+ Effect effect = null;
+ try {
+ effect = (Effect)effectConstructor.newInstance(mEffectContext, name);
+ } catch (Throwable t) {
+ throw new RuntimeException("There was an error constructing the effect '" + effectClass
+ + "'!", t);
+ }
+
+ return effect;
+ }
+}
diff --git a/media/mca/effect/java/android/media/effect/EffectUpdateListener.java b/media/mca/effect/java/android/media/effect/EffectUpdateListener.java
new file mode 100644
index 0000000..155fe49
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/EffectUpdateListener.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media.effect;
+
+/**
+ * Some effects may issue callbacks to inform the host of changes to the effect state. This is the
+ * listener interface for receiving those callbacks.
+ */
+public interface EffectUpdateListener {
+
+ /**
+ * Called when the effect state is updated.
+ *
+ * @param effect The effect that has been updated.
+ * @param info A value that gives more information about the update. See the effect's
+ * documentation for more details on what this object is.
+ */
+ public void onEffectUpdated(Effect effect, Object info);
+
+}
+
diff --git a/media/mca/effect/java/android/media/effect/FilterEffect.java b/media/mca/effect/java/android/media/effect/FilterEffect.java
new file mode 100644
index 0000000..d7c319e
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/FilterEffect.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media.effect;
+
+import android.filterfw.core.CachedFrameManager;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.FilterFactory;
+import android.filterfw.core.GLEnvironment;
+import android.filterfw.core.GLFrame;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.FrameManager;
+import android.filterfw.format.ImageFormat;
+
+/**
+ * The FilterEffect class is the base class for all Effects based on Filters from the Mobile
+ * Filter Framework (MFF).
+ * @hide
+ */
+public abstract class FilterEffect extends Effect {
+
+ protected EffectContext mEffectContext;
+ private String mName;
+
+ /**
+ * Protected constructor as FilterEffects should be created by Factory.
+ */
+ protected FilterEffect(EffectContext context, String name) {
+ mEffectContext = context;
+ mName = name;
+ }
+
+ /**
+ * Get the effect name.
+ *
+ * Returns the unique name of the effect, which matches the name used for instantiating this
+ * effect by the EffectFactory.
+ *
+ * @return The name of the effect.
+ */
+ @Override
+ public String getName() {
+ return mName;
+ }
+
+ // Helper Methods for subclasses ///////////////////////////////////////////////////////////////
+ /**
+ * Call this before manipulating the GL context. Will assert that the GL environment is in a
+ * valid state, and save it.
+ */
+ protected void beginGLEffect() {
+ mEffectContext.assertValidGLState();
+ mEffectContext.saveGLState();
+ }
+
+ /**
+ * Call this after manipulating the GL context. Restores the previous GL state.
+ */
+ protected void endGLEffect() {
+ mEffectContext.restoreGLState();
+ }
+
+ /**
+ * Returns the active filter context for this effect.
+ */
+ protected FilterContext getFilterContext() {
+ return mEffectContext.mFilterContext;
+ }
+
+ /**
+ * Converts a texture into a Frame.
+ */
+ protected Frame frameFromTexture(int texId, int width, int height) {
+ FrameManager manager = getFilterContext().getFrameManager();
+ FrameFormat format = ImageFormat.create(width, height,
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ Frame frame = manager.newBoundFrame(format,
+ GLFrame.EXISTING_TEXTURE_BINDING,
+ texId);
+ frame.setTimestamp(Frame.TIMESTAMP_UNKNOWN);
+ return frame;
+ }
+
+}
+
diff --git a/media/mca/effect/java/android/media/effect/FilterGraphEffect.java b/media/mca/effect/java/android/media/effect/FilterGraphEffect.java
new file mode 100644
index 0000000..b18bea8
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/FilterGraphEffect.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.effect;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterGraph;
+import android.filterfw.core.GraphRunner;
+import android.filterfw.core.SimpleScheduler;
+import android.filterfw.core.SyncRunner;
+import android.media.effect.Effect;
+import android.media.effect.FilterEffect;
+import android.media.effect.EffectContext;
+import android.filterfw.io.GraphIOException;
+import android.filterfw.io.GraphReader;
+import android.filterfw.io.TextGraphReader;
+
+import android.util.Log;
+
+/**
+ * Effect subclass for effects based on a single Filter. Subclasses need only invoke the
+ * constructor with the correct arguments to obtain an Effect implementation.
+ *
+ * @hide
+ */
+public class FilterGraphEffect extends FilterEffect {
+
+ private static final String TAG = "FilterGraphEffect";
+
+ protected String mInputName;
+ protected String mOutputName;
+ protected GraphRunner mRunner;
+ protected FilterGraph mGraph;
+ protected Class mSchedulerClass;
+
+ /**
+ * Constructs a new FilterGraphEffect.
+ *
+ * @param name The name of this effect (used to create it in the EffectFactory).
+ * @param graphString The graph string to create the graph.
+ * @param inputName The name of the input GLTextureSource filter.
+ * @param outputName The name of the output GLTextureSource filter.
+ */
+ public FilterGraphEffect(EffectContext context,
+ String name,
+ String graphString,
+ String inputName,
+ String outputName,
+ Class scheduler) {
+ super(context, name);
+
+ mInputName = inputName;
+ mOutputName = outputName;
+ mSchedulerClass = scheduler;
+ createGraph(graphString);
+
+ }
+
+ private void createGraph(String graphString) {
+ GraphReader reader = new TextGraphReader();
+ try {
+ mGraph = reader.readGraphString(graphString);
+ } catch (GraphIOException e) {
+ throw new RuntimeException("Could not setup effect", e);
+ }
+
+ if (mGraph == null) {
+ throw new RuntimeException("Could not setup effect");
+ }
+ mRunner = new SyncRunner(getFilterContext(), mGraph, mSchedulerClass);
+ }
+
+ @Override
+ public void apply(int inputTexId, int width, int height, int outputTexId) {
+ beginGLEffect();
+ Filter src = mGraph.getFilter(mInputName);
+ if (src != null) {
+ src.setInputValue("texId", inputTexId);
+ src.setInputValue("width", width);
+ src.setInputValue("height", height);
+ } else {
+ throw new RuntimeException("Internal error applying effect");
+ }
+ Filter dest = mGraph.getFilter(mOutputName);
+ if (dest != null) {
+ dest.setInputValue("texId", outputTexId);
+ } else {
+ throw new RuntimeException("Internal error applying effect");
+ }
+ try {
+ mRunner.run();
+ } catch (RuntimeException e) {
+ throw new RuntimeException("Internal error applying effect: ", e);
+ }
+ endGLEffect();
+ }
+
+ @Override
+ public void setParameter(String parameterKey, Object value) {
+ }
+
+ @Override
+ public void release() {
+ mGraph.tearDown(getFilterContext());
+ mGraph = null;
+ }
+}
diff --git a/media/mca/effect/java/android/media/effect/SingleFilterEffect.java b/media/mca/effect/java/android/media/effect/SingleFilterEffect.java
new file mode 100644
index 0000000..6f85861
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/SingleFilterEffect.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media.effect;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterFactory;
+import android.filterfw.core.FilterFunction;
+import android.filterfw.core.Frame;
+import android.media.effect.Effect;
+import android.media.effect.EffectContext;
+
+import android.util.Log;
+
+/**
+ * Effect subclass for effects based on a single Filter. Subclasses need only invoke the
+ * constructor with the correct arguments to obtain an Effect implementation.
+ *
+ * @hide
+ */
+public class SingleFilterEffect extends FilterEffect {
+
+ protected FilterFunction mFunction;
+ protected String mInputName;
+ protected String mOutputName;
+
+ /**
+ * Constructs a new FilterFunctionEffect.
+ *
+ * @param name The name of this effect (used to create it in the EffectFactory).
+ * @param filterClass The class of the filter to wrap.
+ * @param inputName The name of the input image port.
+ * @param outputName The name of the output image port.
+ * @param finalParameters Key-value pairs of final input port assignments.
+ */
+ public SingleFilterEffect(EffectContext context,
+ String name,
+ Class filterClass,
+ String inputName,
+ String outputName,
+ Object... finalParameters) {
+ super(context, name);
+
+ mInputName = inputName;
+ mOutputName = outputName;
+
+ String filterName = filterClass.getSimpleName();
+ FilterFactory factory = FilterFactory.sharedFactory();
+ Filter filter = factory.createFilterByClass(filterClass, filterName);
+ filter.initWithAssignmentList(finalParameters);
+
+ mFunction = new FilterFunction(getFilterContext(), filter);
+ }
+
+ @Override
+ public void apply(int inputTexId, int width, int height, int outputTexId) {
+ beginGLEffect();
+
+ Frame inputFrame = frameFromTexture(inputTexId, width, height);
+ Frame outputFrame = frameFromTexture(outputTexId, width, height);
+
+ Frame resultFrame = mFunction.executeWithArgList(mInputName, inputFrame);
+
+ outputFrame.setDataFromFrame(resultFrame);
+
+ inputFrame.release();
+ outputFrame.release();
+ resultFrame.release();
+
+ endGLEffect();
+ }
+
+ @Override
+ public void setParameter(String parameterKey, Object value) {
+ mFunction.setInputValue(parameterKey, value);
+ }
+
+ @Override
+ public void release() {
+ mFunction.tearDown();
+ mFunction = null;
+ }
+}
+
diff --git a/media/mca/effect/java/android/media/effect/SizeChangeEffect.java b/media/mca/effect/java/android/media/effect/SizeChangeEffect.java
new file mode 100644
index 0000000..4d27bae
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/SizeChangeEffect.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.effect;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterFactory;
+import android.filterfw.core.FilterFunction;
+import android.filterfw.core.Frame;
+import android.media.effect.Effect;
+import android.media.effect.EffectContext;
+
+import android.util.Log;
+
+/**
+ * Effect subclass for effects based on a single Filter with output size differnet
+ * from input. Subclasses need only invoke the constructor with the correct arguments
+ * to obtain an Effect implementation.
+ *
+ * @hide
+ */
+public class SizeChangeEffect extends SingleFilterEffect {
+
+ public SizeChangeEffect(EffectContext context,
+ String name,
+ Class filterClass,
+ String inputName,
+ String outputName,
+ Object... finalParameters) {
+ super(context, name, filterClass, inputName, outputName, finalParameters);
+ }
+
+ @Override
+ public void apply(int inputTexId, int width, int height, int outputTexId) {
+ beginGLEffect();
+
+ Frame inputFrame = frameFromTexture(inputTexId, width, height);
+ Frame resultFrame = mFunction.executeWithArgList(mInputName, inputFrame);
+
+ int outputWidth = resultFrame.getFormat().getWidth();
+ int outputHeight = resultFrame.getFormat().getHeight();
+
+ Frame outputFrame = frameFromTexture(outputTexId, outputWidth, outputHeight);
+ outputFrame.setDataFromFrame(resultFrame);
+
+ inputFrame.release();
+ outputFrame.release();
+ resultFrame.release();
+
+ endGLEffect();
+ }
+}
diff --git a/core/java/android/webkit/FindListener.java b/media/mca/effect/java/android/media/effect/effects/AutoFixEffect.java
index 124f737..44a141b 100644
--- a/core/java/android/webkit/FindListener.java
+++ b/media/mca/effect/java/android/media/effect/effects/AutoFixEffect.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,19 +14,18 @@
* limitations under the License.
*/
-package android.webkit;
+
+package android.media.effect.effects;
+
+import android.media.effect.EffectContext;
+import android.media.effect.SingleFilterEffect;
+import android.filterpacks.imageproc.AutoFixFilter;
/**
* @hide
*/
-public interface FindListener {
- /**
- * Notify the host application that a find result is available.
- *
- * @param numberOfMatches How many matches have been found
- * @param activeMatchOrdinal The ordinal of the currently selected match
- * @param isDoneCounting Whether we have finished counting matches
- */
- public void onFindResultReceived(int numberOfMatches,
- int activeMatchOrdinal, boolean isDoneCounting);
+public class AutoFixEffect extends SingleFilterEffect {
+ public AutoFixEffect(EffectContext context, String name) {
+ super(context, name, AutoFixFilter.class, "image", "image");
+ }
}
diff --git a/media/mca/effect/java/android/media/effect/effects/BackDropperEffect.java b/media/mca/effect/java/android/media/effect/effects/BackDropperEffect.java
new file mode 100644
index 0000000..d5c7aaa
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/effects/BackDropperEffect.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.effect.effects;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.OneShotScheduler;
+import android.media.effect.EffectContext;
+import android.media.effect.FilterGraphEffect;
+import android.media.effect.EffectUpdateListener;
+
+import android.filterpacks.videoproc.BackDropperFilter;
+import android.filterpacks.videoproc.BackDropperFilter.LearningDoneListener;
+
+/**
+ * Background replacement Effect.
+ *
+ * Replaces the background of the input video stream with a selected video
+ * Learns the background when it first starts up;
+ * needs unobstructed view of background when this happens.
+ *
+ * Effect parameters:
+ * source: A URI for the background video
+ * Listener: Called when learning period is complete
+ *
+ * @hide
+ */
+public class BackDropperEffect extends FilterGraphEffect {
+ private static final String mGraphDefinition =
+ "@import android.filterpacks.base;\n" +
+ "@import android.filterpacks.videoproc;\n" +
+ "@import android.filterpacks.videosrc;\n" +
+ "\n" +
+ "@filter GLTextureSource foreground {\n" +
+ " texId = 0;\n" + // Will be set by base class
+ " width = 0;\n" +
+ " height = 0;\n" +
+ " repeatFrame = true;\n" +
+ "}\n" +
+ "\n" +
+ "@filter MediaSource background {\n" +
+ " sourceUrl = \"no_file_specified\";\n" +
+ " waitForNewFrame = false;\n" +
+ " sourceIsUrl = true;\n" +
+ "}\n" +
+ "\n" +
+ "@filter BackDropperFilter replacer {\n" +
+ " autowbToggle = 1;\n" +
+ "}\n" +
+ "\n" +
+ "@filter GLTextureTarget output {\n" +
+ " texId = 0;\n" +
+ "}\n" +
+ "\n" +
+ "@connect foreground[frame] => replacer[video];\n" +
+ "@connect background[video] => replacer[background];\n" +
+ "@connect replacer[video] => output[frame];\n";
+
+ private EffectUpdateListener mEffectListener = null;
+
+ private LearningDoneListener mLearningListener = new LearningDoneListener() {
+ public void onLearningDone(BackDropperFilter filter) {
+ if (mEffectListener != null) {
+ mEffectListener.onEffectUpdated(BackDropperEffect.this, null);
+ }
+ }
+ };
+
+ public BackDropperEffect(EffectContext context, String name) {
+ super(context, name, mGraphDefinition, "foreground", "output", OneShotScheduler.class);
+
+ Filter replacer = mGraph.getFilter("replacer");
+ replacer.setInputValue("learningDoneListener", mLearningListener);
+ }
+
+ @Override
+ public void setParameter(String parameterKey, Object value) {
+ if (parameterKey.equals("source")) {
+ Filter background = mGraph.getFilter("background");
+ background.setInputValue("sourceUrl", value);
+ }
+ }
+
+ @Override
+ public void setUpdateListener(EffectUpdateListener listener) {
+ mEffectListener = listener;
+ }
+
+} \ No newline at end of file
diff --git a/media/mca/effect/java/android/media/effect/effects/BitmapOverlayEffect.java b/media/mca/effect/java/android/media/effect/effects/BitmapOverlayEffect.java
new file mode 100644
index 0000000..43f461c
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/effects/BitmapOverlayEffect.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package android.media.effect.effects;
+
+import android.media.effect.EffectContext;
+import android.media.effect.SingleFilterEffect;
+import android.filterpacks.imageproc.BitmapOverlayFilter;
+
+/**
+ * @hide
+ */
+public class BitmapOverlayEffect extends SingleFilterEffect {
+ public BitmapOverlayEffect(EffectContext context, String name) {
+ super(context, name, BitmapOverlayFilter.class, "image", "image");
+ }
+}
diff --git a/media/mca/effect/java/android/media/effect/effects/BlackWhiteEffect.java b/media/mca/effect/java/android/media/effect/effects/BlackWhiteEffect.java
new file mode 100644
index 0000000..771afff
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/effects/BlackWhiteEffect.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media.effect.effects;
+
+import android.media.effect.EffectContext;
+import android.media.effect.SingleFilterEffect;
+import android.filterpacks.imageproc.BlackWhiteFilter;
+
+/**
+ * @hide
+ */
+public class BlackWhiteEffect extends SingleFilterEffect {
+ public BlackWhiteEffect(EffectContext context, String name) {
+ super(context, name, BlackWhiteFilter.class, "image", "image");
+ }
+}
diff --git a/media/mca/effect/java/android/media/effect/effects/BrightnessEffect.java b/media/mca/effect/java/android/media/effect/effects/BrightnessEffect.java
new file mode 100644
index 0000000..774e72f
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/effects/BrightnessEffect.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media.effect.effects;
+
+import android.media.effect.EffectContext;
+import android.media.effect.SingleFilterEffect;
+import android.filterpacks.imageproc.BrightnessFilter;
+
+/**
+ * @hide
+ */
+public class BrightnessEffect extends SingleFilterEffect {
+ public BrightnessEffect(EffectContext context, String name) {
+ super(context, name, BrightnessFilter.class, "image", "image");
+ }
+}
+
diff --git a/media/mca/effect/java/android/media/effect/effects/ColorTemperatureEffect.java b/media/mca/effect/java/android/media/effect/effects/ColorTemperatureEffect.java
new file mode 100644
index 0000000..62d98ce
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/effects/ColorTemperatureEffect.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media.effect.effects;
+
+import android.media.effect.EffectContext;
+import android.media.effect.SingleFilterEffect;
+import android.filterpacks.imageproc.ColorTemperatureFilter;
+
+/**
+ * @hide
+ */
+public class ColorTemperatureEffect extends SingleFilterEffect {
+ public ColorTemperatureEffect(EffectContext context, String name) {
+ super(context, name, ColorTemperatureFilter.class, "image", "image");
+ }
+}
diff --git a/media/mca/effect/java/android/media/effect/effects/ContrastEffect.java b/media/mca/effect/java/android/media/effect/effects/ContrastEffect.java
new file mode 100644
index 0000000..d5bfc21
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/effects/ContrastEffect.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media.effect.effects;
+
+import android.media.effect.EffectContext;
+import android.media.effect.SingleFilterEffect;
+import android.filterpacks.imageproc.ContrastFilter;
+
+/**
+ * @hide
+ */
+public class ContrastEffect extends SingleFilterEffect {
+ public ContrastEffect(EffectContext context, String name) {
+ super(context, name, ContrastFilter.class, "image", "image");
+ }
+}
+
diff --git a/media/mca/effect/java/android/media/effect/effects/CropEffect.java b/media/mca/effect/java/android/media/effect/effects/CropEffect.java
new file mode 100644
index 0000000..3e8d78a
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/effects/CropEffect.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media.effect.effects;
+
+import android.media.effect.EffectContext;
+import android.media.effect.SizeChangeEffect;
+import android.media.effect.SingleFilterEffect;
+import android.filterpacks.imageproc.CropRectFilter;
+
+/**
+ * @hide
+ */
+//public class CropEffect extends SingleFilterEffect {
+public class CropEffect extends SizeChangeEffect {
+ public CropEffect(EffectContext context, String name) {
+ super(context, name, CropRectFilter.class, "image", "image");
+ }
+}
diff --git a/media/mca/effect/java/android/media/effect/effects/CrossProcessEffect.java b/media/mca/effect/java/android/media/effect/effects/CrossProcessEffect.java
new file mode 100644
index 0000000..d7a7df5
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/effects/CrossProcessEffect.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media.effect.effects;
+
+import android.media.effect.EffectContext;
+import android.media.effect.SingleFilterEffect;
+import android.filterpacks.imageproc.CrossProcessFilter;
+
+/**
+ * @hide
+ */
+public class CrossProcessEffect extends SingleFilterEffect {
+ public CrossProcessEffect(EffectContext context, String name) {
+ super(context, name, CrossProcessFilter.class, "image", "image");
+ }
+}
diff --git a/media/mca/effect/java/android/media/effect/effects/DocumentaryEffect.java b/media/mca/effect/java/android/media/effect/effects/DocumentaryEffect.java
new file mode 100644
index 0000000..1a5ea35
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/effects/DocumentaryEffect.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.effect.effects;
+
+import android.media.effect.EffectContext;
+import android.media.effect.SingleFilterEffect;
+import android.filterpacks.imageproc.DocumentaryFilter;
+
+/**
+ * @hide
+ */
+public class DocumentaryEffect extends SingleFilterEffect {
+ public DocumentaryEffect(EffectContext context, String name) {
+ super(context, name, DocumentaryFilter.class, "image", "image");
+ }
+}
diff --git a/media/mca/effect/java/android/media/effect/effects/DuotoneEffect.java b/media/mca/effect/java/android/media/effect/effects/DuotoneEffect.java
new file mode 100644
index 0000000..1391b1f
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/effects/DuotoneEffect.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media.effect.effects;
+
+import android.media.effect.EffectContext;
+import android.media.effect.SingleFilterEffect;
+import android.filterpacks.imageproc.DuotoneFilter;
+
+/**
+ * @hide
+ */
+public class DuotoneEffect extends SingleFilterEffect {
+ public DuotoneEffect(EffectContext context, String name) {
+ super(context, name, DuotoneFilter.class, "image", "image");
+ }
+}
diff --git a/media/mca/effect/java/android/media/effect/effects/FillLightEffect.java b/media/mca/effect/java/android/media/effect/effects/FillLightEffect.java
new file mode 100644
index 0000000..5260de3
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/effects/FillLightEffect.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media.effect.effects;
+
+import android.media.effect.EffectContext;
+import android.media.effect.SingleFilterEffect;
+import android.filterpacks.imageproc.FillLightFilter;
+
+/**
+ * @hide
+ */
+public class FillLightEffect extends SingleFilterEffect {
+ public FillLightEffect(EffectContext context, String name) {
+ super(context, name, FillLightFilter.class, "image", "image");
+ }
+}
diff --git a/media/mca/effect/java/android/media/effect/effects/FisheyeEffect.java b/media/mca/effect/java/android/media/effect/effects/FisheyeEffect.java
new file mode 100644
index 0000000..6abfe42
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/effects/FisheyeEffect.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media.effect.effects;
+
+import android.media.effect.EffectContext;
+import android.media.effect.SingleFilterEffect;
+import android.filterpacks.imageproc.FisheyeFilter;
+
+/**
+ * @hide
+ */
+public class FisheyeEffect extends SingleFilterEffect {
+ public FisheyeEffect(EffectContext context, String name) {
+ super(context, name, FisheyeFilter.class, "image", "image");
+ }
+}
+
diff --git a/media/mca/effect/java/android/media/effect/effects/FlipEffect.java b/media/mca/effect/java/android/media/effect/effects/FlipEffect.java
new file mode 100644
index 0000000..0f5c421
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/effects/FlipEffect.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media.effect.effects;
+
+import android.media.effect.EffectContext;
+import android.media.effect.SingleFilterEffect;
+import android.filterpacks.imageproc.FlipFilter;
+
+/**
+ * @hide
+ */
+public class FlipEffect extends SingleFilterEffect {
+ public FlipEffect(EffectContext context, String name) {
+ super(context, name, FlipFilter.class, "image", "image");
+ }
+}
diff --git a/media/mca/effect/java/android/media/effect/effects/GrainEffect.java b/media/mca/effect/java/android/media/effect/effects/GrainEffect.java
new file mode 100644
index 0000000..2fda7e9
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/effects/GrainEffect.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media.effect.effects;
+
+import android.media.effect.EffectContext;
+import android.media.effect.SingleFilterEffect;
+import android.filterpacks.imageproc.GrainFilter;
+
+/**
+ * @hide
+ */
+public class GrainEffect extends SingleFilterEffect {
+ public GrainEffect(EffectContext context, String name) {
+ super(context, name, GrainFilter.class, "image", "image");
+ }
+}
diff --git a/media/mca/effect/java/android/media/effect/effects/GrayscaleEffect.java b/media/mca/effect/java/android/media/effect/effects/GrayscaleEffect.java
new file mode 100644
index 0000000..26ca081
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/effects/GrayscaleEffect.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media.effect.effects;
+
+import android.media.effect.EffectContext;
+import android.media.effect.SingleFilterEffect;
+import android.filterpacks.imageproc.ToGrayFilter;
+
+/**
+ * @hide
+ */
+public class GrayscaleEffect extends SingleFilterEffect {
+ public GrayscaleEffect(EffectContext context, String name) {
+ super(context, name, ToGrayFilter.class, "image", "image");
+ }
+}
diff --git a/media/mca/effect/java/android/media/effect/effects/IdentityEffect.java b/media/mca/effect/java/android/media/effect/effects/IdentityEffect.java
new file mode 100644
index 0000000..d07779e
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/effects/IdentityEffect.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media.effect.effects;
+
+import android.filterfw.core.Frame;
+import android.media.effect.EffectContext;
+import android.media.effect.FilterEffect;
+
+/**
+ * @hide
+ */
+public class IdentityEffect extends FilterEffect {
+
+ public IdentityEffect(EffectContext context, String name) {
+ super(context, name);
+ }
+
+ @Override
+ public void apply(int inputTexId, int width, int height, int outputTexId) {
+ beginGLEffect();
+
+ Frame inputFrame = frameFromTexture(inputTexId, width, height);
+ Frame outputFrame = frameFromTexture(outputTexId, width, height);
+
+ outputFrame.setDataFromFrame(inputFrame);
+
+ inputFrame.release();
+ outputFrame.release();
+
+ endGLEffect();
+ }
+
+ @Override
+ public void setParameter(String parameterKey, Object value) {
+ throw new IllegalArgumentException("Unknown parameter " + parameterKey
+ + " for IdentityEffect!");
+ }
+
+ @Override
+ public void release() {
+ }
+}
+
diff --git a/media/mca/effect/java/android/media/effect/effects/LomoishEffect.java b/media/mca/effect/java/android/media/effect/effects/LomoishEffect.java
new file mode 100644
index 0000000..776e53c
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/effects/LomoishEffect.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.effect.effects;
+
+import android.media.effect.EffectContext;
+import android.media.effect.SingleFilterEffect;
+import android.filterpacks.imageproc.LomoishFilter;
+
+/**
+ * @hide
+ */
+public class LomoishEffect extends SingleFilterEffect {
+ public LomoishEffect(EffectContext context, String name) {
+ super(context, name, LomoishFilter.class, "image", "image");
+ }
+}
diff --git a/media/mca/effect/java/android/media/effect/effects/NegativeEffect.java b/media/mca/effect/java/android/media/effect/effects/NegativeEffect.java
new file mode 100644
index 0000000..29fc94a
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/effects/NegativeEffect.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media.effect.effects;
+
+import android.media.effect.EffectContext;
+import android.media.effect.SingleFilterEffect;
+import android.filterpacks.imageproc.NegativeFilter;
+
+/**
+ * @hide
+ */
+public class NegativeEffect extends SingleFilterEffect {
+ public NegativeEffect(EffectContext context, String name) {
+ super(context, name, NegativeFilter.class, "image", "image");
+ }
+}
diff --git a/media/mca/effect/java/android/media/effect/effects/PosterizeEffect.java b/media/mca/effect/java/android/media/effect/effects/PosterizeEffect.java
new file mode 100644
index 0000000..20a8a37
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/effects/PosterizeEffect.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media.effect.effects;
+
+import android.media.effect.EffectContext;
+import android.media.effect.SingleFilterEffect;
+import android.filterpacks.imageproc.PosterizeFilter;
+
+/**
+ * @hide
+ */
+public class PosterizeEffect extends SingleFilterEffect {
+ public PosterizeEffect(EffectContext context, String name) {
+ super(context, name, PosterizeFilter.class, "image", "image");
+ }
+}
diff --git a/media/mca/effect/java/android/media/effect/effects/RedEyeEffect.java b/media/mca/effect/java/android/media/effect/effects/RedEyeEffect.java
new file mode 100644
index 0000000..8ed9909
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/effects/RedEyeEffect.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+package android.media.effect.effects;
+
+import android.media.effect.EffectContext;
+import android.media.effect.SingleFilterEffect;
+import android.filterpacks.imageproc.RedEyeFilter;
+
+/**
+ * @hide
+ */
+public class RedEyeEffect extends SingleFilterEffect {
+ public RedEyeEffect(EffectContext context, String name) {
+ super(context, name, RedEyeFilter.class, "image", "image");
+ }
+}
diff --git a/media/mca/effect/java/android/media/effect/effects/RotateEffect.java b/media/mca/effect/java/android/media/effect/effects/RotateEffect.java
new file mode 100644
index 0000000..2340015
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/effects/RotateEffect.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media.effect.effects;
+
+import android.media.effect.EffectContext;
+import android.media.effect.SizeChangeEffect;
+import android.filterpacks.imageproc.RotateFilter;
+
+/**
+ * @hide
+ */
+public class RotateEffect extends SizeChangeEffect {
+ public RotateEffect(EffectContext context, String name) {
+ super(context, name, RotateFilter.class, "image", "image");
+ }
+}
diff --git a/media/mca/effect/java/android/media/effect/effects/SaturateEffect.java b/media/mca/effect/java/android/media/effect/effects/SaturateEffect.java
new file mode 100644
index 0000000..fe9250a
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/effects/SaturateEffect.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media.effect.effects;
+
+import android.media.effect.EffectContext;
+import android.media.effect.SingleFilterEffect;
+import android.filterpacks.imageproc.SaturateFilter;
+
+/**
+ * @hide
+ */
+public class SaturateEffect extends SingleFilterEffect {
+ public SaturateEffect(EffectContext context, String name) {
+ super(context, name, SaturateFilter.class, "image", "image");
+ }
+}
diff --git a/media/mca/effect/java/android/media/effect/effects/SepiaEffect.java b/media/mca/effect/java/android/media/effect/effects/SepiaEffect.java
new file mode 100644
index 0000000..de85b2d
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/effects/SepiaEffect.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media.effect.effects;
+
+import android.media.effect.EffectContext;
+import android.media.effect.SingleFilterEffect;
+import android.filterpacks.imageproc.SepiaFilter;
+
+/**
+ * @hide
+ */
+public class SepiaEffect extends SingleFilterEffect {
+ public SepiaEffect(EffectContext context, String name) {
+ super(context, name, SepiaFilter.class, "image", "image");
+ }
+}
diff --git a/media/mca/effect/java/android/media/effect/effects/SharpenEffect.java b/media/mca/effect/java/android/media/effect/effects/SharpenEffect.java
new file mode 100644
index 0000000..46776eb
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/effects/SharpenEffect.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media.effect.effects;
+
+import android.media.effect.EffectContext;
+import android.media.effect.SingleFilterEffect;
+import android.filterpacks.imageproc.SharpenFilter;
+
+/**
+ * @hide
+ */
+public class SharpenEffect extends SingleFilterEffect {
+ public SharpenEffect(EffectContext context, String name) {
+ super(context, name, SharpenFilter.class, "image", "image");
+ }
+}
diff --git a/media/mca/effect/java/android/media/effect/effects/StraightenEffect.java b/media/mca/effect/java/android/media/effect/effects/StraightenEffect.java
new file mode 100644
index 0000000..49253a0
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/effects/StraightenEffect.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media.effect.effects;
+
+import android.media.effect.EffectContext;
+import android.media.effect.SingleFilterEffect;
+import android.filterpacks.imageproc.StraightenFilter;
+
+/**
+ * @hide
+ */
+public class StraightenEffect extends SingleFilterEffect {
+ public StraightenEffect(EffectContext context, String name) {
+ super(context, name, StraightenFilter.class, "image", "image");
+ }
+}
diff --git a/media/mca/effect/java/android/media/effect/effects/TintEffect.java b/media/mca/effect/java/android/media/effect/effects/TintEffect.java
new file mode 100644
index 0000000..6de9ea8
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/effects/TintEffect.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media.effect.effects;
+
+import android.media.effect.EffectContext;
+import android.media.effect.SingleFilterEffect;
+import android.filterpacks.imageproc.TintFilter;
+
+/**
+ * @hide
+ */
+public class TintEffect extends SingleFilterEffect {
+ public TintEffect(EffectContext context, String name) {
+ super(context, name, TintFilter.class, "image", "image");
+ }
+}
diff --git a/media/mca/effect/java/android/media/effect/effects/VignetteEffect.java b/media/mca/effect/java/android/media/effect/effects/VignetteEffect.java
new file mode 100644
index 0000000..b143d77
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/effects/VignetteEffect.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media.effect.effects;
+
+import android.media.effect.EffectContext;
+import android.media.effect.SingleFilterEffect;
+import android.filterpacks.imageproc.VignetteFilter;
+
+/**
+ * @hide
+ */
+public class VignetteEffect extends SingleFilterEffect {
+ public VignetteEffect(EffectContext context, String name) {
+ super(context, name, VignetteFilter.class, "image", "image");
+ }
+}
diff --git a/media/mca/effect/java/android/media/effect/package-info.java b/media/mca/effect/java/android/media/effect/package-info.java
new file mode 100644
index 0000000..b2c14ff
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/package-info.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media.effect;
+
+/**
+ * <h1>Effect Framework</h1>
+ *
+ * This package includes a collection of high-performance visual effects that make use of the
+ * mobile filter framework subsystem.
+ *
+ * TODO: More Documentation
+ *
+ */
diff --git a/media/mca/effect/java/android/media/effect/package.html b/media/mca/effect/java/android/media/effect/package.html
new file mode 100644
index 0000000..8a210fd
--- /dev/null
+++ b/media/mca/effect/java/android/media/effect/package.html
@@ -0,0 +1,54 @@
+<HTML>
+<BODY>
+<p>Provides classes that allow you to apply a variety of visual effects to images and
+videos. For example, you can easily fix red-eye, convert an image to grayscale,
+adjust brightness, adjust saturation, rotate an image, apply a fisheye effect, and much more. The
+system performs all effects processing on the GPU to obtain maximum performance.</p>
+
+<p>For maximum performance, effects are applied directly to OpenGL textures, so your application
+must have a valid OpenGL context before it can use the effects APIs. The textures to which you apply
+effects may be from bitmaps, videos or even the camera. However, there are certain restrictions that
+textures must meet:</p>
+<ol>
+<li>They must be bound to a {@link android.opengl.GLES20#GL_TEXTURE_2D} texture image</li>
+<li>They must contain at least one mipmap level</li>
+</ol>
+
+<p>An {@link android.media.effect.Effect} object defines a single media effect that you can apply to
+an image frame. The basic workflow to create an {@link android.media.effect.Effect} is:</p>
+
+<ol>
+<li>Call {@link android.media.effect.EffectContext#createWithCurrentGlContext
+EffectContext.createWithCurrentGlContext()} from your OpenGL ES 2.0 context.</li>
+<li>Use the returned {@link android.media.effect.EffectContext} to call {@link
+android.media.effect.EffectContext#getFactory EffectContext.getFactory()}, which returns an instance
+of {@link android.media.effect.EffectFactory}.</li>
+<li>Call {@link android.media.effect.EffectFactory#createEffect createEffect()}, passing it an
+effect name from @link android.media.effect.EffectFactory}, such as {@link
+android.media.effect.EffectFactory#EFFECT_FISHEYE} or {@link
+android.media.effect.EffectFactory#EFFECT_VIGNETTE}.</li>
+</ol>
+
+<p>You can adjust an effect’s parameters by calling {@link android.media.effect.Effect#setParameter
+setParameter()} and passing a parameter name and parameter value. Each type of effect accepts
+different parameters, which are documented with the effect name. For example, {@link
+android.media.effect.EffectFactory#EFFECT_FISHEYE} has one parameter for the {@code scale} of the
+distortion.</p>
+
+<p>To apply an effect on a texture, call {@link android.media.effect.Effect#apply apply()} on the
+{@link
+android.media.effect.Effect} and pass in the input texture, its width and height, and the output
+texture. The input texture must be bound to a {@link android.opengl.GLES20#GL_TEXTURE_2D} texture
+image (usually done by calling the {@link android.opengl.GLES20#glTexImage2D glTexImage2D()}
+function). You may provide multiple mipmap levels. If the output texture has not been bound to a
+texture image, it will be automatically bound by the effect as a {@link
+android.opengl.GLES20#GL_TEXTURE_2D} and with one mipmap level (0), which will have the same
+size as the input.</p>
+
+<p class="note"><strong>Note:</strong> All effects listed in {@link
+android.media.effect.EffectFactory} are guaranteed to be supported. However, some additional effects
+available from external libraries are not supported by all devices, so you must first check if the
+desired effect from the external library is supported by calling {@link
+android.media.effect.EffectFactory#isEffectSupported isEffectSupported()}.</p>
+</BODY>
+</HTML>
diff --git a/media/mca/filterfw/Android.mk b/media/mca/filterfw/Android.mk
new file mode 100644
index 0000000..b822e99
--- /dev/null
+++ b/media/mca/filterfw/Android.mk
@@ -0,0 +1,53 @@
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#####################
+# Build native sublibraries
+
+include $(all-subdir-makefiles)
+
+#####################
+# Build main libfilterfw
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libfilterfw
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_WHOLE_STATIC_LIBRARIES := libfilterfw_jni \
+ libfilterfw_native
+
+LOCAL_SHARED_LIBRARIES := libstlport \
+ libGLESv2 \
+ libEGL \
+ libgui \
+ libdl \
+ libcutils \
+ libutils \
+ libandroid \
+ libjnigraphics \
+ libmedia \
+ libmedia_native
+
+# Don't prelink this library. For more efficient code, you may want
+# to add this library to the prelink map and set this to true. However,
+# it's difficult to do this for applications that are not supplied as
+# part of a system image.
+LOCAL_PRELINK_MODULE := false
+
+include $(BUILD_SHARED_LIBRARY)
+
+
diff --git a/media/mca/filterfw/java/android/filterfw/FilterFunctionEnvironment.java b/media/mca/filterfw/java/android/filterfw/FilterFunctionEnvironment.java
new file mode 100644
index 0000000..3f36d98
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/FilterFunctionEnvironment.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterFactory;
+import android.filterfw.core.FilterFunction;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameManager;
+
+/**
+ * A FilterFunctionEnvironment provides a simple functional front-end to manually executing
+ * filters. Use this environment if a graph-based approach is not convenient for your case.
+ * Typically, a FilterFunctionEnvironment is used as follows:
+ * 1. Instantiate a new FilterFunctionEnvironment instance.
+ * 2. Perform any configuration, such as setting a GL environment.
+ * 3. Wrap Filters into FilterFunctions by calling createFunction().
+ * 4. Execute FilterFunctions individually and use the results for further processing.
+ * Additionally, there is a convenience method to execute a number of filters in sequence.
+ * @hide
+ */
+public class FilterFunctionEnvironment extends MffEnvironment {
+
+ /**
+ * Create a new FilterFunctionEnvironment with the default components.
+ */
+ public FilterFunctionEnvironment() {
+ super(null);
+ }
+
+ /**
+ * Create a new FilterFunctionEnvironment with a custom FrameManager. Pass null to auto-create
+ * a FrameManager.
+ *
+ * @param frameManager The FrameManager to use, or null to auto-create one.
+ */
+ public FilterFunctionEnvironment(FrameManager frameManager) {
+ super(frameManager);
+ }
+
+ /**
+ * Create a new FilterFunction from a specific filter class. The function is initialized with
+ * the given key-value list of parameters. Note, that this function uses the default shared
+ * FilterFactory to create the filter instance.
+ *
+ * @param filterClass The class of the filter to wrap. This must be a Filter subclass.
+ * @param parameters An argument list of alternating key-value filter parameters.
+ * @return A new FilterFunction instance.
+ */
+ public FilterFunction createFunction(Class filterClass, Object... parameters) {
+ String filterName = "FilterFunction(" + filterClass.getSimpleName() + ")";
+ Filter filter = FilterFactory.sharedFactory().createFilterByClass(filterClass, filterName);
+ filter.initWithAssignmentList(parameters);
+ return new FilterFunction(getContext(), filter);
+ }
+
+ /**
+ * Convenience method to execute a sequence of filter functions. Note that every function in
+ * the list MUST have one input and one output port, except the first filter (which must not
+ * have any input ports) and the last filter (which may not have any output ports).
+ *
+ * @param functions A list of filter functions. The first filter must be a source filter.
+ * @return The result of the last filter executed, or null if the last filter did not
+ produce any output.
+ *
+ public Frame executeSequence(FilterFunction[] functions) {
+ Frame oldFrame = null;
+ Frame newFrame = null;
+ for (FilterFunction filterFunction : functions) {
+ if (oldFrame == null) {
+ newFrame = filterFunction.executeWithArgList();
+ } else {
+ newFrame = filterFunction.executeWithArgList(oldFrame);
+ oldFrame.release();
+ }
+ oldFrame = newFrame;
+ }
+ if (oldFrame != null) {
+ oldFrame.release();
+ }
+ return newFrame;
+ }*/
+
+}
diff --git a/media/mca/filterfw/java/android/filterfw/GraphEnvironment.java b/media/mca/filterfw/java/android/filterfw/GraphEnvironment.java
new file mode 100644
index 0000000..5f6d45c
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/GraphEnvironment.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw;
+
+import android.content.Context;
+import android.filterfw.core.AsyncRunner;
+import android.filterfw.core.FilterGraph;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.FrameManager;
+import android.filterfw.core.GraphRunner;
+import android.filterfw.core.RoundRobinScheduler;
+import android.filterfw.core.SyncRunner;
+import android.filterfw.io.GraphIOException;
+import android.filterfw.io.GraphReader;
+import android.filterfw.io.TextGraphReader;
+
+import java.util.ArrayList;
+
+/**
+ * A GraphEnvironment provides a simple front-end to filter graph setup and execution using the
+ * mobile filter framework. Typically, you use a GraphEnvironment in the following fashion:
+ * 1. Instantiate a new GraphEnvironment instance.
+ * 2. Perform any configuration, such as adding graph references and setting a GL environment.
+ * 3. Load a graph file using loadGraph() or add a graph using addGraph().
+ * 4. Obtain a GraphRunner instance using getRunner().
+ * 5. Execute the obtained runner.
+ * Note that it is possible to add multiple graphs and runners to a single GraphEnvironment.
+ *
+ * @hide
+ */
+public class GraphEnvironment extends MffEnvironment {
+
+ public static final int MODE_ASYNCHRONOUS = 1;
+ public static final int MODE_SYNCHRONOUS = 2;
+
+ private GraphReader mGraphReader;
+ private ArrayList<GraphHandle> mGraphs = new ArrayList<GraphHandle>();
+
+ private class GraphHandle {
+ private FilterGraph mGraph;
+ private AsyncRunner mAsyncRunner;
+ private SyncRunner mSyncRunner;
+
+ public GraphHandle(FilterGraph graph) {
+ mGraph = graph;
+ }
+
+ public FilterGraph getGraph() {
+ return mGraph;
+ }
+
+ public AsyncRunner getAsyncRunner(FilterContext environment) {
+ if (mAsyncRunner == null) {
+ mAsyncRunner = new AsyncRunner(environment, RoundRobinScheduler.class);
+ mAsyncRunner.setGraph(mGraph);
+ }
+ return mAsyncRunner;
+ }
+
+ public GraphRunner getSyncRunner(FilterContext environment) {
+ if (mSyncRunner == null) {
+ mSyncRunner = new SyncRunner(environment, mGraph, RoundRobinScheduler.class);
+ }
+ return mSyncRunner;
+ }
+ }
+
+ /**
+ * Create a new GraphEnvironment with default components.
+ */
+ public GraphEnvironment() {
+ super(null);
+ }
+
+ /**
+ * Create a new GraphEnvironment with a custom FrameManager and GraphReader. Specifying null
+ * for either of these, will auto-create a default instance.
+ *
+ * @param frameManager The FrameManager to use, or null to auto-create one.
+ * @param reader The GraphReader to use for graph loading, or null to auto-create one.
+ * Note, that the reader will not be created until it is required. Pass
+ * null if you will not load any graph files.
+ */
+ public GraphEnvironment(FrameManager frameManager, GraphReader reader) {
+ super(frameManager);
+ mGraphReader = reader;
+ }
+
+ /**
+ * Returns the used graph reader. This will create one, if a reader has not been set already.
+ */
+ public GraphReader getGraphReader() {
+ if (mGraphReader == null) {
+ mGraphReader = new TextGraphReader();
+ }
+ return mGraphReader;
+ }
+
+ /**
+ * Add graph references to resolve during graph reading. The references added here are shared
+ * among all graphs.
+ *
+ * @param references An alternating argument list of keys (Strings) and values.
+ */
+ public void addReferences(Object... references) {
+ getGraphReader().addReferencesByKeysAndValues(references);
+ }
+
+ /**
+ * Loads a graph file from the specified resource and adds it to this environment.
+ *
+ * @param context The context in which to read the resource.
+ * @param resourceId The ID of the graph resource to load.
+ * @return A unique ID for the graph.
+ */
+ public int loadGraph(Context context, int resourceId) {
+ // Read the file into a graph
+ FilterGraph graph = null;
+ try {
+ graph = getGraphReader().readGraphResource(context, resourceId);
+ } catch (GraphIOException e) {
+ throw new RuntimeException("Could not read graph: " + e.getMessage());
+ }
+
+ // Add graph to our list of graphs
+ return addGraph(graph);
+ }
+
+ /**
+ * Add a graph to the environment. Consider using loadGraph() if you are loading a graph from
+ * a graph file.
+ *
+ * @param graph The graph to add to the environment.
+ * @return A unique ID for the added graph.
+ */
+ public int addGraph(FilterGraph graph) {
+ GraphHandle graphHandle = new GraphHandle(graph);
+ mGraphs.add(graphHandle);
+ return mGraphs.size() - 1;
+ }
+
+ /**
+ * Access a specific graph of this environment given a graph ID (previously returned from
+ * loadGraph() or addGraph()). Throws an InvalidArgumentException if no graph with the
+ * specified ID could be found.
+ *
+ * @param graphId The ID of the graph to get.
+ * @return The graph with the specified ID.
+ */
+ public FilterGraph getGraph(int graphId) {
+ if (graphId < 0 || graphId >= mGraphs.size()) {
+ throw new IllegalArgumentException(
+ "Invalid graph ID " + graphId + " specified in runGraph()!");
+ }
+ return mGraphs.get(graphId).getGraph();
+ }
+
+ /**
+ * Get a GraphRunner instance for the graph with the specified ID. The GraphRunner instance can
+ * be used to execute the graph. Throws an InvalidArgumentException if no graph with the
+ * specified ID could be found.
+ *
+ * @param graphId The ID of the graph to get.
+ * @param executionMode The mode of graph execution. Currently this can be either
+ MODE_SYNCHRONOUS or MODE_ASYNCHRONOUS.
+ * @return A GraphRunner instance for this graph.
+ */
+ public GraphRunner getRunner(int graphId, int executionMode) {
+ switch (executionMode) {
+ case MODE_ASYNCHRONOUS:
+ return mGraphs.get(graphId).getAsyncRunner(getContext());
+
+ case MODE_SYNCHRONOUS:
+ return mGraphs.get(graphId).getSyncRunner(getContext());
+
+ default:
+ throw new RuntimeException(
+ "Invalid execution mode " + executionMode + " specified in getRunner()!");
+ }
+ }
+
+}
diff --git a/media/mca/filterfw/java/android/filterfw/MffEnvironment.java b/media/mca/filterfw/java/android/filterfw/MffEnvironment.java
new file mode 100644
index 0000000..1ab416a
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/MffEnvironment.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw;
+
+import android.filterfw.core.CachedFrameManager;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.FrameManager;
+import android.filterfw.core.GLEnvironment;
+
+/**
+ * Base class for mobile filter framework (MFF) frontend environments. These convenience classes
+ * allow using the filter framework without the requirement of performing manual setup of its
+ * required components.
+ *
+ * @hide
+ */
+public class MffEnvironment {
+
+ private FilterContext mContext;
+
+ /**
+ * Protected constructor to initialize the environment's essential components. These are the
+ * frame-manager and the filter-context. Passing in null for the frame-manager causes this
+ * to be auto-created.
+ *
+ * @param frameManager The FrameManager to use or null to auto-create one.
+ */
+ protected MffEnvironment(FrameManager frameManager) {
+ // Get or create the frame manager
+ if (frameManager == null) {
+ frameManager = new CachedFrameManager();
+ }
+
+ // Setup the environment
+ mContext = new FilterContext();
+ mContext.setFrameManager(frameManager);
+
+ }
+
+ /**
+ * Returns the environment's filter-context.
+ */
+ public FilterContext getContext() {
+ return mContext;
+ }
+
+ /**
+ * Set the environment's GL environment to the specified environment. This does not activate
+ * the environment.
+ */
+ public void setGLEnvironment(GLEnvironment glEnvironment) {
+ mContext.initGLEnvironment(glEnvironment);
+ }
+
+ /**
+ * Create and activate a new GL environment for use in this filter context.
+ */
+ public void createGLEnvironment() {
+ GLEnvironment glEnvironment = new GLEnvironment();
+ glEnvironment.initWithNewContext();
+ setGLEnvironment(glEnvironment);
+ }
+
+ /**
+ * Activate the GL environment for use in the current thread. A GL environment must have been
+ * previously set or created using setGLEnvironment() or createGLEnvironment()! Call this after
+ * having switched to a new thread for GL filter execution.
+ */
+ public void activateGLEnvironment() {
+ GLEnvironment glEnv = mContext.getGLEnvironment();
+ if (glEnv != null) {
+ mContext.getGLEnvironment().activate();
+ } else {
+ throw new NullPointerException("No GLEnvironment in place to activate!");
+ }
+ }
+
+ /**
+ * Deactivate the GL environment from use in the current thread. A GL environment must have been
+ * previously set or created using setGLEnvironment() or createGLEnvironment()! Call this before
+ * running GL filters in another thread.
+ */
+ public void deactivateGLEnvironment() {
+ GLEnvironment glEnv = mContext.getGLEnvironment();
+ if (glEnv != null) {
+ mContext.getGLEnvironment().deactivate();
+ } else {
+ throw new NullPointerException("No GLEnvironment in place to deactivate!");
+ }
+ }
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/AsyncRunner.java b/media/mca/filterfw/java/android/filterfw/core/AsyncRunner.java
new file mode 100644
index 0000000..70cbad4
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/AsyncRunner.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import android.os.AsyncTask;
+import android.os.Handler;
+
+import android.util.Log;
+
+import java.lang.InterruptedException;
+import java.lang.Runnable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @hide
+ */
+public class AsyncRunner extends GraphRunner{
+
+ private Class mSchedulerClass;
+ private SyncRunner mRunner;
+ private AsyncRunnerTask mRunTask;
+
+ private OnRunnerDoneListener mDoneListener;
+ private boolean isProcessing;
+
+ private Exception mException;
+
+ private class RunnerResult {
+ public int status = RESULT_UNKNOWN;
+ public Exception exception;
+ }
+
+ private class AsyncRunnerTask extends AsyncTask<SyncRunner, Void, RunnerResult> {
+
+ private static final String TAG = "AsyncRunnerTask";
+
+ @Override
+ protected RunnerResult doInBackground(SyncRunner... runner) {
+ RunnerResult result = new RunnerResult();
+ try {
+ if (runner.length > 1) {
+ throw new RuntimeException("More than one runner received!");
+ }
+
+ runner[0].assertReadyToStep();
+
+ // Preparation
+ if (mLogVerbose) Log.v(TAG, "Starting background graph processing.");
+ activateGlContext();
+
+ if (mLogVerbose) Log.v(TAG, "Preparing filter graph for processing.");
+ runner[0].beginProcessing();
+
+ if (mLogVerbose) Log.v(TAG, "Running graph.");
+
+ // Run loop
+ result.status = RESULT_RUNNING;
+ while (!isCancelled() && result.status == RESULT_RUNNING) {
+ if (!runner[0].performStep()) {
+ result.status = runner[0].determinePostRunState();
+ if (result.status == GraphRunner.RESULT_SLEEPING) {
+ runner[0].waitUntilWake();
+ result.status = RESULT_RUNNING;
+ }
+ }
+ }
+
+ // Cleanup
+ if (isCancelled()) {
+ result.status = RESULT_STOPPED;
+ }
+ } catch (Exception exception) {
+ result.exception = exception;
+ result.status = RESULT_ERROR;
+ }
+
+ // Deactivate context.
+ try {
+ deactivateGlContext();
+ } catch (Exception exception) {
+ result.exception = exception;
+ result.status = RESULT_ERROR;
+ }
+
+ if (mLogVerbose) Log.v(TAG, "Done with background graph processing.");
+ return result;
+ }
+
+ @Override
+ protected void onCancelled(RunnerResult result) {
+ onPostExecute(result);
+ }
+
+ @Override
+ protected void onPostExecute(RunnerResult result) {
+ if (mLogVerbose) Log.v(TAG, "Starting post-execute.");
+ setRunning(false);
+ if (result == null) {
+ // Cancelled before got to doInBackground
+ result = new RunnerResult();
+ result.status = RESULT_STOPPED;
+ }
+ setException(result.exception);
+ if (result.status == RESULT_STOPPED || result.status == RESULT_ERROR) {
+ if (mLogVerbose) Log.v(TAG, "Closing filters.");
+ try {
+ mRunner.close();
+ } catch (Exception exception) {
+ result.status = RESULT_ERROR;
+ setException(exception);
+ }
+ }
+ if (mDoneListener != null) {
+ if (mLogVerbose) Log.v(TAG, "Calling graph done callback.");
+ mDoneListener.onRunnerDone(result.status);
+ }
+ if (mLogVerbose) Log.v(TAG, "Completed post-execute.");
+ }
+ }
+
+ private boolean mLogVerbose;
+ private static final String TAG = "AsyncRunner";
+
+ /** Create a new asynchronous graph runner with the given filter
+ * context, and the given scheduler class.
+ *
+ * Must be created on the UI thread.
+ */
+ public AsyncRunner(FilterContext context, Class schedulerClass) {
+ super(context);
+
+ mSchedulerClass = schedulerClass;
+ mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+ }
+
+ /** Create a new asynchronous graph runner with the given filter
+ * context. Uses a default scheduler.
+ *
+ * Must be created on the UI thread.
+ */
+ public AsyncRunner(FilterContext context) {
+ super(context);
+
+ mSchedulerClass = SimpleScheduler.class;
+ mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+ }
+
+ /** Set a callback to be called in the UI thread once the AsyncRunner
+ * completes running a graph, whether the completion is due to a stop() call
+ * or the filters running out of data to process.
+ */
+ @Override
+ public void setDoneCallback(OnRunnerDoneListener listener) {
+ mDoneListener = listener;
+ }
+
+ /** Sets the graph to be run. Will call prepare() on graph. Cannot be called
+ * when a graph is already running.
+ */
+ synchronized public void setGraph(FilterGraph graph) {
+ if (isRunning()) {
+ throw new RuntimeException("Graph is already running!");
+ }
+ mRunner = new SyncRunner(mFilterContext, graph, mSchedulerClass);
+ }
+
+ @Override
+ public FilterGraph getGraph() {
+ return mRunner != null ? mRunner.getGraph() : null;
+ }
+
+ /** Execute the graph in a background thread. */
+ @Override
+ synchronized public void run() {
+ if (mLogVerbose) Log.v(TAG, "Running graph.");
+ setException(null);
+
+ if (isRunning()) {
+ throw new RuntimeException("Graph is already running!");
+ }
+ if (mRunner == null) {
+ throw new RuntimeException("Cannot run before a graph is set!");
+ }
+ mRunTask = this.new AsyncRunnerTask();
+
+ setRunning(true);
+ mRunTask.execute(mRunner);
+ }
+
+ /** Stop graph execution. This is an asynchronous call; register a callback
+ * with setDoneCallback to be notified of when the background processing has
+ * been completed. Calling stop will close the filter graph. */
+ @Override
+ synchronized public void stop() {
+ if (mRunTask != null && !mRunTask.isCancelled() ) {
+ if (mLogVerbose) Log.v(TAG, "Stopping graph.");
+ mRunTask.cancel(false);
+ }
+ }
+
+ @Override
+ synchronized public void close() {
+ if (isRunning()) {
+ throw new RuntimeException("Cannot close graph while it is running!");
+ }
+ if (mLogVerbose) Log.v(TAG, "Closing filters.");
+ mRunner.close();
+ }
+
+ /** Check if background processing is happening */
+ @Override
+ synchronized public boolean isRunning() {
+ return isProcessing;
+ }
+
+ @Override
+ synchronized public Exception getError() {
+ return mException;
+ }
+
+ synchronized private void setRunning(boolean running) {
+ isProcessing = running;
+ }
+
+ synchronized private void setException(Exception exception) {
+ mException = exception;
+ }
+
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/CachedFrameManager.java b/media/mca/filterfw/java/android/filterfw/core/CachedFrameManager.java
new file mode 100644
index 0000000..a2cf2a0
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/CachedFrameManager.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.SimpleFrameManager;
+
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * @hide
+ */
+public class CachedFrameManager extends SimpleFrameManager {
+
+ private SortedMap<Integer, Frame> mAvailableFrames;
+ private int mStorageCapacity = 24 * 1024 * 1024; // Cap default storage to 24MB
+ private int mStorageSize = 0;
+ private int mTimeStamp = 0;
+
+ public CachedFrameManager() {
+ super();
+ mAvailableFrames = new TreeMap<Integer, Frame>();
+ }
+
+ @Override
+ public Frame newFrame(FrameFormat format) {
+ Frame result = findAvailableFrame(format, Frame.NO_BINDING, 0);
+ if (result == null) {
+ result = super.newFrame(format);
+ }
+ result.setTimestamp(Frame.TIMESTAMP_NOT_SET);
+ return result;
+ }
+
+ @Override
+ public Frame newBoundFrame(FrameFormat format, int bindingType, long bindingId) {
+ Frame result = findAvailableFrame(format, bindingType, bindingId);
+ if (result == null) {
+ result = super.newBoundFrame(format, bindingType, bindingId);
+ }
+ result.setTimestamp(Frame.TIMESTAMP_NOT_SET);
+ return result;
+ }
+
+ @Override
+ public Frame retainFrame(Frame frame) {
+ return super.retainFrame(frame);
+ }
+
+ @Override
+ public Frame releaseFrame(Frame frame) {
+ if (frame.isReusable()) {
+ int refCount = frame.decRefCount();
+ if (refCount == 0 && frame.hasNativeAllocation()) {
+ if (!storeFrame(frame)) {
+ frame.releaseNativeAllocation();
+ }
+ return null;
+ } else if (refCount < 0) {
+ throw new RuntimeException("Frame reference count dropped below 0!");
+ }
+ } else {
+ super.releaseFrame(frame);
+ }
+ return frame;
+ }
+
+ public void clearCache() {
+ for (Frame frame : mAvailableFrames.values()) {
+ frame.releaseNativeAllocation();
+ }
+ mAvailableFrames.clear();
+ }
+
+ @Override
+ public void tearDown() {
+ clearCache();
+ }
+
+ private boolean storeFrame(Frame frame) {
+ synchronized(mAvailableFrames) {
+ // Make sure this frame alone does not exceed capacity
+ int frameSize = frame.getFormat().getSize();
+ if (frameSize > mStorageCapacity) {
+ return false;
+ }
+
+ // Drop frames if adding this frame would exceed capacity
+ int newStorageSize = mStorageSize + frameSize;
+ while (newStorageSize > mStorageCapacity) {
+ dropOldestFrame();
+ newStorageSize = mStorageSize + frameSize;
+ }
+
+ // Store new frame
+ frame.onFrameStore();
+ mStorageSize = newStorageSize;
+ mAvailableFrames.put(mTimeStamp, frame);
+ ++mTimeStamp;
+ return true;
+ }
+ }
+
+ private void dropOldestFrame() {
+ int oldest = mAvailableFrames.firstKey();
+ Frame frame = mAvailableFrames.get(oldest);
+ mStorageSize -= frame.getFormat().getSize();
+ frame.releaseNativeAllocation();
+ mAvailableFrames.remove(oldest);
+ }
+
+ private Frame findAvailableFrame(FrameFormat format, int bindingType, long bindingId) {
+ // Look for a frame that is compatible with the requested format
+ synchronized(mAvailableFrames) {
+ for (Map.Entry<Integer, Frame> entry : mAvailableFrames.entrySet()) {
+ Frame frame = entry.getValue();
+ // Check that format is compatible
+ if (frame.getFormat().isReplaceableBy(format)) {
+ // Check that binding is compatible (if frame is bound)
+ if ((bindingType == frame.getBindingType())
+ && (bindingType == Frame.NO_BINDING || bindingId == frame.getBindingId())) {
+ // We found one! Take it out of the set of available frames and attach the
+ // requested format to it.
+ super.retainFrame(frame);
+ mAvailableFrames.remove(entry.getKey());
+ frame.onFrameFetch();
+ frame.reset(format);
+ mStorageSize -= format.getSize();
+ return frame;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/FieldPort.java b/media/mca/filterfw/java/android/filterfw/core/FieldPort.java
new file mode 100644
index 0000000..b0350cc
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/FieldPort.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import java.lang.reflect.Field;
+
+/**
+ * @hide
+ */
+public class FieldPort extends InputPort {
+
+ protected Field mField;
+ protected boolean mHasFrame;
+ protected boolean mValueWaiting = false;
+ protected Object mValue;
+
+ public FieldPort(Filter filter, String name, Field field, boolean hasDefault) {
+ super(filter, name);
+ mField = field;
+ mHasFrame = hasDefault;
+ }
+
+ @Override
+ public void clear() {
+ }
+
+ @Override
+ public void pushFrame(Frame frame) {
+ setFieldFrame(frame, false);
+ }
+
+ @Override
+ public void setFrame(Frame frame) {
+ setFieldFrame(frame, true);
+ }
+
+ @Override
+ public Object getTarget() {
+ try {
+ return mField.get(mFilter);
+ } catch (IllegalAccessException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public synchronized void transfer(FilterContext context) {
+ if (mValueWaiting) {
+ try {
+ mField.set(mFilter, mValue);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(
+ "Access to field '" + mField.getName() + "' was denied!");
+ }
+ mValueWaiting = false;
+ if (context != null) {
+ mFilter.notifyFieldPortValueUpdated(mName, context);
+ }
+ }
+ }
+
+ @Override
+ public synchronized Frame pullFrame() {
+ throw new RuntimeException("Cannot pull frame on " + this + "!");
+ }
+
+ @Override
+ public synchronized boolean hasFrame() {
+ return mHasFrame;
+ }
+
+ @Override
+ public synchronized boolean acceptsFrame() {
+ return !mValueWaiting;
+ }
+
+ @Override
+ public String toString() {
+ return "field " + super.toString();
+ }
+
+ protected synchronized void setFieldFrame(Frame frame, boolean isAssignment) {
+ assertPortIsOpen();
+ checkFrameType(frame, isAssignment);
+
+ // Store the object value
+ Object value = frame.getObjectValue();
+ if ((value == null && mValue != null) || !value.equals(mValue)) {
+ mValue = value;
+ mValueWaiting = true;
+ }
+
+ // Since a frame was set, mark this port as having a frame to pull
+ mHasFrame = true;
+ }
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/Filter.java b/media/mca/filterfw/java/android/filterfw/core/Filter.java
new file mode 100644
index 0000000..73b009d
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/Filter.java
@@ -0,0 +1,709 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.FilterPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.io.TextGraphReader;
+import android.filterfw.io.GraphIOException;
+import android.filterfw.format.ObjectFormat;
+import android.util.Log;
+
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.Thread;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map.Entry;
+import java.util.LinkedList;
+import java.util.Set;
+
+/**
+ * @hide
+ */
+public abstract class Filter {
+
+ static final int STATUS_PREINIT = 0;
+ static final int STATUS_UNPREPARED = 1;
+ static final int STATUS_PREPARED = 2;
+ static final int STATUS_PROCESSING = 3;
+ static final int STATUS_SLEEPING = 4;
+ static final int STATUS_FINISHED = 5;
+ static final int STATUS_ERROR = 6;
+ static final int STATUS_RELEASED = 7;
+
+ private String mName;
+
+ private int mInputCount = -1;
+ private int mOutputCount = -1;
+
+ private HashMap<String, InputPort> mInputPorts;
+ private HashMap<String, OutputPort> mOutputPorts;
+
+ private HashSet<Frame> mFramesToRelease;
+ private HashMap<String, Frame> mFramesToSet;
+
+ private int mStatus = 0;
+ private boolean mIsOpen = false;
+ private int mSleepDelay;
+
+ private long mCurrentTimestamp;
+
+ private boolean mLogVerbose;
+ private static final String TAG = "Filter";
+
+ public Filter(String name) {
+ mName = name;
+ mFramesToRelease = new HashSet<Frame>();
+ mFramesToSet = new HashMap<String, Frame>();
+ mStatus = STATUS_PREINIT;
+
+ mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+ }
+
+ /** Tests to see if a given filter is installed on the system. Requires
+ * full filter package name, including filterpack.
+ */
+ public static final boolean isAvailable(String filterName) {
+ ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+ Class filterClass;
+ // First see if a class of that name exists
+ try {
+ filterClass = contextClassLoader.loadClass(filterName);
+ } catch (ClassNotFoundException e) {
+ return false;
+ }
+ // Then make sure it's a subclass of Filter.
+ try {
+ filterClass.asSubclass(Filter.class);
+ } catch (ClassCastException e) {
+ return false;
+ }
+ return true;
+ }
+
+ public final void initWithValueMap(KeyValueMap valueMap) {
+ // Initialization
+ initFinalPorts(valueMap);
+
+ // Setup remaining ports
+ initRemainingPorts(valueMap);
+
+ // This indicates that final ports can no longer be set
+ mStatus = STATUS_UNPREPARED;
+ }
+
+ public final void initWithAssignmentString(String assignments) {
+ try {
+ KeyValueMap valueMap = new TextGraphReader().readKeyValueAssignments(assignments);
+ initWithValueMap(valueMap);
+ } catch (GraphIOException e) {
+ throw new IllegalArgumentException(e.getMessage());
+ }
+ }
+
+ public final void initWithAssignmentList(Object... keyValues) {
+ KeyValueMap valueMap = new KeyValueMap();
+ valueMap.setKeyValues(keyValues);
+ initWithValueMap(valueMap);
+ }
+
+ public final void init() throws ProtocolException {
+ KeyValueMap valueMap = new KeyValueMap();
+ initWithValueMap(valueMap);
+ }
+
+ public String getFilterClassName() {
+ return getClass().getSimpleName();
+ }
+
+ public final String getName() {
+ return mName;
+ }
+
+ public boolean isOpen() {
+ return mIsOpen;
+ }
+
+ public void setInputFrame(String inputName, Frame frame) {
+ FilterPort port = getInputPort(inputName);
+ if (!port.isOpen()) {
+ port.open();
+ }
+ port.setFrame(frame);
+ }
+
+ public final void setInputValue(String inputName, Object value) {
+ setInputFrame(inputName, wrapInputValue(inputName, value));
+ }
+
+ protected void prepare(FilterContext context) {
+ }
+
+ protected void parametersUpdated(Set<String> updated) {
+ }
+
+ protected void delayNextProcess(int millisecs) {
+ mSleepDelay = millisecs;
+ mStatus = STATUS_SLEEPING;
+ }
+
+ public abstract void setupPorts();
+
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return null;
+ }
+
+ public final FrameFormat getInputFormat(String portName) {
+ InputPort inputPort = getInputPort(portName);
+ return inputPort.getSourceFormat();
+ }
+
+ public void open(FilterContext context) {
+ }
+
+ public abstract void process(FilterContext context);
+
+ public final int getSleepDelay() {
+ return 250;
+ }
+
+ public void close(FilterContext context) {
+ }
+
+ public void tearDown(FilterContext context) {
+ }
+
+ public final int getNumberOfConnectedInputs() {
+ int c = 0;
+ for (InputPort inputPort : mInputPorts.values()) {
+ if (inputPort.isConnected()) {
+ ++c;
+ }
+ }
+ return c;
+ }
+
+ public final int getNumberOfConnectedOutputs() {
+ int c = 0;
+ for (OutputPort outputPort : mOutputPorts.values()) {
+ if (outputPort.isConnected()) {
+ ++c;
+ }
+ }
+ return c;
+ }
+
+ public final int getNumberOfInputs() {
+ return mOutputPorts == null ? 0 : mInputPorts.size();
+ }
+
+ public final int getNumberOfOutputs() {
+ return mInputPorts == null ? 0 : mOutputPorts.size();
+ }
+
+ public final InputPort getInputPort(String portName) {
+ if (mInputPorts == null) {
+ throw new NullPointerException("Attempting to access input port '" + portName
+ + "' of " + this + " before Filter has been initialized!");
+ }
+ InputPort result = mInputPorts.get(portName);
+ if (result == null) {
+ throw new IllegalArgumentException("Unknown input port '" + portName + "' on filter "
+ + this + "!");
+ }
+ return result;
+ }
+
+ public final OutputPort getOutputPort(String portName) {
+ if (mInputPorts == null) {
+ throw new NullPointerException("Attempting to access output port '" + portName
+ + "' of " + this + " before Filter has been initialized!");
+ }
+ OutputPort result = mOutputPorts.get(portName);
+ if (result == null) {
+ throw new IllegalArgumentException("Unknown output port '" + portName + "' on filter "
+ + this + "!");
+ }
+ return result;
+ }
+
+ protected final void pushOutput(String name, Frame frame) {
+ if (frame.getTimestamp() == Frame.TIMESTAMP_NOT_SET) {
+ if (mLogVerbose) Log.v(TAG, "Default-setting output Frame timestamp on port " + name + " to " + mCurrentTimestamp);
+ frame.setTimestamp(mCurrentTimestamp);
+ }
+ getOutputPort(name).pushFrame(frame);
+ }
+
+ protected final Frame pullInput(String name) {
+ Frame result = getInputPort(name).pullFrame();
+ if (mCurrentTimestamp == Frame.TIMESTAMP_UNKNOWN) {
+ mCurrentTimestamp = result.getTimestamp();
+ if (mLogVerbose) Log.v(TAG, "Default-setting current timestamp from input port " + name + " to " + mCurrentTimestamp);
+ }
+ // As result is retained, we add it to the release pool here
+ mFramesToRelease.add(result);
+
+ return result;
+ }
+
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ }
+
+ /**
+ * Transfers any frame from an input port to its destination. This is useful to force a
+ * transfer from a FieldPort or ProgramPort to its connected target (field or program variable).
+ */
+ protected void transferInputPortFrame(String name, FilterContext context) {
+ getInputPort(name).transfer(context);
+ }
+
+ /**
+ * Assigns all program variables to the ports they are connected to. Call this after
+ * constructing a Program instance with attached ProgramPorts.
+ */
+ protected void initProgramInputs(Program program, FilterContext context) {
+ if (program != null) {
+ for (InputPort inputPort : mInputPorts.values()) {
+ if (inputPort.getTarget() == program) {
+ inputPort.transfer(context);
+ }
+ }
+ }
+ }
+
+ /**
+ * Adds an input port to the filter. You should call this from within setupPorts, if your
+ * filter has input ports. No type-checking is performed on the input. If you would like to
+ * check against a type mask, use
+ * {@link #addMaskedInputPort(String, FrameFormat) addMaskedInputPort} instead.
+ *
+ * @param name the name of the input port
+ */
+ protected void addInputPort(String name) {
+ addMaskedInputPort(name, null);
+ }
+
+ /**
+ * Adds an input port to the filter. You should call this from within setupPorts, if your
+ * filter has input ports. When type-checking is performed, the input format is
+ * checked against the provided format mask. An exception is thrown in case of a conflict.
+ *
+ * @param name the name of the input port
+ * @param formatMask a format mask, which filters the allowable input types
+ */
+ protected void addMaskedInputPort(String name, FrameFormat formatMask) {
+ InputPort port = new StreamPort(this, name);
+ if (mLogVerbose) Log.v(TAG, "Filter " + this + " adding " + port);
+ mInputPorts.put(name, port);
+ port.setPortFormat(formatMask);
+ }
+
+ /**
+ * Adds an output port to the filter with a fixed output format. You should call this from
+ * within setupPorts, if your filter has output ports. You cannot use this method, if your
+ * output format depends on the input format (e.g. in a pass-through filter). In this case, use
+ * {@link #addOutputBasedOnInput(String, String) addOutputBasedOnInput} instead.
+ *
+ * @param name the name of the output port
+ * @param format the fixed output format of this port
+ */
+ protected void addOutputPort(String name, FrameFormat format) {
+ OutputPort port = new OutputPort(this, name);
+ if (mLogVerbose) Log.v(TAG, "Filter " + this + " adding " + port);
+ port.setPortFormat(format);
+ mOutputPorts.put(name, port);
+ }
+
+ /**
+ * Adds an output port to the filter. You should call this from within setupPorts, if your
+ * filter has output ports. Using this method indicates that the output format for this
+ * particular port, depends on the format of an input port. You MUST also override
+ * {@link #getOutputFormat(String, FrameFormat) getOutputFormat} to specify what format your
+ * filter will output for a given input. If the output format of your filter port does not
+ * depend on the input, use {@link #addOutputPort(String, FrameFormat) addOutputPort} instead.
+ *
+ * @param outputName the name of the output port
+ * @param inputName the name of the input port, that this output depends on
+ */
+ protected void addOutputBasedOnInput(String outputName, String inputName) {
+ OutputPort port = new OutputPort(this, outputName);
+ if (mLogVerbose) Log.v(TAG, "Filter " + this + " adding " + port);
+ port.setBasePort(getInputPort(inputName));
+ mOutputPorts.put(outputName, port);
+ }
+
+ protected void addFieldPort(String name,
+ Field field,
+ boolean hasDefault,
+ boolean isFinal) {
+ // Make sure field is accessible
+ field.setAccessible(true);
+
+ // Create port for this input
+ InputPort fieldPort = isFinal
+ ? new FinalPort(this, name, field, hasDefault)
+ : new FieldPort(this, name, field, hasDefault);
+
+ // Create format for this input
+ if (mLogVerbose) Log.v(TAG, "Filter " + this + " adding " + fieldPort);
+ MutableFrameFormat format = ObjectFormat.fromClass(field.getType(),
+ FrameFormat.TARGET_SIMPLE);
+ fieldPort.setPortFormat(format);
+
+ // Add port
+ mInputPorts.put(name, fieldPort);
+ }
+
+ protected void addProgramPort(String name,
+ String varName,
+ Field field,
+ Class varType,
+ boolean hasDefault) {
+ // Make sure field is accessible
+ field.setAccessible(true);
+
+ // Create port for this input
+ InputPort programPort = new ProgramPort(this, name, varName, field, hasDefault);
+
+ // Create format for this input
+ if (mLogVerbose) Log.v(TAG, "Filter " + this + " adding " + programPort);
+ MutableFrameFormat format = ObjectFormat.fromClass(varType,
+ FrameFormat.TARGET_SIMPLE);
+ programPort.setPortFormat(format);
+
+ // Add port
+ mInputPorts.put(name, programPort);
+ }
+
+ protected void closeOutputPort(String name) {
+ getOutputPort(name).close();
+ }
+
+ /**
+ * Specifies whether the filter should not be scheduled until a frame is available on that
+ * input port. Note, that setting this to false, does not block a new frame from coming in
+ * (though there is no necessity to pull that frame for processing).
+ * @param portName the name of the input port.
+ * @param waits true, if the filter should wait for a frame on this port.
+ */
+ protected void setWaitsOnInputPort(String portName, boolean waits) {
+ getInputPort(portName).setBlocking(waits);
+ }
+
+ /**
+ * Specifies whether the filter should not be scheduled until the output port is free, i.e.
+ * there is no frame waiting on that output.
+ * @param portName the name of the output port.
+ * @param waits true, if the filter should wait for the port to become free.
+ */
+ protected void setWaitsOnOutputPort(String portName, boolean waits) {
+ getOutputPort(portName).setBlocking(waits);
+ }
+
+ public String toString() {
+ return "'" + getName() + "' (" + getFilterClassName() + ")";
+ }
+
+ // Core internal methods ///////////////////////////////////////////////////////////////////////
+ final Collection<InputPort> getInputPorts() {
+ return mInputPorts.values();
+ }
+
+ final Collection<OutputPort> getOutputPorts() {
+ return mOutputPorts.values();
+ }
+
+ final synchronized int getStatus() {
+ return mStatus;
+ }
+
+ final synchronized void unsetStatus(int flag) {
+ mStatus &= ~flag;
+ }
+
+ final synchronized void performOpen(FilterContext context) {
+ if (!mIsOpen) {
+ if (mStatus == STATUS_UNPREPARED) {
+ if (mLogVerbose) Log.v(TAG, "Preparing " + this);
+ prepare(context);
+ mStatus = STATUS_PREPARED;
+ }
+ if (mStatus == STATUS_PREPARED) {
+ if (mLogVerbose) Log.v(TAG, "Opening " + this);
+ open(context);
+ mStatus = STATUS_PROCESSING;
+ }
+ if (mStatus != STATUS_PROCESSING) {
+ throw new RuntimeException("Filter " + this + " was brought into invalid state during "
+ + "opening (state: " + mStatus + ")!");
+ }
+ mIsOpen = true;
+ }
+ }
+
+ final synchronized void performProcess(FilterContext context) {
+ if (mStatus == STATUS_RELEASED) {
+ throw new RuntimeException("Filter " + this + " is already torn down!");
+ }
+ transferInputFrames(context);
+ if (mStatus < STATUS_PROCESSING) {
+ performOpen(context);
+ }
+ if (mLogVerbose) Log.v(TAG, "Processing " + this);
+ mCurrentTimestamp = Frame.TIMESTAMP_UNKNOWN;
+ process(context);
+ releasePulledFrames(context);
+ if (filterMustClose()) {
+ performClose(context);
+ }
+ }
+
+ final synchronized void performClose(FilterContext context) {
+ if (mIsOpen) {
+ if (mLogVerbose) Log.v(TAG, "Closing " + this);
+ mIsOpen = false;
+ mStatus = STATUS_PREPARED;
+ close(context);
+ closePorts();
+ }
+ }
+
+ final synchronized void performTearDown(FilterContext context) {
+ performClose(context);
+ if (mStatus != STATUS_RELEASED) {
+ tearDown(context);
+ mStatus = STATUS_RELEASED;
+ }
+ }
+
+ synchronized final boolean canProcess() {
+ if (mLogVerbose) Log.v(TAG, "Checking if can process: " + this + " (" + mStatus + ").");
+ if (mStatus <= STATUS_PROCESSING) {
+ return inputConditionsMet() && outputConditionsMet();
+ } else {
+ return false;
+ }
+ }
+
+ final void openOutputs() {
+ if (mLogVerbose) Log.v(TAG, "Opening all output ports on " + this + "!");
+ for (OutputPort outputPort : mOutputPorts.values()) {
+ if (!outputPort.isOpen()) {
+ outputPort.open();
+ }
+ }
+ }
+
+ final void clearInputs() {
+ for (InputPort inputPort : mInputPorts.values()) {
+ inputPort.clear();
+ }
+ }
+
+ final void clearOutputs() {
+ for (OutputPort outputPort : mOutputPorts.values()) {
+ outputPort.clear();
+ }
+ }
+
+ final void notifyFieldPortValueUpdated(String name, FilterContext context) {
+ if (mStatus == STATUS_PROCESSING || mStatus == STATUS_PREPARED) {
+ fieldPortValueUpdated(name, context);
+ }
+ }
+
+ final synchronized void pushInputFrame(String inputName, Frame frame) {
+ FilterPort port = getInputPort(inputName);
+ if (!port.isOpen()) {
+ port.open();
+ }
+ port.pushFrame(frame);
+ }
+
+ final synchronized void pushInputValue(String inputName, Object value) {
+ pushInputFrame(inputName, wrapInputValue(inputName, value));
+ }
+
+ // Filter internal methods /////////////////////////////////////////////////////////////////////
+ private final void initFinalPorts(KeyValueMap values) {
+ mInputPorts = new HashMap<String, InputPort>();
+ mOutputPorts = new HashMap<String, OutputPort>();
+ addAndSetFinalPorts(values);
+ }
+
+ private final void initRemainingPorts(KeyValueMap values) {
+ addAnnotatedPorts();
+ setupPorts(); // TODO: rename to addFilterPorts() ?
+ setInitialInputValues(values);
+ }
+
+ private final void addAndSetFinalPorts(KeyValueMap values) {
+ Class filterClass = getClass();
+ Annotation annotation;
+ for (Field field : filterClass.getDeclaredFields()) {
+ if ((annotation = field.getAnnotation(GenerateFinalPort.class)) != null) {
+ GenerateFinalPort generator = (GenerateFinalPort)annotation;
+ String name = generator.name().isEmpty() ? field.getName() : generator.name();
+ boolean hasDefault = generator.hasDefault();
+ addFieldPort(name, field, hasDefault, true);
+ if (values.containsKey(name)) {
+ setImmediateInputValue(name, values.get(name));
+ values.remove(name);
+ } else if (!generator.hasDefault()) {
+ throw new RuntimeException("No value specified for final input port '"
+ + name + "' of filter " + this + "!");
+ }
+ }
+ }
+ }
+
+ private final void addAnnotatedPorts() {
+ Class filterClass = getClass();
+ Annotation annotation;
+ for (Field field : filterClass.getDeclaredFields()) {
+ if ((annotation = field.getAnnotation(GenerateFieldPort.class)) != null) {
+ GenerateFieldPort generator = (GenerateFieldPort)annotation;
+ addFieldGenerator(generator, field);
+ } else if ((annotation = field.getAnnotation(GenerateProgramPort.class)) != null) {
+ GenerateProgramPort generator = (GenerateProgramPort)annotation;
+ addProgramGenerator(generator, field);
+ } else if ((annotation = field.getAnnotation(GenerateProgramPorts.class)) != null) {
+ GenerateProgramPorts generators = (GenerateProgramPorts)annotation;
+ for (GenerateProgramPort generator : generators.value()) {
+ addProgramGenerator(generator, field);
+ }
+ }
+ }
+ }
+
+ private final void addFieldGenerator(GenerateFieldPort generator, Field field) {
+ String name = generator.name().isEmpty() ? field.getName() : generator.name();
+ boolean hasDefault = generator.hasDefault();
+ addFieldPort(name, field, hasDefault, false);
+ }
+
+ private final void addProgramGenerator(GenerateProgramPort generator, Field field) {
+ String name = generator.name();
+ String varName = generator.variableName().isEmpty() ? name
+ : generator.variableName();
+ Class varType = generator.type();
+ boolean hasDefault = generator.hasDefault();
+ addProgramPort(name, varName, field, varType, hasDefault);
+ }
+
+ private final void setInitialInputValues(KeyValueMap values) {
+ for (Entry<String, Object> entry : values.entrySet()) {
+ setInputValue(entry.getKey(), entry.getValue());
+ }
+ }
+
+ private final void setImmediateInputValue(String name, Object value) {
+ if (mLogVerbose) Log.v(TAG, "Setting immediate value " + value + " for port " + name + "!");
+ FilterPort port = getInputPort(name);
+ port.open();
+ port.setFrame(SimpleFrame.wrapObject(value, null));
+ }
+
+ private final void transferInputFrames(FilterContext context) {
+ for (InputPort inputPort : mInputPorts.values()) {
+ inputPort.transfer(context);
+ }
+ }
+
+ private final Frame wrapInputValue(String inputName, Object value) {
+ MutableFrameFormat inputFormat = ObjectFormat.fromObject(value, FrameFormat.TARGET_SIMPLE);
+ if (value == null) {
+ // If the value is null, the format cannot guess the class, so we adjust it to the
+ // class of the input port here
+ FrameFormat portFormat = getInputPort(inputName).getPortFormat();
+ Class portClass = (portFormat == null) ? null : portFormat.getObjectClass();
+ inputFormat.setObjectClass(portClass);
+ }
+
+ // Serialize if serializable, and type is not an immutable primitive.
+ boolean shouldSerialize = !(value instanceof Number)
+ && !(value instanceof Boolean)
+ && !(value instanceof String)
+ && value instanceof Serializable;
+
+ // Create frame wrapper
+ Frame frame = shouldSerialize
+ ? new SerializedFrame(inputFormat, null)
+ : new SimpleFrame(inputFormat, null);
+ frame.setObjectValue(value);
+ return frame;
+ }
+
+ private final void releasePulledFrames(FilterContext context) {
+ for (Frame frame : mFramesToRelease) {
+ context.getFrameManager().releaseFrame(frame);
+ }
+ mFramesToRelease.clear();
+ }
+
+ private final boolean inputConditionsMet() {
+ for (FilterPort port : mInputPorts.values()) {
+ if (!port.isReady()) {
+ if (mLogVerbose) Log.v(TAG, "Input condition not met: " + port + "!");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private final boolean outputConditionsMet() {
+ for (FilterPort port : mOutputPorts.values()) {
+ if (!port.isReady()) {
+ if (mLogVerbose) Log.v(TAG, "Output condition not met: " + port + "!");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private final void closePorts() {
+ if (mLogVerbose) Log.v(TAG, "Closing all ports on " + this + "!");
+ for (InputPort inputPort : mInputPorts.values()) {
+ inputPort.close();
+ }
+ for (OutputPort outputPort : mOutputPorts.values()) {
+ outputPort.close();
+ }
+ }
+
+ private final boolean filterMustClose() {
+ for (InputPort inputPort : mInputPorts.values()) {
+ if (inputPort.filterMustClose()) {
+ if (mLogVerbose) Log.v(TAG, "Filter " + this + " must close due to port " + inputPort);
+ return true;
+ }
+ }
+ for (OutputPort outputPort : mOutputPorts.values()) {
+ if (outputPort.filterMustClose()) {
+ if (mLogVerbose) Log.v(TAG, "Filter " + this + " must close due to port " + outputPort);
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/FilterContext.java b/media/mca/filterfw/java/android/filterfw/core/FilterContext.java
new file mode 100644
index 0000000..3c79d1b
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/FilterContext.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameManager;
+import android.filterfw.core.GLEnvironment;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @hide
+ */
+public class FilterContext {
+
+ private FrameManager mFrameManager;
+ private GLEnvironment mGLEnvironment;
+ private HashMap<String, Frame> mStoredFrames = new HashMap<String, Frame>();
+ private Set<FilterGraph> mGraphs = new HashSet<FilterGraph>();
+
+ public FrameManager getFrameManager() {
+ return mFrameManager;
+ }
+
+ public void setFrameManager(FrameManager manager) {
+ if (manager == null) {
+ throw new NullPointerException("Attempting to set null FrameManager!");
+ } else if (manager.getContext() != null) {
+ throw new IllegalArgumentException("Attempting to set FrameManager which is already "
+ + "bound to another FilterContext!");
+ } else {
+ mFrameManager = manager;
+ mFrameManager.setContext(this);
+ }
+ }
+
+ public GLEnvironment getGLEnvironment() {
+ return mGLEnvironment;
+ }
+
+ public void initGLEnvironment(GLEnvironment environment) {
+ if (mGLEnvironment == null) {
+ mGLEnvironment = environment;
+ } else {
+ throw new RuntimeException("Attempting to re-initialize GL Environment for " +
+ "FilterContext!");
+ }
+ }
+
+ public interface OnFrameReceivedListener {
+ public void onFrameReceived(Filter filter, Frame frame, Object userData);
+ }
+
+ public synchronized void storeFrame(String key, Frame frame) {
+ Frame storedFrame = fetchFrame(key);
+ if (storedFrame != null) {
+ storedFrame.release();
+ }
+ frame.onFrameStore();
+ mStoredFrames.put(key, frame.retain());
+ }
+
+ public synchronized Frame fetchFrame(String key) {
+ Frame frame = mStoredFrames.get(key);
+ if (frame != null) {
+ frame.onFrameFetch();
+ }
+ return frame;
+ }
+
+ public synchronized void removeFrame(String key) {
+ Frame frame = mStoredFrames.get(key);
+ if (frame != null) {
+ mStoredFrames.remove(key);
+ frame.release();
+ }
+ }
+
+ public synchronized void tearDown() {
+ // Release stored frames
+ for (Frame frame : mStoredFrames.values()) {
+ frame.release();
+ }
+ mStoredFrames.clear();
+
+ // Release graphs
+ for (FilterGraph graph : mGraphs) {
+ graph.tearDown(this);
+ }
+ mGraphs.clear();
+
+ // Release frame manager
+ if (mFrameManager != null) {
+ mFrameManager.tearDown();
+ mFrameManager = null;
+ }
+
+ // Release GL context
+ if (mGLEnvironment != null) {
+ mGLEnvironment.tearDown();
+ mGLEnvironment = null;
+ }
+ }
+
+ final void addGraph(FilterGraph graph) {
+ mGraphs.add(graph);
+ }
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/FilterFactory.java b/media/mca/filterfw/java/android/filterfw/core/FilterFactory.java
new file mode 100644
index 0000000..779df99
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/FilterFactory.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import android.filterfw.core.Filter;
+import android.util.Log;
+
+import dalvik.system.PathClassLoader;
+
+import java.lang.reflect.Constructor;
+import java.lang.ClassLoader;
+import java.lang.Thread;
+import java.util.HashSet;
+
+/**
+ * @hide
+ */
+public class FilterFactory {
+
+ private static FilterFactory mSharedFactory;
+ private HashSet<String> mPackages = new HashSet<String>();
+
+ private static ClassLoader mCurrentClassLoader;
+ private static HashSet<String> mLibraries;
+ private static Object mClassLoaderGuard;
+
+ static {
+ mCurrentClassLoader = Thread.currentThread().getContextClassLoader();
+ mLibraries = new HashSet<String>();
+ mClassLoaderGuard = new Object();
+ }
+
+ private static final String TAG = "FilterFactory";
+ private static boolean mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+
+ public static FilterFactory sharedFactory() {
+ if (mSharedFactory == null) {
+ mSharedFactory = new FilterFactory();
+ }
+ return mSharedFactory;
+ }
+
+ /**
+ * Adds a new Java library to the list to be scanned for filters.
+ * libraryPath must be an absolute path of the jar file. This needs to be
+ * static because only one classloader per process can open a shared native
+ * library, which a filter may well have.
+ */
+ public static void addFilterLibrary(String libraryPath) {
+ if (mLogVerbose) Log.v(TAG, "Adding filter library " + libraryPath);
+ synchronized(mClassLoaderGuard) {
+ if (mLibraries.contains(libraryPath)) {
+ if (mLogVerbose) Log.v(TAG, "Library already added");
+ return;
+ }
+ mLibraries.add(libraryPath);
+ // Chain another path loader to the current chain
+ mCurrentClassLoader = new PathClassLoader(libraryPath, mCurrentClassLoader);
+ }
+ }
+
+ public void addPackage(String packageName) {
+ if (mLogVerbose) Log.v(TAG, "Adding package " + packageName);
+ /* TODO: This should use a getPackage call in the caller's context, but no such method exists.
+ Package pkg = Package.getPackage(packageName);
+ if (pkg == null) {
+ throw new IllegalArgumentException("Unknown filter package '" + packageName + "'!");
+ }
+ */
+ mPackages.add(packageName);
+ }
+
+ public Filter createFilterByClassName(String className, String filterName) {
+ if (mLogVerbose) Log.v(TAG, "Looking up class " + className);
+ Class filterClass = null;
+
+ // Look for the class in the imported packages
+ for (String packageName : mPackages) {
+ try {
+ if (mLogVerbose) Log.v(TAG, "Trying "+packageName + "." + className);
+ synchronized(mClassLoaderGuard) {
+ filterClass = mCurrentClassLoader.loadClass(packageName + "." + className);
+ }
+ } catch (ClassNotFoundException e) {
+ continue;
+ }
+ // Exit loop if class was found.
+ if (filterClass != null) {
+ break;
+ }
+ }
+ if (filterClass == null) {
+ throw new IllegalArgumentException("Unknown filter class '" + className + "'!");
+ }
+ return createFilterByClass(filterClass, filterName);
+ }
+
+ public Filter createFilterByClass(Class filterClass, String filterName) {
+ // Make sure this is a Filter subclass
+ try {
+ filterClass.asSubclass(Filter.class);
+ } catch (ClassCastException e) {
+ throw new IllegalArgumentException("Attempting to allocate class '" + filterClass
+ + "' which is not a subclass of Filter!");
+ }
+
+ // Look for the correct constructor
+ Constructor filterConstructor = null;
+ try {
+ filterConstructor = filterClass.getConstructor(String.class);
+ } catch (NoSuchMethodException e) {
+ throw new IllegalArgumentException("The filter class '" + filterClass
+ + "' does not have a constructor of the form <init>(String name)!");
+ }
+
+ // Construct the filter
+ Filter filter = null;
+ try {
+ filter = (Filter)filterConstructor.newInstance(filterName);
+ } catch (Throwable t) {
+ // Condition checked below
+ }
+
+ if (filter == null) {
+ throw new IllegalArgumentException("Could not construct the filter '"
+ + filterName + "'!");
+ }
+ return filter;
+ }
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/FilterFunction.java b/media/mca/filterfw/java/android/filterfw/core/FilterFunction.java
new file mode 100644
index 0000000..ce81a18
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/FilterFunction.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import java.util.Map.Entry;
+
+/**
+ * @hide
+ */
+public class FilterFunction {
+
+ private Filter mFilter;
+ private FilterContext mFilterContext;
+ private boolean mFilterIsSetup = false;
+ private FrameHolderPort[] mResultHolders;
+
+ private class FrameHolderPort extends StreamPort {
+ public FrameHolderPort() {
+ super(null, "holder");
+ }
+ }
+
+ public FilterFunction(FilterContext context, Filter filter) {
+ mFilterContext = context;
+ mFilter = filter;
+ }
+
+ public Frame execute(KeyValueMap inputMap) {
+ int filterOutCount = mFilter.getNumberOfOutputs();
+
+ // Sanity checks
+ if (filterOutCount > 1) {
+ throw new RuntimeException("Calling execute on filter " + mFilter + " with multiple "
+ + "outputs! Use executeMulti() instead!");
+ }
+
+ // Setup filter
+ if (!mFilterIsSetup) {
+ connectFilterOutputs();
+ mFilterIsSetup = true;
+ }
+
+ // Make sure GL environment is active
+ boolean didActivateGLEnv = false;
+ GLEnvironment glEnv = mFilterContext.getGLEnvironment();
+ if (glEnv != null && !glEnv.isActive()) {
+ glEnv.activate();
+ didActivateGLEnv = true;
+ }
+
+ // Setup the inputs
+ for (Entry<String, Object> entry : inputMap.entrySet()) {
+ if (entry.getValue() instanceof Frame) {
+ mFilter.pushInputFrame(entry.getKey(), (Frame)entry.getValue());
+ } else {
+ mFilter.pushInputValue(entry.getKey(), entry.getValue());
+ }
+ }
+
+ // Process the filter
+ if (mFilter.getStatus() != Filter.STATUS_PROCESSING) {
+ mFilter.openOutputs();
+ }
+
+ mFilter.performProcess(mFilterContext);
+
+ // Create result handle
+ Frame result = null;
+ if (filterOutCount == 1 && mResultHolders[0].hasFrame()) {
+ result = mResultHolders[0].pullFrame();
+ }
+
+ // Deactivate GL environment if activated
+ if (didActivateGLEnv) {
+ glEnv.deactivate();
+ }
+
+ return result;
+ }
+
+ public Frame executeWithArgList(Object... inputs) {
+ return execute(KeyValueMap.fromKeyValues(inputs));
+ }
+
+ public void close() {
+ mFilter.performClose(mFilterContext);
+ }
+
+ public FilterContext getContext() {
+ return mFilterContext;
+ }
+
+ public Filter getFilter() {
+ return mFilter;
+ }
+
+ public void setInputFrame(String input, Frame frame) {
+ mFilter.setInputFrame(input, frame);
+ }
+
+ public void setInputValue(String input, Object value) {
+ mFilter.setInputValue(input, value);
+ }
+
+ public void tearDown() {
+ mFilter.performTearDown(mFilterContext);
+ mFilter = null;
+ }
+
+ @Override
+ public String toString() {
+ return mFilter.getName();
+ }
+
+ private void connectFilterOutputs() {
+ int i = 0;
+ mResultHolders = new FrameHolderPort[mFilter.getNumberOfOutputs()];
+ for (OutputPort outputPort : mFilter.getOutputPorts()) {
+ mResultHolders[i] = new FrameHolderPort();
+ outputPort.connectTo(mResultHolders[i]);
+ ++i;
+ }
+ }
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/FilterGraph.java b/media/mca/filterfw/java/android/filterfw/core/FilterGraph.java
new file mode 100644
index 0000000..12f7892
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/FilterGraph.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.Stack;
+
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.KeyValueMap;
+import android.filterpacks.base.FrameBranch;
+import android.filterpacks.base.NullFilter;
+
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class FilterGraph {
+
+ private HashSet<Filter> mFilters = new HashSet<Filter>();
+ private HashMap<String, Filter> mNameMap = new HashMap<String, Filter>();
+ private HashMap<OutputPort, LinkedList<InputPort>> mPreconnections = new
+ HashMap<OutputPort, LinkedList<InputPort>>();
+
+ public static final int AUTOBRANCH_OFF = 0;
+ public static final int AUTOBRANCH_SYNCED = 1;
+ public static final int AUTOBRANCH_UNSYNCED = 2;
+
+ public static final int TYPECHECK_OFF = 0;
+ public static final int TYPECHECK_DYNAMIC = 1;
+ public static final int TYPECHECK_STRICT = 2;
+
+ private boolean mIsReady = false;
+ private int mAutoBranchMode = AUTOBRANCH_OFF;
+ private int mTypeCheckMode = TYPECHECK_STRICT;
+ private boolean mDiscardUnconnectedOutputs = false;
+
+ private boolean mLogVerbose;
+ private String TAG = "FilterGraph";
+
+ public FilterGraph() {
+ mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+ }
+
+ public boolean addFilter(Filter filter) {
+ if (!containsFilter(filter)) {
+ mFilters.add(filter);
+ mNameMap.put(filter.getName(), filter);
+ return true;
+ }
+ return false;
+ }
+
+ public boolean containsFilter(Filter filter) {
+ return mFilters.contains(filter);
+ }
+
+ public Filter getFilter(String name) {
+ return mNameMap.get(name);
+ }
+
+ public void connect(Filter source,
+ String outputName,
+ Filter target,
+ String inputName) {
+ if (source == null || target == null) {
+ throw new IllegalArgumentException("Passing null Filter in connect()!");
+ } else if (!containsFilter(source) || !containsFilter(target)) {
+ throw new RuntimeException("Attempting to connect filter not in graph!");
+ }
+
+ OutputPort outPort = source.getOutputPort(outputName);
+ InputPort inPort = target.getInputPort(inputName);
+ if (outPort == null) {
+ throw new RuntimeException("Unknown output port '" + outputName + "' on Filter " +
+ source + "!");
+ } else if (inPort == null) {
+ throw new RuntimeException("Unknown input port '" + inputName + "' on Filter " +
+ target + "!");
+ }
+
+ preconnect(outPort, inPort);
+ }
+
+ public void connect(String sourceName,
+ String outputName,
+ String targetName,
+ String inputName) {
+ Filter source = getFilter(sourceName);
+ Filter target = getFilter(targetName);
+ if (source == null) {
+ throw new RuntimeException(
+ "Attempting to connect unknown source filter '" + sourceName + "'!");
+ } else if (target == null) {
+ throw new RuntimeException(
+ "Attempting to connect unknown target filter '" + targetName + "'!");
+ }
+ connect(source, outputName, target, inputName);
+ }
+
+ public Set<Filter> getFilters() {
+ return mFilters;
+ }
+
+ public void beginProcessing() {
+ if (mLogVerbose) Log.v(TAG, "Opening all filter connections...");
+ for (Filter filter : mFilters) {
+ filter.openOutputs();
+ }
+ mIsReady = true;
+ }
+
+ public void flushFrames() {
+ for (Filter filter : mFilters) {
+ filter.clearOutputs();
+ }
+ }
+
+ public void closeFilters(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Closing all filters...");
+ for (Filter filter : mFilters) {
+ filter.performClose(context);
+ }
+ mIsReady = false;
+ }
+
+ public boolean isReady() {
+ return mIsReady;
+ }
+
+ public void setAutoBranchMode(int autoBranchMode) {
+ mAutoBranchMode = autoBranchMode;
+ }
+
+ public void setDiscardUnconnectedOutputs(boolean discard) {
+ mDiscardUnconnectedOutputs = discard;
+ }
+
+ public void setTypeCheckMode(int typeCheckMode) {
+ mTypeCheckMode = typeCheckMode;
+ }
+
+ public void tearDown(FilterContext context) {
+ if (!mFilters.isEmpty()) {
+ flushFrames();
+ for (Filter filter : mFilters) {
+ filter.performTearDown(context);
+ }
+ mFilters.clear();
+ mNameMap.clear();
+ mIsReady = false;
+ }
+ }
+
+ private boolean readyForProcessing(Filter filter, Set<Filter> processed) {
+ // Check if this has been already processed
+ if (processed.contains(filter)) {
+ return false;
+ }
+
+ // Check if all dependencies have been processed
+ for (InputPort port : filter.getInputPorts()) {
+ Filter dependency = port.getSourceFilter();
+ if (dependency != null && !processed.contains(dependency)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void runTypeCheck() {
+ Stack<Filter> filterStack = new Stack<Filter>();
+ Set<Filter> processedFilters = new HashSet<Filter>();
+ filterStack.addAll(getSourceFilters());
+
+ while (!filterStack.empty()) {
+ // Get current filter and mark as processed
+ Filter filter = filterStack.pop();
+ processedFilters.add(filter);
+
+ // Anchor output formats
+ updateOutputs(filter);
+
+ // Perform type check
+ if (mLogVerbose) Log.v(TAG, "Running type check on " + filter + "...");
+ runTypeCheckOn(filter);
+
+ // Push connected filters onto stack
+ for (OutputPort port : filter.getOutputPorts()) {
+ Filter target = port.getTargetFilter();
+ if (target != null && readyForProcessing(target, processedFilters)) {
+ filterStack.push(target);
+ }
+ }
+ }
+
+ // Make sure all ports were setup
+ if (processedFilters.size() != getFilters().size()) {
+ throw new RuntimeException("Could not schedule all filters! Is your graph malformed?");
+ }
+ }
+
+ private void updateOutputs(Filter filter) {
+ for (OutputPort outputPort : filter.getOutputPorts()) {
+ InputPort inputPort = outputPort.getBasePort();
+ if (inputPort != null) {
+ FrameFormat inputFormat = inputPort.getSourceFormat();
+ FrameFormat outputFormat = filter.getOutputFormat(outputPort.getName(),
+ inputFormat);
+ if (outputFormat == null) {
+ throw new RuntimeException("Filter did not return an output format for "
+ + outputPort + "!");
+ }
+ outputPort.setPortFormat(outputFormat);
+ }
+ }
+ }
+
+ private void runTypeCheckOn(Filter filter) {
+ for (InputPort inputPort : filter.getInputPorts()) {
+ if (mLogVerbose) Log.v(TAG, "Type checking port " + inputPort);
+ FrameFormat sourceFormat = inputPort.getSourceFormat();
+ FrameFormat targetFormat = inputPort.getPortFormat();
+ if (sourceFormat != null && targetFormat != null) {
+ if (mLogVerbose) Log.v(TAG, "Checking " + sourceFormat + " against " + targetFormat + ".");
+
+ boolean compatible = true;
+ switch (mTypeCheckMode) {
+ case TYPECHECK_OFF:
+ inputPort.setChecksType(false);
+ break;
+ case TYPECHECK_DYNAMIC:
+ compatible = sourceFormat.mayBeCompatibleWith(targetFormat);
+ inputPort.setChecksType(true);
+ break;
+ case TYPECHECK_STRICT:
+ compatible = sourceFormat.isCompatibleWith(targetFormat);
+ inputPort.setChecksType(false);
+ break;
+ }
+
+ if (!compatible) {
+ throw new RuntimeException("Type mismatch: Filter " + filter + " expects a "
+ + "format of type " + targetFormat + " but got a format of type "
+ + sourceFormat + "!");
+ }
+ }
+ }
+ }
+
+ private void checkConnections() {
+ // TODO
+ }
+
+ private void discardUnconnectedOutputs() {
+ // Connect unconnected ports to Null filters
+ LinkedList<Filter> addedFilters = new LinkedList<Filter>();
+ for (Filter filter : mFilters) {
+ int id = 0;
+ for (OutputPort port : filter.getOutputPorts()) {
+ if (!port.isConnected()) {
+ if (mLogVerbose) Log.v(TAG, "Autoconnecting unconnected " + port + " to Null filter.");
+ NullFilter nullFilter = new NullFilter(filter.getName() + "ToNull" + id);
+ nullFilter.init();
+ addedFilters.add(nullFilter);
+ port.connectTo(nullFilter.getInputPort("frame"));
+ ++id;
+ }
+ }
+ }
+ // Add all added filters to this graph
+ for (Filter filter : addedFilters) {
+ addFilter(filter);
+ }
+ }
+
+ private void removeFilter(Filter filter) {
+ mFilters.remove(filter);
+ mNameMap.remove(filter.getName());
+ }
+
+ private void preconnect(OutputPort outPort, InputPort inPort) {
+ LinkedList<InputPort> targets;
+ targets = mPreconnections.get(outPort);
+ if (targets == null) {
+ targets = new LinkedList<InputPort>();
+ mPreconnections.put(outPort, targets);
+ }
+ targets.add(inPort);
+ }
+
+ private void connectPorts() {
+ int branchId = 1;
+ for (Entry<OutputPort, LinkedList<InputPort>> connection : mPreconnections.entrySet()) {
+ OutputPort outputPort = connection.getKey();
+ LinkedList<InputPort> inputPorts = connection.getValue();
+ if (inputPorts.size() == 1) {
+ outputPort.connectTo(inputPorts.get(0));
+ } else if (mAutoBranchMode == AUTOBRANCH_OFF) {
+ throw new RuntimeException("Attempting to connect " + outputPort + " to multiple "
+ + "filter ports! Enable auto-branching to allow this.");
+ } else {
+ if (mLogVerbose) Log.v(TAG, "Creating branch for " + outputPort + "!");
+ FrameBranch branch = null;
+ if (mAutoBranchMode == AUTOBRANCH_SYNCED) {
+ branch = new FrameBranch("branch" + branchId++);
+ } else {
+ throw new RuntimeException("TODO: Unsynced branches not implemented yet!");
+ }
+ KeyValueMap branchParams = new KeyValueMap();
+ branch.initWithAssignmentList("outputs", inputPorts.size());
+ addFilter(branch);
+ outputPort.connectTo(branch.getInputPort("in"));
+ Iterator<InputPort> inputPortIter = inputPorts.iterator();
+ for (OutputPort branchOutPort : ((Filter)branch).getOutputPorts()) {
+ branchOutPort.connectTo(inputPortIter.next());
+ }
+ }
+ }
+ mPreconnections.clear();
+ }
+
+ private HashSet<Filter> getSourceFilters() {
+ HashSet<Filter> sourceFilters = new HashSet<Filter>();
+ for (Filter filter : getFilters()) {
+ if (filter.getNumberOfConnectedInputs() == 0) {
+ if (mLogVerbose) Log.v(TAG, "Found source filter: " + filter);
+ sourceFilters.add(filter);
+ }
+ }
+ return sourceFilters;
+ }
+
+ // Core internal methods /////////////////////////////////////////////////////////////////////////
+ void setupFilters() {
+ if (mDiscardUnconnectedOutputs) {
+ discardUnconnectedOutputs();
+ }
+ connectPorts();
+ checkConnections();
+ runTypeCheck();
+ }
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/FilterPort.java b/media/mca/filterfw/java/android/filterfw/core/FilterPort.java
new file mode 100644
index 0000000..9734b89
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/FilterPort.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FrameFormat;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public abstract class FilterPort {
+
+ protected Filter mFilter;
+ protected String mName;
+ protected FrameFormat mPortFormat;
+ protected boolean mIsBlocking = true;
+ protected boolean mIsOpen = false;
+ protected boolean mChecksType = false;
+ private boolean mLogVerbose;
+ private static final String TAG = "FilterPort";
+
+ public FilterPort(Filter filter, String name) {
+ mName = name;
+ mFilter = filter;
+ mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+ }
+
+ public boolean isAttached() {
+ return mFilter != null;
+ }
+
+ public FrameFormat getPortFormat() {
+ return mPortFormat;
+ }
+
+ public void setPortFormat(FrameFormat format) {
+ mPortFormat = format;
+ }
+
+ public Filter getFilter() {
+ return mFilter;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public void setBlocking(boolean blocking) {
+ mIsBlocking = blocking;
+ }
+
+ public void setChecksType(boolean checksType) {
+ mChecksType = checksType;
+ }
+
+ public void open() {
+ if (!mIsOpen) {
+ if (mLogVerbose) Log.v(TAG, "Opening " + this);
+ }
+ mIsOpen = true;
+ }
+
+ public void close() {
+ if (mIsOpen) {
+ if (mLogVerbose) Log.v(TAG, "Closing " + this);
+ }
+ mIsOpen = false;
+ }
+
+ public boolean isOpen() {
+ return mIsOpen;
+ }
+
+ public boolean isBlocking() {
+ return mIsBlocking;
+ }
+
+ public abstract boolean filterMustClose();
+
+ public abstract boolean isReady();
+
+ public abstract void pushFrame(Frame frame);
+
+ public abstract void setFrame(Frame frame);
+
+ public abstract Frame pullFrame();
+
+ public abstract boolean hasFrame();
+
+ public abstract void clear();
+
+ public String toString() {
+ return "port '" + mName + "' of " + mFilter;
+ }
+
+ protected void assertPortIsOpen() {
+ if (!isOpen()) {
+ throw new RuntimeException("Illegal operation on closed " + this + "!");
+ }
+ }
+
+ protected void checkFrameType(Frame frame, boolean forceCheck) {
+ if ((mChecksType || forceCheck)
+ && mPortFormat != null
+ && !frame.getFormat().isCompatibleWith(mPortFormat)) {
+ throw new RuntimeException("Frame passed to " + this + " is of incorrect type! "
+ + "Expected " + mPortFormat + " but got " + frame.getFormat());
+ }
+ }
+
+ protected void checkFrameManager(Frame frame, FilterContext context) {
+ if (frame.getFrameManager() != null
+ && frame.getFrameManager() != context.getFrameManager()) {
+ throw new RuntimeException("Frame " + frame + " is managed by foreign FrameManager! ");
+ }
+ }
+}
+
diff --git a/media/mca/filterfw/java/android/filterfw/core/FilterSurfaceView.java b/media/mca/filterfw/java/android/filterfw/core/FilterSurfaceView.java
new file mode 100644
index 0000000..49306b2
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/FilterSurfaceView.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+/**
+ * @hide
+ */
+public class FilterSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
+
+ private static int STATE_ALLOCATED = 0;
+ private static int STATE_CREATED = 1;
+ private static int STATE_INITIALIZED = 2;
+
+ private int mState = STATE_ALLOCATED;
+ private SurfaceHolder.Callback mListener;
+ private GLEnvironment mGLEnv;
+ private int mFormat;
+ private int mWidth;
+ private int mHeight;
+ private int mSurfaceId = -1;
+
+ public FilterSurfaceView(Context context) {
+ super(context);
+ getHolder().addCallback(this);
+ }
+
+ public FilterSurfaceView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ getHolder().addCallback(this);
+ }
+
+ public synchronized void bindToListener(SurfaceHolder.Callback listener, GLEnvironment glEnv) {
+ // Make sure we are not bound already
+ if (listener == null) {
+ throw new NullPointerException("Attempting to bind null filter to SurfaceView!");
+ } else if (mListener != null && mListener != listener) {
+ throw new RuntimeException(
+ "Attempting to bind filter " + listener + " to SurfaceView with another open "
+ + "filter " + mListener + " attached already!");
+ }
+
+ // Set listener
+ mListener = listener;
+
+ // Set GLEnv
+ if (mGLEnv != null && mGLEnv != glEnv) {
+ mGLEnv.unregisterSurfaceId(mSurfaceId);
+ }
+ mGLEnv = glEnv;
+
+ // Check if surface has been created already
+ if (mState >= STATE_CREATED) {
+ // Register with env (double registration will be ignored by GLEnv, so we can simply
+ // try to do it here).
+ registerSurface();
+
+ // Forward surface created to listener
+ mListener.surfaceCreated(getHolder());
+
+ // Forward surface changed to listener
+ if (mState == STATE_INITIALIZED) {
+ mListener.surfaceChanged(getHolder(), mFormat, mWidth, mHeight);
+ }
+ }
+ }
+
+ public synchronized void unbind() {
+ mListener = null;
+ }
+
+ public synchronized int getSurfaceId() {
+ return mSurfaceId;
+ }
+
+ public synchronized GLEnvironment getGLEnv() {
+ return mGLEnv;
+ }
+
+ @Override
+ public synchronized void surfaceCreated(SurfaceHolder holder) {
+ mState = STATE_CREATED;
+
+ // Register with GLEnvironment if we have it already
+ if (mGLEnv != null) {
+ registerSurface();
+ }
+
+ // Forward callback to listener
+ if (mListener != null) {
+ mListener.surfaceCreated(holder);
+ }
+ }
+
+ @Override
+ public synchronized void surfaceChanged(SurfaceHolder holder,
+ int format,
+ int width,
+ int height) {
+ // Remember these values
+ mFormat = format;
+ mWidth = width;
+ mHeight = height;
+ mState = STATE_INITIALIZED;
+
+ // Forward to renderer
+ if (mListener != null) {
+ mListener.surfaceChanged(holder, format, width, height);
+ }
+ }
+
+ @Override
+ public synchronized void surfaceDestroyed(SurfaceHolder holder) {
+ mState = STATE_ALLOCATED;
+
+ // Forward to renderer
+ if (mListener != null) {
+ mListener.surfaceDestroyed(holder);
+ }
+
+ // Get rid of internal objects associated with this surface
+ unregisterSurface();
+ }
+
+ private void registerSurface() {
+ mSurfaceId = mGLEnv.registerSurface(getHolder().getSurface());
+ if (mSurfaceId < 0) {
+ throw new RuntimeException("Could not register Surface: " + getHolder().getSurface() +
+ " in FilterSurfaceView!");
+ }
+ }
+ private void unregisterSurface() {
+ if (mGLEnv != null && mSurfaceId > 0) {
+ mGLEnv.unregisterSurfaceId(mSurfaceId);
+ }
+ }
+
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/FinalPort.java b/media/mca/filterfw/java/android/filterfw/core/FinalPort.java
new file mode 100644
index 0000000..ad65169
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/FinalPort.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import java.lang.reflect.Field;
+
+/**
+ * @hide
+ */
+public class FinalPort extends FieldPort {
+
+ public FinalPort(Filter filter, String name, Field field, boolean hasDefault) {
+ super(filter, name, field, hasDefault);
+ }
+
+ @Override
+ protected synchronized void setFieldFrame(Frame frame, boolean isAssignment) {
+ assertPortIsOpen();
+ checkFrameType(frame, isAssignment);
+ if (mFilter.getStatus() != Filter.STATUS_PREINIT) {
+ throw new RuntimeException("Attempting to modify " + this + "!");
+ } else {
+ super.setFieldFrame(frame, isAssignment);
+ super.transfer(null);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "final " + super.toString();
+ }
+
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/Frame.java b/media/mca/filterfw/java/android/filterfw/core/Frame.java
new file mode 100644
index 0000000..ef8c542
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/Frame.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.FrameManager;
+import android.graphics.Bitmap;
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+
+/**
+ * @hide
+ */
+public abstract class Frame {
+
+ public final static int NO_BINDING = 0;
+
+ public final static long TIMESTAMP_NOT_SET = -2;
+ public final static long TIMESTAMP_UNKNOWN = -1;
+
+ private FrameFormat mFormat;
+ private FrameManager mFrameManager;
+ private boolean mReadOnly = false;
+ private boolean mReusable = false;
+ private int mRefCount = 1;
+ private int mBindingType = NO_BINDING;
+ private long mBindingId = 0;
+ private long mTimestamp = TIMESTAMP_NOT_SET;
+
+ Frame(FrameFormat format, FrameManager frameManager) {
+ mFormat = format.mutableCopy();
+ mFrameManager = frameManager;
+ }
+
+ Frame(FrameFormat format, FrameManager frameManager, int bindingType, long bindingId) {
+ mFormat = format.mutableCopy();
+ mFrameManager = frameManager;
+ mBindingType = bindingType;
+ mBindingId = bindingId;
+ }
+
+ public FrameFormat getFormat() {
+ return mFormat;
+ }
+
+ public int getCapacity() {
+ return getFormat().getSize();
+ }
+
+ public boolean isReadOnly() {
+ return mReadOnly;
+ }
+
+ public int getBindingType() {
+ return mBindingType;
+ }
+
+ public long getBindingId() {
+ return mBindingId;
+ }
+
+ public void setObjectValue(Object object) {
+ assertFrameMutable();
+
+ // Attempt to set the value using a specific setter (which may be more optimized), and
+ // fall back to the setGenericObjectValue(...) in case of no match.
+ if (object instanceof int[]) {
+ setInts((int[])object);
+ } else if (object instanceof float[]) {
+ setFloats((float[])object);
+ } else if (object instanceof ByteBuffer) {
+ setData((ByteBuffer)object);
+ } else if (object instanceof Bitmap) {
+ setBitmap((Bitmap)object);
+ } else {
+ setGenericObjectValue(object);
+ }
+ }
+
+ public abstract Object getObjectValue();
+
+ public abstract void setInts(int[] ints);
+
+ public abstract int[] getInts();
+
+ public abstract void setFloats(float[] floats);
+
+ public abstract float[] getFloats();
+
+ public abstract void setData(ByteBuffer buffer, int offset, int length);
+
+ public void setData(ByteBuffer buffer) {
+ setData(buffer, 0, buffer.limit());
+ }
+
+ public void setData(byte[] bytes, int offset, int length) {
+ setData(ByteBuffer.wrap(bytes, offset, length));
+ }
+
+ public abstract ByteBuffer getData();
+
+ public abstract void setBitmap(Bitmap bitmap);
+
+ public abstract Bitmap getBitmap();
+
+ public void setTimestamp(long timestamp) {
+ mTimestamp = timestamp;
+ }
+
+ public long getTimestamp() {
+ return mTimestamp;
+ }
+
+ public void setDataFromFrame(Frame frame) {
+ setData(frame.getData());
+ }
+
+ protected boolean requestResize(int[] newDimensions) {
+ return false;
+ }
+
+ public int getRefCount() {
+ return mRefCount;
+ }
+
+ public Frame release() {
+ if (mFrameManager != null) {
+ return mFrameManager.releaseFrame(this);
+ } else {
+ return this;
+ }
+ }
+
+ public Frame retain() {
+ if (mFrameManager != null) {
+ return mFrameManager.retainFrame(this);
+ } else {
+ return this;
+ }
+ }
+
+ public FrameManager getFrameManager() {
+ return mFrameManager;
+ }
+
+ protected void assertFrameMutable() {
+ if (isReadOnly()) {
+ throw new RuntimeException("Attempting to modify read-only frame!");
+ }
+ }
+
+ protected void setReusable(boolean reusable) {
+ mReusable = reusable;
+ }
+
+ protected void setFormat(FrameFormat format) {
+ mFormat = format.mutableCopy();
+ }
+
+ protected void setGenericObjectValue(Object value) {
+ throw new RuntimeException(
+ "Cannot set object value of unsupported type: " + value.getClass());
+ }
+
+ protected static Bitmap convertBitmapToRGBA(Bitmap bitmap) {
+ if (bitmap.getConfig() == Bitmap.Config.ARGB_8888) {
+ return bitmap;
+ } else {
+ Bitmap result = bitmap.copy(Bitmap.Config.ARGB_8888, false);
+ if (result == null) {
+ throw new RuntimeException("Error converting bitmap to RGBA!");
+ } else if (result.getRowBytes() != result.getWidth() * 4) {
+ throw new RuntimeException("Unsupported row byte count in bitmap!");
+ }
+ return result;
+ }
+ }
+
+ protected void reset(FrameFormat newFormat) {
+ mFormat = newFormat.mutableCopy();
+ mReadOnly = false;
+ mRefCount = 1;
+ }
+
+ /**
+ * Called just before a frame is stored, such as when storing to a cache or context.
+ */
+ protected void onFrameStore() {
+ }
+
+ /**
+ * Called when a frame is fetched from an internal store such as a cache.
+ */
+ protected void onFrameFetch() {
+ }
+
+ // Core internal methods ///////////////////////////////////////////////////////////////////////
+ protected abstract boolean hasNativeAllocation();
+
+ protected abstract void releaseNativeAllocation();
+
+ final int incRefCount() {
+ ++mRefCount;
+ return mRefCount;
+ }
+
+ final int decRefCount() {
+ --mRefCount;
+ return mRefCount;
+ }
+
+ final boolean isReusable() {
+ return mReusable;
+ }
+
+ final void markReadOnly() {
+ mReadOnly = true;
+ }
+
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/FrameFormat.java b/media/mca/filterfw/java/android/filterfw/core/FrameFormat.java
new file mode 100644
index 0000000..8f619be
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/FrameFormat.java
@@ -0,0 +1,439 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+
+import java.util.Arrays;
+import java.util.Map.Entry;
+
+/**
+ * @hide
+ */
+public class FrameFormat {
+
+ public static final int TYPE_UNSPECIFIED = 0;
+ public static final int TYPE_BIT = 1;
+ public static final int TYPE_BYTE = 2;
+ public static final int TYPE_INT16 = 3;
+ public static final int TYPE_INT32 = 4;
+ public static final int TYPE_FLOAT = 5;
+ public static final int TYPE_DOUBLE = 6;
+ public static final int TYPE_POINTER = 7;
+ public static final int TYPE_OBJECT = 8;
+
+ public static final int TARGET_UNSPECIFIED = 0;
+ public static final int TARGET_SIMPLE = 1;
+ public static final int TARGET_NATIVE = 2;
+ public static final int TARGET_GPU = 3;
+ public static final int TARGET_VERTEXBUFFER = 4;
+ public static final int TARGET_RS = 5;
+
+ public static final int SIZE_UNSPECIFIED = 0;
+
+ // TODO: When convenience formats are used, consider changing this to 0 and have the convenience
+ // intializers use a proper BPS.
+ public static final int BYTES_PER_SAMPLE_UNSPECIFIED = 1;
+
+ protected static final int SIZE_UNKNOWN = -1;
+
+ protected int mBaseType = TYPE_UNSPECIFIED;
+ protected int mBytesPerSample = 1;
+ protected int mSize = SIZE_UNKNOWN;
+ protected int mTarget = TARGET_UNSPECIFIED;
+ protected int[] mDimensions;
+ protected KeyValueMap mMetaData;
+ protected Class mObjectClass;
+
+ protected FrameFormat() {
+ }
+
+ public FrameFormat(int baseType, int target) {
+ mBaseType = baseType;
+ mTarget = target;
+ initDefaults();
+ }
+
+ public static FrameFormat unspecified() {
+ return new FrameFormat(TYPE_UNSPECIFIED, TARGET_UNSPECIFIED);
+ }
+
+ public int getBaseType() {
+ return mBaseType;
+ }
+
+ public boolean isBinaryDataType() {
+ return mBaseType >= TYPE_BIT && mBaseType <= TYPE_DOUBLE;
+ }
+
+ public int getBytesPerSample() {
+ return mBytesPerSample;
+ }
+
+ public int getValuesPerSample() {
+ return mBytesPerSample / bytesPerSampleOf(mBaseType);
+ }
+
+ public int getTarget() {
+ return mTarget;
+ }
+
+ public int[] getDimensions() {
+ return mDimensions;
+ }
+
+ public int getDimension(int i) {
+ return mDimensions[i];
+ }
+
+ public int getDimensionCount() {
+ return mDimensions == null ? 0 : mDimensions.length;
+ }
+
+ public boolean hasMetaKey(String key) {
+ return mMetaData != null ? mMetaData.containsKey(key) : false;
+ }
+
+ public boolean hasMetaKey(String key, Class expectedClass) {
+ if (mMetaData != null && mMetaData.containsKey(key)) {
+ if (!expectedClass.isAssignableFrom(mMetaData.get(key).getClass())) {
+ throw new RuntimeException(
+ "FrameFormat meta-key '" + key + "' is of type " +
+ mMetaData.get(key).getClass() + " but expected to be of type " +
+ expectedClass + "!");
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public Object getMetaValue(String key) {
+ return mMetaData != null ? mMetaData.get(key) : null;
+ }
+
+ public int getNumberOfDimensions() {
+ return mDimensions != null ? mDimensions.length : 0;
+ }
+
+ public int getLength() {
+ return (mDimensions != null && mDimensions.length >= 1) ? mDimensions[0] : -1;
+ }
+
+ public int getWidth() {
+ return getLength();
+ }
+
+ public int getHeight() {
+ return (mDimensions != null && mDimensions.length >= 2) ? mDimensions[1] : -1;
+ }
+
+ public int getDepth() {
+ return (mDimensions != null && mDimensions.length >= 3) ? mDimensions[2] : -1;
+ }
+
+ public int getSize() {
+ if (mSize == SIZE_UNKNOWN) mSize = calcSize(mDimensions);
+ return mSize;
+ }
+
+ public Class getObjectClass() {
+ return mObjectClass;
+ }
+
+ public MutableFrameFormat mutableCopy() {
+ MutableFrameFormat result = new MutableFrameFormat();
+ result.setBaseType(getBaseType());
+ result.setTarget(getTarget());
+ result.setBytesPerSample(getBytesPerSample());
+ result.setDimensions(getDimensions());
+ result.setObjectClass(getObjectClass());
+ result.mMetaData = mMetaData == null ? null : (KeyValueMap)mMetaData.clone();
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (this == object) {
+ return true;
+ }
+
+ if (!(object instanceof FrameFormat)) {
+ return false;
+ }
+
+ FrameFormat format = (FrameFormat)object;
+ return format.mBaseType == mBaseType &&
+ format.mTarget == mTarget &&
+ format.mBytesPerSample == mBytesPerSample &&
+ Arrays.equals(format.mDimensions, mDimensions) &&
+ format.mMetaData.equals(mMetaData);
+ }
+
+ @Override
+ public int hashCode() {
+ return 4211 ^ mBaseType ^ mBytesPerSample ^ getSize();
+ }
+
+ public boolean isCompatibleWith(FrameFormat specification) {
+ // Check base type
+ if (specification.getBaseType() != TYPE_UNSPECIFIED
+ && getBaseType() != specification.getBaseType()) {
+ return false;
+ }
+
+ // Check target
+ if (specification.getTarget() != TARGET_UNSPECIFIED
+ && getTarget() != specification.getTarget()) {
+ return false;
+ }
+
+ // Check bytes per sample
+ if (specification.getBytesPerSample() != BYTES_PER_SAMPLE_UNSPECIFIED
+ && getBytesPerSample() != specification.getBytesPerSample()) {
+ return false;
+ }
+
+ // Check number of dimensions
+ if (specification.getDimensionCount() > 0
+ && getDimensionCount() != specification.getDimensionCount()) {
+ return false;
+ }
+
+ // Check dimensions
+ for (int i = 0; i < specification.getDimensionCount(); ++i) {
+ int specDim = specification.getDimension(i);
+ if (specDim != SIZE_UNSPECIFIED && getDimension(i) != specDim) {
+ return false;
+ }
+ }
+
+ // Check class
+ if (specification.getObjectClass() != null) {
+ if (getObjectClass() == null
+ || !specification.getObjectClass().isAssignableFrom(getObjectClass())) {
+ return false;
+ }
+ }
+
+ // Check meta-data
+ if (specification.mMetaData != null) {
+ for (String specKey : specification.mMetaData.keySet()) {
+ if (mMetaData == null
+ || !mMetaData.containsKey(specKey)
+ || !mMetaData.get(specKey).equals(specification.mMetaData.get(specKey))) {
+ return false;
+ }
+ }
+ }
+
+ // Passed all the tests
+ return true;
+ }
+
+ public boolean mayBeCompatibleWith(FrameFormat specification) {
+ // Check base type
+ if (specification.getBaseType() != TYPE_UNSPECIFIED
+ && getBaseType() != TYPE_UNSPECIFIED
+ && getBaseType() != specification.getBaseType()) {
+ return false;
+ }
+
+ // Check target
+ if (specification.getTarget() != TARGET_UNSPECIFIED
+ && getTarget() != TARGET_UNSPECIFIED
+ && getTarget() != specification.getTarget()) {
+ return false;
+ }
+
+ // Check bytes per sample
+ if (specification.getBytesPerSample() != BYTES_PER_SAMPLE_UNSPECIFIED
+ && getBytesPerSample() != BYTES_PER_SAMPLE_UNSPECIFIED
+ && getBytesPerSample() != specification.getBytesPerSample()) {
+ return false;
+ }
+
+ // Check number of dimensions
+ if (specification.getDimensionCount() > 0
+ && getDimensionCount() > 0
+ && getDimensionCount() != specification.getDimensionCount()) {
+ return false;
+ }
+
+ // Check dimensions
+ for (int i = 0; i < specification.getDimensionCount(); ++i) {
+ int specDim = specification.getDimension(i);
+ if (specDim != SIZE_UNSPECIFIED
+ && getDimension(i) != SIZE_UNSPECIFIED
+ && getDimension(i) != specDim) {
+ return false;
+ }
+ }
+
+ // Check class
+ if (specification.getObjectClass() != null && getObjectClass() != null) {
+ if (!specification.getObjectClass().isAssignableFrom(getObjectClass())) {
+ return false;
+ }
+ }
+
+ // Check meta-data
+ if (specification.mMetaData != null && mMetaData != null) {
+ for (String specKey : specification.mMetaData.keySet()) {
+ if (mMetaData.containsKey(specKey)
+ && !mMetaData.get(specKey).equals(specification.mMetaData.get(specKey))) {
+ return false;
+ }
+ }
+ }
+
+ // Passed all the tests
+ return true;
+ }
+
+ public static int bytesPerSampleOf(int baseType) {
+ // Defaults based on base-type
+ switch (baseType) {
+ case TYPE_BIT:
+ case TYPE_BYTE:
+ return 1;
+ case TYPE_INT16:
+ return 2;
+ case TYPE_INT32:
+ case TYPE_FLOAT:
+ case TYPE_POINTER:
+ return 4;
+ case TYPE_DOUBLE:
+ return 8;
+ default:
+ return 1;
+ }
+ }
+
+ public static String dimensionsToString(int[] dimensions) {
+ StringBuffer buffer = new StringBuffer();
+ if (dimensions != null) {
+ int n = dimensions.length;
+ for (int i = 0; i < n; ++i) {
+ if (dimensions[i] == SIZE_UNSPECIFIED) {
+ buffer.append("[]");
+ } else {
+ buffer.append("[" + String.valueOf(dimensions[i]) + "]");
+ }
+ }
+ }
+ return buffer.toString();
+ }
+
+ public static String baseTypeToString(int baseType) {
+ switch (baseType) {
+ case TYPE_UNSPECIFIED: return "unspecified";
+ case TYPE_BIT: return "bit";
+ case TYPE_BYTE: return "byte";
+ case TYPE_INT16: return "int";
+ case TYPE_INT32: return "int";
+ case TYPE_FLOAT: return "float";
+ case TYPE_DOUBLE: return "double";
+ case TYPE_POINTER: return "pointer";
+ case TYPE_OBJECT: return "object";
+ default: return "unknown";
+ }
+ }
+
+ public static String targetToString(int target) {
+ switch (target) {
+ case TARGET_UNSPECIFIED: return "unspecified";
+ case TARGET_SIMPLE: return "simple";
+ case TARGET_NATIVE: return "native";
+ case TARGET_GPU: return "gpu";
+ case TARGET_VERTEXBUFFER: return "vbo";
+ case TARGET_RS: return "renderscript";
+ default: return "unknown";
+ }
+ }
+
+ public static String metaDataToString(KeyValueMap metaData) {
+ if (metaData == null) {
+ return "";
+ } else {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("{ ");
+ for (Entry<String, Object> entry : metaData.entrySet()) {
+ buffer.append(entry.getKey() + ": " + entry.getValue() + " ");
+ }
+ buffer.append("}");
+ return buffer.toString();
+ }
+ }
+
+ public static int readTargetString(String targetString) {
+ if (targetString.equalsIgnoreCase("CPU") || targetString.equalsIgnoreCase("NATIVE")) {
+ return FrameFormat.TARGET_NATIVE;
+ } else if (targetString.equalsIgnoreCase("GPU")) {
+ return FrameFormat.TARGET_GPU;
+ } else if (targetString.equalsIgnoreCase("SIMPLE")) {
+ return FrameFormat.TARGET_SIMPLE;
+ } else if (targetString.equalsIgnoreCase("VERTEXBUFFER")) {
+ return FrameFormat.TARGET_VERTEXBUFFER;
+ } else if (targetString.equalsIgnoreCase("UNSPECIFIED")) {
+ return FrameFormat.TARGET_UNSPECIFIED;
+ } else {
+ throw new RuntimeException("Unknown target type '" + targetString + "'!");
+ }
+ }
+
+ // TODO: FromString
+
+ public String toString() {
+ int valuesPerSample = getValuesPerSample();
+ String sampleCountString = valuesPerSample == 1 ? "" : String.valueOf(valuesPerSample);
+ String targetString = mTarget == TARGET_UNSPECIFIED ? "" : (targetToString(mTarget) + " ");
+ String classString = mObjectClass == null
+ ? ""
+ : (" class(" + mObjectClass.getSimpleName() + ") ");
+
+ return targetString
+ + baseTypeToString(mBaseType)
+ + sampleCountString
+ + dimensionsToString(mDimensions)
+ + classString
+ + metaDataToString(mMetaData);
+ }
+
+ private void initDefaults() {
+ mBytesPerSample = bytesPerSampleOf(mBaseType);
+ }
+
+ // Core internal methods ///////////////////////////////////////////////////////////////////////
+ int calcSize(int[] dimensions) {
+ if (dimensions != null && dimensions.length > 0) {
+ int size = getBytesPerSample();
+ for (int dim : dimensions) {
+ size *= dim;
+ }
+ return size;
+ }
+ return 0;
+ }
+
+ boolean isReplaceableBy(FrameFormat format) {
+ return mTarget == format.mTarget
+ && getSize() == format.getSize()
+ && Arrays.equals(format.mDimensions, mDimensions);
+ }
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/FrameManager.java b/media/mca/filterfw/java/android/filterfw/core/FrameManager.java
new file mode 100644
index 0000000..8d6c483
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/FrameManager.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.MutableFrameFormat;
+
+/**
+ * @hide
+ */
+public abstract class FrameManager {
+
+ private FilterContext mContext;
+
+ public abstract Frame newFrame(FrameFormat format);
+
+ public abstract Frame newBoundFrame(FrameFormat format, int bindingType, long bindingId);
+
+ public Frame duplicateFrame(Frame frame) {
+ Frame result = newFrame(frame.getFormat());
+ result.setDataFromFrame(frame);
+ return result;
+ }
+
+ public Frame duplicateFrameToTarget(Frame frame, int newTarget) {
+ MutableFrameFormat newFormat = frame.getFormat().mutableCopy();
+ newFormat.setTarget(newTarget);
+ Frame result = newFrame(newFormat);
+ result.setDataFromFrame(frame);
+ return result;
+ }
+
+ public abstract Frame retainFrame(Frame frame);
+
+ public abstract Frame releaseFrame(Frame frame);
+
+ public FilterContext getContext() {
+ return mContext;
+ }
+
+ public GLEnvironment getGLEnvironment() {
+ return mContext != null ? mContext.getGLEnvironment() : null;
+ }
+
+ public void tearDown() {
+ }
+
+ void setContext(FilterContext context) {
+ mContext = context;
+ }
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/GLEnvironment.java b/media/mca/filterfw/java/android/filterfw/core/GLEnvironment.java
new file mode 100644
index 0000000..fcf5f5d
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/GLEnvironment.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import android.filterfw.core.NativeAllocatorTag;
+import android.graphics.SurfaceTexture;
+import android.os.Looper;
+import android.util.Log;
+import android.view.Surface;
+import android.media.MediaRecorder;
+
+/**
+ * @hide
+ */
+public class GLEnvironment {
+
+ private int glEnvId;
+
+ public GLEnvironment() {
+ nativeAllocate();
+ }
+
+ private GLEnvironment(NativeAllocatorTag tag) {
+ }
+
+ public synchronized void tearDown() {
+ if (glEnvId != -1) {
+ nativeDeallocate();
+ glEnvId = -1;
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ tearDown();
+ }
+
+ public void initWithNewContext() {
+ if (!nativeInitWithNewContext()) {
+ throw new RuntimeException("Could not initialize GLEnvironment with new context!");
+ }
+ }
+
+ public void initWithCurrentContext() {
+ if (!nativeInitWithCurrentContext()) {
+ throw new RuntimeException("Could not initialize GLEnvironment with current context!");
+ }
+ }
+
+ public boolean isActive() {
+ return nativeIsActive();
+ }
+
+ public boolean isContextActive() {
+ return nativeIsContextActive();
+ }
+
+ public static boolean isAnyContextActive() {
+ return nativeIsAnyContextActive();
+ }
+
+ public void activate() {
+ if (Looper.myLooper() != null && Looper.myLooper().equals(Looper.getMainLooper())) {
+ Log.e("FilterFramework", "Activating GL context in UI thread!");
+ }
+ if (!nativeActivate()) {
+ throw new RuntimeException("Could not activate GLEnvironment!");
+ }
+ }
+
+ public void deactivate() {
+ if (!nativeDeactivate()) {
+ throw new RuntimeException("Could not deactivate GLEnvironment!");
+ }
+ }
+
+ public void swapBuffers() {
+ if (!nativeSwapBuffers()) {
+ throw new RuntimeException("Error swapping EGL buffers!");
+ }
+ }
+
+ public int registerSurface(Surface surface) {
+ int result = nativeAddSurface(surface);
+ if (result < 0) {
+ throw new RuntimeException("Error registering surface " + surface + "!");
+ }
+ return result;
+ }
+
+ public int registerSurfaceTexture(SurfaceTexture surfaceTexture, int width, int height) {
+ Surface surface = new Surface(surfaceTexture);
+ int result = nativeAddSurfaceWidthHeight(surface, width, height);
+ surface.release();
+ if (result < 0) {
+ throw new RuntimeException("Error registering surfaceTexture " + surfaceTexture + "!");
+ }
+ return result;
+ }
+
+ public int registerSurfaceFromMediaRecorder(MediaRecorder mediaRecorder) {
+ int result = nativeAddSurfaceFromMediaRecorder(mediaRecorder);
+ if (result < 0) {
+ throw new RuntimeException("Error registering surface from "
+ + "MediaRecorder" + mediaRecorder + "!");
+ }
+ return result;
+ }
+
+ public void activateSurfaceWithId(int surfaceId) {
+ if (!nativeActivateSurfaceId(surfaceId)) {
+ throw new RuntimeException("Could not activate surface " + surfaceId + "!");
+ }
+ }
+
+ public void unregisterSurfaceId(int surfaceId) {
+ if (!nativeRemoveSurfaceId(surfaceId)) {
+ throw new RuntimeException("Could not unregister surface " + surfaceId + "!");
+ }
+ }
+
+ public void setSurfaceTimestamp(long timestamp) {
+ if (!nativeSetSurfaceTimestamp(timestamp)) {
+ throw new RuntimeException("Could not set timestamp for current surface!");
+ }
+ }
+
+ static {
+ System.loadLibrary("filterfw");
+ }
+
+ private native boolean nativeInitWithNewContext();
+
+ private native boolean nativeInitWithCurrentContext();
+
+ private native boolean nativeIsActive();
+
+ private native boolean nativeIsContextActive();
+
+ private static native boolean nativeIsAnyContextActive();
+
+ private native boolean nativeActivate();
+
+ private native boolean nativeDeactivate();
+
+ private native boolean nativeSwapBuffers();
+
+ private native boolean nativeAllocate();
+
+ private native boolean nativeDeallocate();
+
+ private native int nativeAddSurface(Surface surface);
+
+ private native int nativeAddSurfaceWidthHeight(Surface surface, int width, int height);
+
+ private native int nativeAddSurfaceFromMediaRecorder(MediaRecorder mediaRecorder);
+
+ private native boolean nativeDisconnectSurfaceMediaSource(MediaRecorder mediaRecorder);
+
+ private native boolean nativeActivateSurfaceId(int surfaceId);
+
+ private native boolean nativeRemoveSurfaceId(int surfaceId);
+
+ private native boolean nativeSetSurfaceTimestamp(long timestamp);
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/GLFrame.java b/media/mca/filterfw/java/android/filterfw/core/GLFrame.java
new file mode 100644
index 0000000..1558cc6
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/GLFrame.java
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.FrameManager;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.StopWatchMap;
+import android.graphics.Bitmap;
+import android.opengl.GLES20;
+import android.graphics.Rect;
+
+import java.nio.ByteBuffer;
+
+class GLFrameTimer {
+
+ private static StopWatchMap mTimer = null;
+
+ public static StopWatchMap get() {
+ if (mTimer == null) {
+ mTimer = new StopWatchMap();
+ }
+ return mTimer;
+ }
+
+}
+
+/**
+ * @hide
+ */
+public class GLFrame extends Frame {
+
+ // GL-related binding types
+ public final static int EXISTING_TEXTURE_BINDING = 100;
+ public final static int EXISTING_FBO_BINDING = 101;
+ public final static int NEW_TEXTURE_BINDING = 102; // TODO: REMOVE THIS
+ public final static int NEW_FBO_BINDING = 103; // TODO: REMOVE THIS
+ public final static int EXTERNAL_TEXTURE = 104;
+
+ private int glFrameId = -1;
+
+ /**
+ * Flag whether we own the texture or not. If we do not, we must be careful when caching or
+ * storing the frame, as the user may delete, and regenerate it.
+ */
+ private boolean mOwnsTexture = true;
+
+ /**
+ * Keep a reference to the GL environment, so that it does not get deallocated while there
+ * are still frames living in it.
+ */
+ private GLEnvironment mGLEnvironment;
+
+ GLFrame(FrameFormat format, FrameManager frameManager) {
+ super(format, frameManager);
+ }
+
+ GLFrame(FrameFormat format, FrameManager frameManager, int bindingType, long bindingId) {
+ super(format, frameManager, bindingType, bindingId);
+ }
+
+ void init(GLEnvironment glEnv) {
+ FrameFormat format = getFormat();
+ mGLEnvironment = glEnv;
+
+ // Check that we have a valid format
+ if (format.getBytesPerSample() != 4) {
+ throw new IllegalArgumentException("GL frames must have 4 bytes per sample!");
+ } else if (format.getDimensionCount() != 2) {
+ throw new IllegalArgumentException("GL frames must be 2-dimensional!");
+ } else if (getFormat().getSize() < 0) {
+ throw new IllegalArgumentException("Initializing GL frame with zero size!");
+ }
+
+ // Create correct frame
+ int bindingType = getBindingType();
+ boolean reusable = true;
+ if (bindingType == Frame.NO_BINDING) {
+ initNew(false);
+ } else if (bindingType == EXTERNAL_TEXTURE) {
+ initNew(true);
+ reusable = false;
+ } else if (bindingType == EXISTING_TEXTURE_BINDING) {
+ initWithTexture((int)getBindingId());
+ } else if (bindingType == EXISTING_FBO_BINDING) {
+ initWithFbo((int)getBindingId());
+ } else if (bindingType == NEW_TEXTURE_BINDING) {
+ initWithTexture((int)getBindingId());
+ } else if (bindingType == NEW_FBO_BINDING) {
+ initWithFbo((int)getBindingId());
+ } else {
+ throw new RuntimeException("Attempting to create GL frame with unknown binding type "
+ + bindingType + "!");
+ }
+ setReusable(reusable);
+ }
+
+ private void initNew(boolean isExternal) {
+ if (isExternal) {
+ if (!nativeAllocateExternal(mGLEnvironment)) {
+ throw new RuntimeException("Could not allocate external GL frame!");
+ }
+ } else {
+ if (!nativeAllocate(mGLEnvironment, getFormat().getWidth(), getFormat().getHeight())) {
+ throw new RuntimeException("Could not allocate GL frame!");
+ }
+ }
+ }
+
+ private void initWithTexture(int texId) {
+ int width = getFormat().getWidth();
+ int height = getFormat().getHeight();
+ if (!nativeAllocateWithTexture(mGLEnvironment, texId, width, height)) {
+ throw new RuntimeException("Could not allocate texture backed GL frame!");
+ }
+ mOwnsTexture = false;
+ markReadOnly();
+ }
+
+ private void initWithFbo(int fboId) {
+ int width = getFormat().getWidth();
+ int height = getFormat().getHeight();
+ if (!nativeAllocateWithFbo(mGLEnvironment, fboId, width, height)) {
+ throw new RuntimeException("Could not allocate FBO backed GL frame!");
+ }
+ }
+
+ void flushGPU(String message) {
+ StopWatchMap timer = GLFrameTimer.get();
+ if (timer.LOG_MFF_RUNNING_TIMES) {
+ timer.start("glFinish " + message);
+ GLES20.glFinish();
+ timer.stop("glFinish " + message);
+ }
+ }
+
+ @Override
+ protected synchronized boolean hasNativeAllocation() {
+ return glFrameId != -1;
+ }
+
+ @Override
+ protected synchronized void releaseNativeAllocation() {
+ nativeDeallocate();
+ glFrameId = -1;
+ }
+
+ public GLEnvironment getGLEnvironment() {
+ return mGLEnvironment;
+ }
+
+ @Override
+ public Object getObjectValue() {
+ assertGLEnvValid();
+ return ByteBuffer.wrap(getNativeData());
+ }
+
+ @Override
+ public void setInts(int[] ints) {
+ assertFrameMutable();
+ assertGLEnvValid();
+ if (!setNativeInts(ints)) {
+ throw new RuntimeException("Could not set int values for GL frame!");
+ }
+ }
+
+ @Override
+ public int[] getInts() {
+ assertGLEnvValid();
+ flushGPU("getInts");
+ return getNativeInts();
+ }
+
+ @Override
+ public void setFloats(float[] floats) {
+ assertFrameMutable();
+ assertGLEnvValid();
+ if (!setNativeFloats(floats)) {
+ throw new RuntimeException("Could not set int values for GL frame!");
+ }
+ }
+
+ @Override
+ public float[] getFloats() {
+ assertGLEnvValid();
+ flushGPU("getFloats");
+ return getNativeFloats();
+ }
+
+ @Override
+ public void setData(ByteBuffer buffer, int offset, int length) {
+ assertFrameMutable();
+ assertGLEnvValid();
+ byte[] bytes = buffer.array();
+ if (getFormat().getSize() != bytes.length) {
+ throw new RuntimeException("Data size in setData does not match GL frame size!");
+ } else if (!setNativeData(bytes, offset, length)) {
+ throw new RuntimeException("Could not set GL frame data!");
+ }
+ }
+
+ @Override
+ public ByteBuffer getData() {
+ assertGLEnvValid();
+ flushGPU("getData");
+ return ByteBuffer.wrap(getNativeData());
+ }
+
+ @Override
+ public void setBitmap(Bitmap bitmap) {
+ assertFrameMutable();
+ assertGLEnvValid();
+ if (getFormat().getWidth() != bitmap.getWidth() ||
+ getFormat().getHeight() != bitmap.getHeight()) {
+ throw new RuntimeException("Bitmap dimensions do not match GL frame dimensions!");
+ } else {
+ Bitmap rgbaBitmap = convertBitmapToRGBA(bitmap);
+ if (!setNativeBitmap(rgbaBitmap, rgbaBitmap.getByteCount())) {
+ throw new RuntimeException("Could not set GL frame bitmap data!");
+ }
+ }
+ }
+
+ @Override
+ public Bitmap getBitmap() {
+ assertGLEnvValid();
+ flushGPU("getBitmap");
+ Bitmap result = Bitmap.createBitmap(getFormat().getWidth(),
+ getFormat().getHeight(),
+ Bitmap.Config.ARGB_8888);
+ if (!getNativeBitmap(result)) {
+ throw new RuntimeException("Could not get bitmap data from GL frame!");
+ }
+ return result;
+ }
+
+ @Override
+ public void setDataFromFrame(Frame frame) {
+ assertGLEnvValid();
+
+ // Make sure frame fits
+ if (getFormat().getSize() < frame.getFormat().getSize()) {
+ throw new RuntimeException(
+ "Attempting to assign frame of size " + frame.getFormat().getSize() + " to " +
+ "smaller GL frame of size " + getFormat().getSize() + "!");
+ }
+
+ // Invoke optimized implementations if possible
+ if (frame instanceof NativeFrame) {
+ nativeCopyFromNative((NativeFrame)frame);
+ } else if (frame instanceof GLFrame) {
+ nativeCopyFromGL((GLFrame)frame);
+ } else if (frame instanceof SimpleFrame) {
+ setObjectValue(frame.getObjectValue());
+ } else {
+ super.setDataFromFrame(frame);
+ }
+ }
+
+ public void setViewport(int x, int y, int width, int height) {
+ assertFrameMutable();
+ setNativeViewport(x, y, width, height);
+ }
+
+ public void setViewport(Rect rect) {
+ assertFrameMutable();
+ setNativeViewport(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
+ }
+
+ public void generateMipMap() {
+ assertFrameMutable();
+ assertGLEnvValid();
+ if (!generateNativeMipMap()) {
+ throw new RuntimeException("Could not generate mip-map for GL frame!");
+ }
+ }
+
+ public void setTextureParameter(int param, int value) {
+ assertFrameMutable();
+ assertGLEnvValid();
+ if (!setNativeTextureParam(param, value)) {
+ throw new RuntimeException("Could not set texture value " + param + " = " + value + " " +
+ "for GLFrame!");
+ }
+ }
+
+ public int getTextureId() {
+ return getNativeTextureId();
+ }
+
+ public int getFboId() {
+ return getNativeFboId();
+ }
+
+ public void focus() {
+ if (!nativeFocus()) {
+ throw new RuntimeException("Could not focus on GLFrame for drawing!");
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "GLFrame id: " + glFrameId + " (" + getFormat() + ") with texture ID "
+ + getTextureId() + ", FBO ID " + getFboId();
+ }
+
+ @Override
+ protected void reset(FrameFormat newFormat) {
+ if (!nativeResetParams()) {
+ throw new RuntimeException("Could not reset GLFrame texture parameters!");
+ }
+ super.reset(newFormat);
+ }
+
+ @Override
+ protected void onFrameStore() {
+ if (!mOwnsTexture) {
+ // Detach texture from FBO in case user manipulates it.
+ nativeDetachTexFromFbo();
+ }
+ }
+
+ @Override
+ protected void onFrameFetch() {
+ if (!mOwnsTexture) {
+ // Reattach texture to FBO when using frame again. This may reallocate the texture
+ // in case it has become invalid.
+ nativeReattachTexToFbo();
+ }
+ }
+
+ private void assertGLEnvValid() {
+ if (!mGLEnvironment.isContextActive()) {
+ if (GLEnvironment.isAnyContextActive()) {
+ throw new RuntimeException("Attempting to access " + this + " with foreign GL " +
+ "context active!");
+ } else {
+ throw new RuntimeException("Attempting to access " + this + " with no GL context " +
+ " active!");
+ }
+ }
+ }
+
+ static {
+ System.loadLibrary("filterfw");
+ }
+
+ private native boolean nativeAllocate(GLEnvironment env, int width, int height);
+
+ private native boolean nativeAllocateWithTexture(GLEnvironment env,
+ int textureId,
+ int width,
+ int height);
+
+ private native boolean nativeAllocateWithFbo(GLEnvironment env,
+ int fboId,
+ int width,
+ int height);
+
+ private native boolean nativeAllocateExternal(GLEnvironment env);
+
+ private native boolean nativeDeallocate();
+
+ private native boolean setNativeData(byte[] data, int offset, int length);
+
+ private native byte[] getNativeData();
+
+ private native boolean setNativeInts(int[] ints);
+
+ private native boolean setNativeFloats(float[] floats);
+
+ private native int[] getNativeInts();
+
+ private native float[] getNativeFloats();
+
+ private native boolean setNativeBitmap(Bitmap bitmap, int size);
+
+ private native boolean getNativeBitmap(Bitmap bitmap);
+
+ private native boolean setNativeViewport(int x, int y, int width, int height);
+
+ private native int getNativeTextureId();
+
+ private native int getNativeFboId();
+
+ private native boolean generateNativeMipMap();
+
+ private native boolean setNativeTextureParam(int param, int value);
+
+ private native boolean nativeResetParams();
+
+ private native boolean nativeCopyFromNative(NativeFrame frame);
+
+ private native boolean nativeCopyFromGL(GLFrame frame);
+
+ private native boolean nativeFocus();
+
+ private native boolean nativeReattachTexToFbo();
+
+ private native boolean nativeDetachTexFromFbo();
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/GenerateFieldPort.java b/media/mca/filterfw/java/android/filterfw/core/GenerateFieldPort.java
new file mode 100644
index 0000000..3e37d4f
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/GenerateFieldPort.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import java.lang.annotation.*;
+
+/**
+ * @hide
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface GenerateFieldPort {
+ String name() default "";
+ boolean hasDefault() default false;
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/GenerateFinalPort.java b/media/mca/filterfw/java/android/filterfw/core/GenerateFinalPort.java
new file mode 100644
index 0000000..0dec8cc
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/GenerateFinalPort.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import java.lang.annotation.*;
+
+/**
+ * @hide
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface GenerateFinalPort {
+ String name() default "";
+ boolean hasDefault() default false;
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/GenerateProgramPort.java b/media/mca/filterfw/java/android/filterfw/core/GenerateProgramPort.java
new file mode 100644
index 0000000..fb40416
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/GenerateProgramPort.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import java.lang.annotation.*;
+
+/**
+ * @hide
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface GenerateProgramPort {
+ String name();
+ Class type();
+ String variableName() default "";
+ boolean hasDefault() default false;
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/GenerateProgramPorts.java b/media/mca/filterfw/java/android/filterfw/core/GenerateProgramPorts.java
new file mode 100644
index 0000000..354126d
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/GenerateProgramPorts.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import java.lang.annotation.*;
+
+/**
+ * @hide
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface GenerateProgramPorts {
+ GenerateProgramPort[] value();
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/GraphRunner.java b/media/mca/filterfw/java/android/filterfw/core/GraphRunner.java
new file mode 100644
index 0000000..b496c54
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/GraphRunner.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+/**
+ * @hide
+ */
+public abstract class GraphRunner {
+
+ protected FilterContext mFilterContext = null;
+
+ /** Interface for listeners waiting for the runner to complete. */
+ public interface OnRunnerDoneListener {
+ /** Callback method to be called when the runner completes a
+ * {@link #run()} call.
+ *
+ * @param result will be RESULT_FINISHED if the graph finished running
+ * on its own, RESULT_STOPPED if the runner was stopped by a call
+ * to stop(), RESULT_BLOCKED if no filters could run due to lack
+ * of inputs or outputs or due to scheduling policies, and
+ * RESULT_SLEEPING if a filter node requested sleep.
+ */
+ public void onRunnerDone(int result);
+ }
+
+ public static final int RESULT_UNKNOWN = 0;
+ public static final int RESULT_RUNNING = 1;
+ public static final int RESULT_FINISHED = 2;
+ public static final int RESULT_SLEEPING = 3;
+ public static final int RESULT_BLOCKED = 4;
+ public static final int RESULT_STOPPED = 5;
+ public static final int RESULT_ERROR = 6;
+
+ public GraphRunner(FilterContext context) {
+ mFilterContext = context;
+ }
+
+ public abstract FilterGraph getGraph();
+
+ public FilterContext getContext() {
+ return mFilterContext;
+ }
+
+ /**
+ * Helper function for subclasses to activate the GL environment before running.
+ * @return true, if the GL environment was activated. Returns false, if the GL environment
+ * was already active.
+ */
+ protected boolean activateGlContext() {
+ GLEnvironment glEnv = mFilterContext.getGLEnvironment();
+ if (glEnv != null && !glEnv.isActive()) {
+ glEnv.activate();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Helper function for subclasses to deactivate the GL environment after running.
+ */
+ protected void deactivateGlContext() {
+ GLEnvironment glEnv = mFilterContext.getGLEnvironment();
+ if (glEnv != null) {
+ glEnv.deactivate();
+ }
+ }
+
+ /** Starts running the graph. Will open the filters in the graph if they are not already open. */
+ public abstract void run();
+
+ public abstract void setDoneCallback(OnRunnerDoneListener listener);
+ public abstract boolean isRunning();
+
+ /** Stops graph execution. As part of stopping, also closes the graph nodes. */
+ public abstract void stop();
+
+ /** Closes the filters in a graph. Can only be called if the graph is not running. */
+ public abstract void close();
+
+ /**
+ * Returns the last exception that happened during an asynchronous run. Returns null if
+ * there is nothing to report.
+ */
+ public abstract Exception getError();
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/InputPort.java b/media/mca/filterfw/java/android/filterfw/core/InputPort.java
new file mode 100644
index 0000000..de5cccc
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/InputPort.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+/**
+ * @hide
+ */
+public abstract class InputPort extends FilterPort {
+
+ protected OutputPort mSourcePort;
+
+ public InputPort(Filter filter, String name) {
+ super(filter, name);
+ }
+
+ public void setSourcePort(OutputPort source) {
+ if (mSourcePort != null) {
+ throw new RuntimeException(this + " already connected to " + mSourcePort + "!");
+ }
+ mSourcePort = source;
+ }
+
+ public boolean isConnected() {
+ return mSourcePort != null;
+ }
+
+ public void open() {
+ super.open();
+ if (mSourcePort != null && !mSourcePort.isOpen()) {
+ mSourcePort.open();
+ }
+ }
+
+ public void close() {
+ if (mSourcePort != null && mSourcePort.isOpen()) {
+ mSourcePort.close();
+ }
+ super.close();
+ }
+
+ public OutputPort getSourcePort() {
+ return mSourcePort;
+ }
+
+ public Filter getSourceFilter() {
+ return mSourcePort == null ? null : mSourcePort.getFilter();
+ }
+
+ public FrameFormat getSourceFormat() {
+ return mSourcePort != null ? mSourcePort.getPortFormat() : getPortFormat();
+ }
+
+ public Object getTarget() {
+ return null;
+ }
+
+ public boolean filterMustClose() {
+ return !isOpen() && isBlocking() && !hasFrame();
+ }
+
+ public boolean isReady() {
+ return hasFrame() || !isBlocking();
+ }
+
+ public boolean acceptsFrame() {
+ return !hasFrame();
+ }
+
+ public abstract void transfer(FilterContext context);
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/KeyValueMap.java b/media/mca/filterfw/java/android/filterfw/core/KeyValueMap.java
new file mode 100644
index 0000000..8cf9a13
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/KeyValueMap.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @hide
+ */
+public class KeyValueMap extends HashMap<String, Object> {
+
+ public void setKeyValues(Object... keyValues) {
+ if (keyValues.length % 2 != 0) {
+ throw new RuntimeException("Key-Value arguments passed into setKeyValues must be "
+ + "an alternating list of keys and values!");
+ }
+ for (int i = 0; i < keyValues.length; i += 2) {
+ if (!(keyValues[i] instanceof String)) {
+ throw new RuntimeException("Key-value argument " + i + " must be a key of type "
+ + "String, but found an object of type " + keyValues[i].getClass() + "!");
+ }
+ String key = (String)keyValues[i];
+ Object value = keyValues[i+1];
+ put(key, value);
+ }
+ }
+
+ public static KeyValueMap fromKeyValues(Object... keyValues) {
+ KeyValueMap result = new KeyValueMap();
+ result.setKeyValues(keyValues);
+ return result;
+ }
+
+ public String getString(String key) {
+ Object result = get(key);
+ return result != null ? (String)result : null;
+ }
+
+ public int getInt(String key) {
+ Object result = get(key);
+ return result != null ? (Integer)result : null;
+ }
+
+ public float getFloat(String key) {
+ Object result = get(key);
+ return result != null ? (Float)result : null;
+ }
+
+ @Override
+ public String toString() {
+ StringWriter writer = new StringWriter();
+ for (Map.Entry<String, Object> entry : entrySet()) {
+ String valueString;
+ Object value = entry.getValue();
+ if (value instanceof String) {
+ valueString = "\"" + value + "\"";
+ } else {
+ valueString = value.toString();
+ }
+ writer.write(entry.getKey() + " = " + valueString + ";\n");
+ }
+ return writer.toString();
+ }
+
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/MutableFrameFormat.java b/media/mca/filterfw/java/android/filterfw/core/MutableFrameFormat.java
new file mode 100644
index 0000000..8c78975
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/MutableFrameFormat.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.KeyValueMap;
+
+import java.util.Arrays;
+
+/**
+ * @hide
+ */
+public class MutableFrameFormat extends FrameFormat {
+
+ public MutableFrameFormat() {
+ super();
+ }
+
+ public MutableFrameFormat(int baseType, int target) {
+ super(baseType, target);
+ }
+
+ public void setBaseType(int baseType) {
+ mBaseType = baseType;
+ mBytesPerSample = bytesPerSampleOf(baseType);
+ }
+
+ public void setTarget(int target) {
+ mTarget = target;
+ }
+
+ public void setBytesPerSample(int bytesPerSample) {
+ mBytesPerSample = bytesPerSample;
+ mSize = SIZE_UNKNOWN;
+ }
+
+ public void setDimensions(int[] dimensions) {
+ mDimensions = (dimensions == null) ? null : Arrays.copyOf(dimensions, dimensions.length);
+ mSize = SIZE_UNKNOWN;
+ }
+
+ public void setDimensions(int size) {
+ int[] dimensions = new int[1];
+ dimensions[0] = size;
+ mDimensions = dimensions;
+ mSize = SIZE_UNKNOWN;
+ }
+
+ public void setDimensions(int width, int height) {
+ int[] dimensions = new int[2];
+ dimensions[0] = width;
+ dimensions[1] = height;
+ mDimensions = dimensions;
+ mSize = SIZE_UNKNOWN;
+ }
+
+ public void setDimensions(int width, int height, int depth) {
+ int[] dimensions = new int[3];
+ dimensions[0] = width;
+ dimensions[1] = height;
+ dimensions[2] = depth;
+ mDimensions = dimensions;
+ mSize = SIZE_UNKNOWN;
+ }
+
+ public void setDimensionCount(int count) {
+ mDimensions = new int[count];
+ }
+
+ public void setObjectClass(Class objectClass) {
+ mObjectClass = objectClass;
+ }
+
+ public void setMetaValue(String key, Object value) {
+ if (mMetaData == null) {
+ mMetaData = new KeyValueMap();
+ }
+ mMetaData.put(key, value);
+ }
+
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/NativeAllocatorTag.java b/media/mca/filterfw/java/android/filterfw/core/NativeAllocatorTag.java
new file mode 100644
index 0000000..4d43d7c
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/NativeAllocatorTag.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+/**
+ * This class is simply a place-holder type used to identify calls coming
+ * from the native layer. This way method signatures can be selected
+ * that are to be accessed from the native layer only.
+ *
+ * @hide
+ **/
+public class NativeAllocatorTag {
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/NativeBuffer.java b/media/mca/filterfw/java/android/filterfw/core/NativeBuffer.java
new file mode 100644
index 0000000..80da5ea
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/NativeBuffer.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import android.filterfw.core.Frame;
+
+/**
+ * @hide
+ */
+public class NativeBuffer {
+
+ // These are set by the native layer
+ private long mDataPointer = 0;
+ private int mSize = 0;
+
+ private Frame mAttachedFrame;
+
+ private boolean mOwnsData = false;
+ private int mRefCount = 1;
+
+ public NativeBuffer() {
+ }
+
+ public NativeBuffer(int count) {
+ allocate(count * getElementSize());
+ mOwnsData = true;
+ }
+
+ public NativeBuffer mutableCopy() {
+ NativeBuffer result = null;
+ try {
+ Class myClass = getClass();
+ result = (NativeBuffer)myClass.newInstance();
+ } catch (Exception e) {
+ throw new RuntimeException("Unable to allocate a copy of " + getClass() + "! Make " +
+ "sure the class has a default constructor!");
+ }
+ if (mSize > 0 && !nativeCopyTo(result)) {
+ throw new RuntimeException("Failed to copy NativeBuffer to mutable instance!");
+ }
+ return result;
+ }
+
+ public int size() {
+ return mSize;
+ }
+
+ public int count() {
+ return (mDataPointer != 0) ? mSize / getElementSize() : 0;
+ }
+
+ public int getElementSize() {
+ return 1;
+ }
+
+ public NativeBuffer retain() {
+ if (mAttachedFrame != null) {
+ mAttachedFrame.retain();
+ } else if (mOwnsData) {
+ ++mRefCount;
+ }
+ return this;
+ }
+
+ public NativeBuffer release() {
+ // Decrement refcount
+ boolean doDealloc = false;
+ if (mAttachedFrame != null) {
+ doDealloc = (mAttachedFrame.release() == null);
+ } else if (mOwnsData) {
+ --mRefCount;
+ doDealloc = (mRefCount == 0);
+ }
+
+ // Deallocate if necessary
+ if (doDealloc) {
+ deallocate(mOwnsData);
+ return null;
+ } else {
+ return this;
+ }
+ }
+
+ public boolean isReadOnly() {
+ return (mAttachedFrame != null) ? mAttachedFrame.isReadOnly() : false;
+ }
+
+ static {
+ System.loadLibrary("filterfw");
+ }
+
+ void attachToFrame(Frame frame) {
+ // We do not auto-retain. We expect the user to call retain() if they want to hold on to
+ // the frame.
+ mAttachedFrame = frame;
+ }
+
+ protected void assertReadable() {
+ if (mDataPointer == 0 || mSize == 0
+ || (mAttachedFrame != null && !mAttachedFrame.hasNativeAllocation())) {
+ throw new NullPointerException("Attempting to read from null data frame!");
+ }
+ }
+
+ protected void assertWritable() {
+ if (isReadOnly()) {
+ throw new RuntimeException("Attempting to modify read-only native (structured) data!");
+ }
+ }
+
+ private native boolean allocate(int size);
+ private native boolean deallocate(boolean ownsData);
+ private native boolean nativeCopyTo(NativeBuffer buffer);
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/NativeFrame.java b/media/mca/filterfw/java/android/filterfw/core/NativeFrame.java
new file mode 100644
index 0000000..bfd09ba
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/NativeFrame.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.FrameManager;
+import android.filterfw.core.GLFrame;
+import android.filterfw.core.NativeBuffer;
+import android.graphics.Bitmap;
+
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+
+/**
+ * @hide
+ */
+public class NativeFrame extends Frame {
+
+ private int nativeFrameId = -1;
+
+ NativeFrame(FrameFormat format, FrameManager frameManager) {
+ super(format, frameManager);
+ int capacity = format.getSize();
+ nativeAllocate(capacity);
+ setReusable(capacity != 0);
+ }
+
+ @Override
+ protected synchronized void releaseNativeAllocation() {
+ nativeDeallocate();
+ nativeFrameId = -1;
+ }
+
+ @Override
+ protected synchronized boolean hasNativeAllocation() {
+ return nativeFrameId != -1;
+ }
+
+ @Override
+ public int getCapacity() {
+ return getNativeCapacity();
+ }
+
+ /**
+ * Returns the native frame's Object value.
+ *
+ * If the frame's base-type is not TYPE_OBJECT, this returns a data buffer containing the native
+ * data (this is equivalent to calling getData().
+ * If the frame is based on an object type, this type is expected to be a subclass of
+ * NativeBuffer. The NativeBuffer returned is only valid for as long as the frame is alive. If
+ * you need to hold on to the returned value, you must retain it.
+ */
+ @Override
+ public Object getObjectValue() {
+ // If this is not a structured frame, return our data
+ if (getFormat().getBaseType() != FrameFormat.TYPE_OBJECT) {
+ return getData();
+ }
+
+ // Get the structure class
+ Class structClass = getFormat().getObjectClass();
+ if (structClass == null) {
+ throw new RuntimeException("Attempting to get object data from frame that does " +
+ "not specify a structure object class!");
+ }
+
+ // Make sure it is a NativeBuffer subclass
+ if (!NativeBuffer.class.isAssignableFrom(structClass)) {
+ throw new RuntimeException("NativeFrame object class must be a subclass of " +
+ "NativeBuffer!");
+ }
+
+ // Instantiate a new empty instance of this class
+ NativeBuffer structData = null;
+ try {
+ structData = (NativeBuffer)structClass.newInstance();
+ } catch (Exception e) {
+ throw new RuntimeException("Could not instantiate new structure instance of type '" +
+ structClass + "'!");
+ }
+
+ // Wrap it around our data
+ if (!getNativeBuffer(structData)) {
+ throw new RuntimeException("Could not get the native structured data for frame!");
+ }
+
+ // Attach this frame to it
+ structData.attachToFrame(this);
+
+ return structData;
+ }
+
+ @Override
+ public void setInts(int[] ints) {
+ assertFrameMutable();
+ if (ints.length * nativeIntSize() > getFormat().getSize()) {
+ throw new RuntimeException(
+ "NativeFrame cannot hold " + ints.length + " integers. (Can only hold " +
+ (getFormat().getSize() / nativeIntSize()) + " integers).");
+ } else if (!setNativeInts(ints)) {
+ throw new RuntimeException("Could not set int values for native frame!");
+ }
+ }
+
+ @Override
+ public int[] getInts() {
+ return getNativeInts(getFormat().getSize());
+ }
+
+ @Override
+ public void setFloats(float[] floats) {
+ assertFrameMutable();
+ if (floats.length * nativeFloatSize() > getFormat().getSize()) {
+ throw new RuntimeException(
+ "NativeFrame cannot hold " + floats.length + " floats. (Can only hold " +
+ (getFormat().getSize() / nativeFloatSize()) + " floats).");
+ } else if (!setNativeFloats(floats)) {
+ throw new RuntimeException("Could not set int values for native frame!");
+ }
+ }
+
+ @Override
+ public float[] getFloats() {
+ return getNativeFloats(getFormat().getSize());
+ }
+
+ // TODO: This function may be a bit confusing: Is the offset the target or source offset? Maybe
+ // we should allow specifying both? (May be difficult for other frame types).
+ @Override
+ public void setData(ByteBuffer buffer, int offset, int length) {
+ assertFrameMutable();
+ byte[] bytes = buffer.array();
+ if ((length + offset) > buffer.limit()) {
+ throw new RuntimeException("Offset and length exceed buffer size in native setData: " +
+ (length + offset) + " bytes given, but only " + buffer.limit() +
+ " bytes available!");
+ } else if (getFormat().getSize() != length) {
+ throw new RuntimeException("Data size in setData does not match native frame size: " +
+ "Frame size is " + getFormat().getSize() + " bytes, but " +
+ length + " bytes given!");
+ } else if (!setNativeData(bytes, offset, length)) {
+ throw new RuntimeException("Could not set native frame data!");
+ }
+ }
+
+ @Override
+ public ByteBuffer getData() {
+ byte[] data = getNativeData(getFormat().getSize());
+ return data == null ? null : ByteBuffer.wrap(data);
+ }
+
+ @Override
+ public void setBitmap(Bitmap bitmap) {
+ assertFrameMutable();
+ if (getFormat().getNumberOfDimensions() != 2) {
+ throw new RuntimeException("Attempting to set Bitmap for non 2-dimensional native frame!");
+ } else if (getFormat().getWidth() != bitmap.getWidth() ||
+ getFormat().getHeight() != bitmap.getHeight()) {
+ throw new RuntimeException("Bitmap dimensions do not match native frame dimensions!");
+ } else {
+ Bitmap rgbaBitmap = convertBitmapToRGBA(bitmap);
+ int byteCount = rgbaBitmap.getByteCount();
+ int bps = getFormat().getBytesPerSample();
+ if (!setNativeBitmap(rgbaBitmap, byteCount, bps)) {
+ throw new RuntimeException("Could not set native frame bitmap data!");
+ }
+ }
+ }
+
+ @Override
+ public Bitmap getBitmap() {
+ if (getFormat().getNumberOfDimensions() != 2) {
+ throw new RuntimeException("Attempting to get Bitmap for non 2-dimensional native frame!");
+ }
+ Bitmap result = Bitmap.createBitmap(getFormat().getWidth(),
+ getFormat().getHeight(),
+ Bitmap.Config.ARGB_8888);
+ int byteCount = result.getByteCount();
+ int bps = getFormat().getBytesPerSample();
+ if (!getNativeBitmap(result, byteCount, bps)) {
+ throw new RuntimeException("Could not get bitmap data from native frame!");
+ }
+ return result;
+ }
+
+ @Override
+ public void setDataFromFrame(Frame frame) {
+ // Make sure frame fits
+ if (getFormat().getSize() < frame.getFormat().getSize()) {
+ throw new RuntimeException(
+ "Attempting to assign frame of size " + frame.getFormat().getSize() + " to " +
+ "smaller native frame of size " + getFormat().getSize() + "!");
+ }
+
+ // Invoke optimized implementations if possible
+ if (frame instanceof NativeFrame) {
+ nativeCopyFromNative((NativeFrame)frame);
+ } else if (frame instanceof GLFrame) {
+ nativeCopyFromGL((GLFrame)frame);
+ } else if (frame instanceof SimpleFrame) {
+ setObjectValue(frame.getObjectValue());
+ } else {
+ super.setDataFromFrame(frame);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "NativeFrame id: " + nativeFrameId + " (" + getFormat() + ") of size "
+ + getCapacity();
+ }
+
+ static {
+ System.loadLibrary("filterfw");
+ }
+
+ private native boolean nativeAllocate(int capacity);
+
+ private native boolean nativeDeallocate();
+
+ private native int getNativeCapacity();
+
+ private static native int nativeIntSize();
+
+ private static native int nativeFloatSize();
+
+ private native boolean setNativeData(byte[] data, int offset, int length);
+
+ private native byte[] getNativeData(int byteCount);
+
+ private native boolean getNativeBuffer(NativeBuffer buffer);
+
+ private native boolean setNativeInts(int[] ints);
+
+ private native boolean setNativeFloats(float[] floats);
+
+ private native int[] getNativeInts(int byteCount);
+
+ private native float[] getNativeFloats(int byteCount);
+
+ private native boolean setNativeBitmap(Bitmap bitmap, int size, int bytesPerSample);
+
+ private native boolean getNativeBitmap(Bitmap bitmap, int size, int bytesPerSample);
+
+ private native boolean nativeCopyFromNative(NativeFrame frame);
+
+ private native boolean nativeCopyFromGL(GLFrame frame);
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/NativeProgram.java b/media/mca/filterfw/java/android/filterfw/core/NativeProgram.java
new file mode 100644
index 0000000..791ab3c
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/NativeProgram.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import android.filterfw.core.Frame;
+import android.filterfw.core.Program;
+
+/**
+ * @hide
+ */
+public class NativeProgram extends Program {
+
+ private int nativeProgramId;
+ private boolean mHasInitFunction = false;
+ private boolean mHasTeardownFunction = false;
+ private boolean mHasSetValueFunction = false;
+ private boolean mHasGetValueFunction = false;
+ private boolean mHasResetFunction = false;
+ private boolean mTornDown = false;
+
+ public NativeProgram(String nativeLibName, String nativeFunctionPrefix) {
+ // Allocate the native instance
+ allocate();
+
+ // Open the native library
+ String fullLibName = "lib" + nativeLibName + ".so";
+ if (!openNativeLibrary(fullLibName)) {
+ throw new RuntimeException("Could not find native library named '" + fullLibName + "' " +
+ "required for native program!");
+ }
+
+ // Bind the native functions
+ String processFuncName = nativeFunctionPrefix + "_process";
+ if (!bindProcessFunction(processFuncName)) {
+ throw new RuntimeException("Could not find native program function name " +
+ processFuncName + " in library " + fullLibName + "! " +
+ "This function is required!");
+ }
+
+ String initFuncName = nativeFunctionPrefix + "_init";
+ mHasInitFunction = bindInitFunction(initFuncName);
+
+ String teardownFuncName = nativeFunctionPrefix + "_teardown";
+ mHasTeardownFunction = bindTeardownFunction(teardownFuncName);
+
+ String setValueFuncName = nativeFunctionPrefix + "_setvalue";
+ mHasSetValueFunction = bindSetValueFunction(setValueFuncName);
+
+ String getValueFuncName = nativeFunctionPrefix + "_getvalue";
+ mHasGetValueFunction = bindGetValueFunction(getValueFuncName);
+
+ String resetFuncName = nativeFunctionPrefix + "_reset";
+ mHasResetFunction = bindResetFunction(resetFuncName);
+
+ // Initialize the native code
+ if (mHasInitFunction && !callNativeInit()) {
+ throw new RuntimeException("Could not initialize NativeProgram!");
+ }
+ }
+
+ public void tearDown() {
+ if (mTornDown) return;
+ if (mHasTeardownFunction && !callNativeTeardown()) {
+ throw new RuntimeException("Could not tear down NativeProgram!");
+ }
+ deallocate();
+ mTornDown = true;
+ }
+
+ @Override
+ public void reset() {
+ if (mHasResetFunction && !callNativeReset()) {
+ throw new RuntimeException("Could not reset NativeProgram!");
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ tearDown();
+ }
+
+ @Override
+ public void process(Frame[] inputs, Frame output) {
+ if (mTornDown) {
+ throw new RuntimeException("NativeProgram already torn down!");
+ }
+ NativeFrame[] nativeInputs = new NativeFrame[inputs.length];
+ for (int i = 0; i < inputs.length; ++i) {
+ if (inputs[i] == null || inputs[i] instanceof NativeFrame) {
+ nativeInputs[i] = (NativeFrame)inputs[i];
+ } else {
+ throw new RuntimeException("NativeProgram got non-native frame as input "+ i +"!");
+ }
+ }
+
+ // Get the native output frame
+ NativeFrame nativeOutput = null;
+ if (output == null || output instanceof NativeFrame) {
+ nativeOutput = (NativeFrame)output;
+ } else {
+ throw new RuntimeException("NativeProgram got non-native output frame!");
+ }
+
+ // Process!
+ if (!callNativeProcess(nativeInputs, nativeOutput)) {
+ throw new RuntimeException("Calling native process() caused error!");
+ }
+ }
+
+ @Override
+ public void setHostValue(String variableName, Object value) {
+ if (mTornDown) {
+ throw new RuntimeException("NativeProgram already torn down!");
+ }
+ if (!mHasSetValueFunction) {
+ throw new RuntimeException("Attempting to set native variable, but native code does not " +
+ "define native setvalue function!");
+ }
+ if (!callNativeSetValue(variableName, value.toString())) {
+ throw new RuntimeException("Error setting native value for variable '" + variableName + "'!");
+ }
+ }
+
+ @Override
+ public Object getHostValue(String variableName) {
+ if (mTornDown) {
+ throw new RuntimeException("NativeProgram already torn down!");
+ }
+ if (!mHasGetValueFunction) {
+ throw new RuntimeException("Attempting to get native variable, but native code does not " +
+ "define native getvalue function!");
+ }
+ return callNativeGetValue(variableName);
+ }
+
+ static {
+ System.loadLibrary("filterfw");
+ }
+
+ private native boolean allocate();
+
+ private native boolean deallocate();
+
+ private native boolean nativeInit();
+
+ private native boolean openNativeLibrary(String libName);
+
+ private native boolean bindInitFunction(String funcName);
+ private native boolean bindSetValueFunction(String funcName);
+ private native boolean bindGetValueFunction(String funcName);
+ private native boolean bindProcessFunction(String funcName);
+ private native boolean bindResetFunction(String funcName);
+ private native boolean bindTeardownFunction(String funcName);
+
+ private native boolean callNativeInit();
+ private native boolean callNativeSetValue(String key, String value);
+ private native String callNativeGetValue(String key);
+ private native boolean callNativeProcess(NativeFrame[] inputs, NativeFrame output);
+ private native boolean callNativeReset();
+ private native boolean callNativeTeardown();
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/OneShotScheduler.java b/media/mca/filterfw/java/android/filterfw/core/OneShotScheduler.java
new file mode 100644
index 0000000..dbc8d16
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/OneShotScheduler.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.Scheduler;
+import android.filterfw.core.RoundRobinScheduler;
+import android.util.Log;
+
+import java.util.HashMap;
+
+/**
+ * This OneShotScheduler only schedules source filters at most once. All other
+ * filters will be scheduled, and possibly repeatedly, until there is no filter
+ * that can be scheduled.
+ *
+ * @hide
+ */
+public class OneShotScheduler extends RoundRobinScheduler {
+ private HashMap <String, Integer> scheduled;
+
+ private final boolean mLogVerbose;
+ private static final String TAG = "OneShotScheduler";
+
+ public OneShotScheduler(FilterGraph graph) {
+ super(graph);
+ scheduled = new HashMap<String, Integer>();
+ mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+ scheduled.clear();
+ }
+
+ @Override
+ public Filter scheduleNextNode() {
+ Filter first = null;
+ // return the first filter that is not scheduled before.
+ while (true) {
+ Filter filter = super.scheduleNextNode();
+ if (filter == null) {
+ if (mLogVerbose) Log.v(TAG, "No filters available to run.");
+ return null;
+ }
+ if (!scheduled.containsKey(filter.getName())) {
+ if (filter.getNumberOfConnectedInputs() == 0)
+ scheduled.put(filter.getName(),1);
+ if (mLogVerbose) Log.v(TAG, "Scheduling filter \"" + filter.getName() + "\" of type " + filter.getFilterClassName());
+ return filter;
+ }
+ // if loop back, nothing available
+ if (first == filter) {
+ break;
+ }
+ // save the first scheduled one
+ if (first == null) first = filter;
+ }
+ if (mLogVerbose) Log.v(TAG, "One pass through graph completed.");
+ return null;
+ }
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/OutputPort.java b/media/mca/filterfw/java/android/filterfw/core/OutputPort.java
new file mode 100644
index 0000000..872dbdd
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/OutputPort.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+/**
+ * @hide
+ */
+public class OutputPort extends FilterPort {
+
+ protected InputPort mTargetPort;
+ protected InputPort mBasePort;
+
+ public OutputPort(Filter filter, String name) {
+ super(filter, name);
+ }
+
+ public void connectTo(InputPort target) {
+ if (mTargetPort != null) {
+ throw new RuntimeException(this + " already connected to " + mTargetPort + "!");
+ }
+ mTargetPort = target;
+ mTargetPort.setSourcePort(this);
+ }
+
+ public boolean isConnected() {
+ return mTargetPort != null;
+ }
+
+ public void open() {
+ super.open();
+ if (mTargetPort != null && !mTargetPort.isOpen()) {
+ mTargetPort.open();
+ }
+ }
+
+ public void close() {
+ super.close();
+ if (mTargetPort != null && mTargetPort.isOpen()) {
+ mTargetPort.close();
+ }
+ }
+
+ public InputPort getTargetPort() {
+ return mTargetPort;
+ }
+
+ public Filter getTargetFilter() {
+ return mTargetPort == null ? null : mTargetPort.getFilter();
+ }
+
+ public void setBasePort(InputPort basePort) {
+ mBasePort = basePort;
+ }
+
+ public InputPort getBasePort() {
+ return mBasePort;
+ }
+
+ public boolean filterMustClose() {
+ return !isOpen() && isBlocking();
+ }
+
+ public boolean isReady() {
+ return (isOpen() && mTargetPort.acceptsFrame()) || !isBlocking();
+ }
+
+ @Override
+ public void clear() {
+ if (mTargetPort != null) {
+ mTargetPort.clear();
+ }
+ }
+
+ @Override
+ public void pushFrame(Frame frame) {
+ if (mTargetPort == null) {
+ throw new RuntimeException(
+ "Attempting to push frame on unconnected port: " + this + "!");
+ }
+ mTargetPort.pushFrame(frame);
+ }
+
+ @Override
+ public void setFrame(Frame frame) {
+ assertPortIsOpen();
+ if (mTargetPort == null) {
+ throw new RuntimeException(
+ "Attempting to set frame on unconnected port: " + this + "!");
+ }
+ mTargetPort.setFrame(frame);
+ }
+
+ @Override
+ public Frame pullFrame() {
+ throw new RuntimeException("Cannot pull frame on " + this + "!");
+ }
+
+ @Override
+ public boolean hasFrame() {
+ return mTargetPort == null ? false : mTargetPort.hasFrame();
+ }
+
+ @Override
+ public String toString() {
+ return "output " + super.toString();
+ }
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/Program.java b/media/mca/filterfw/java/android/filterfw/core/Program.java
new file mode 100644
index 0000000..1930648
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/Program.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import android.filterfw.core.Frame;
+
+/**
+ * @hide
+ */
+public abstract class Program {
+
+ public abstract void process(Frame[] inputs, Frame output);
+
+ public void process(Frame input, Frame output) {
+ Frame[] inputs = new Frame[1];
+ inputs[0] = input;
+ process(inputs, output);
+ }
+
+ public abstract void setHostValue(String variableName, Object value);
+
+ public abstract Object getHostValue(String variableName);
+
+ public void reset() {
+ }
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/ProgramPort.java b/media/mca/filterfw/java/android/filterfw/core/ProgramPort.java
new file mode 100644
index 0000000..3cab26d
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/ProgramPort.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import java.lang.reflect.Field;
+
+/**
+ * @hide
+ */
+public class ProgramPort extends FieldPort {
+
+ protected String mVarName;
+
+ public ProgramPort(Filter filter,
+ String name,
+ String varName,
+ Field field,
+ boolean hasDefault) {
+ super(filter, name, field, hasDefault);
+ mVarName = varName;
+ }
+
+ @Override
+ public String toString() {
+ return "Program " + super.toString();
+ }
+
+ @Override
+ public synchronized void transfer(FilterContext context) {
+ if (mValueWaiting) {
+ try {
+ Object fieldValue = mField.get(mFilter);
+ if (fieldValue != null) {
+ Program program = (Program)fieldValue;
+ program.setHostValue(mVarName, mValue);
+ mValueWaiting = false;
+ }
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(
+ "Access to program field '" + mField.getName() + "' was denied!");
+ } catch (ClassCastException e) {
+ throw new RuntimeException("Non Program field '" + mField.getName()
+ + "' annotated with ProgramParameter!");
+ }
+ }
+ }
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/ProgramVariable.java b/media/mca/filterfw/java/android/filterfw/core/ProgramVariable.java
new file mode 100644
index 0000000..5592d37
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/ProgramVariable.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+/**
+ * @hide
+ */
+public class ProgramVariable {
+
+ private Program mProgram;
+ private String mVarName;
+
+ public ProgramVariable(Program program, String varName) {
+ mProgram = program;
+ mVarName = varName;
+ }
+
+ public Program getProgram() {
+ return mProgram;
+ }
+
+ public String getVariableName() {
+ return mVarName;
+ }
+
+ public void setValue(Object value) {
+ if (mProgram == null) {
+ throw new RuntimeException("Attempting to set program variable '" + mVarName
+ + "' but the program is null!");
+ }
+ mProgram.setHostValue(mVarName, value);
+ }
+
+ public Object getValue() {
+ if (mProgram == null) {
+ throw new RuntimeException("Attempting to get program variable '" + mVarName
+ + "' but the program is null!");
+ }
+ return mProgram.getHostValue(mVarName);
+ }
+
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/ProtocolException.java b/media/mca/filterfw/java/android/filterfw/core/ProtocolException.java
new file mode 100644
index 0000000..2c7a29a
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/ProtocolException.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+/**
+ * @hide
+ */
+public class ProtocolException extends RuntimeException {
+
+ public ProtocolException() {
+ super();
+ }
+
+ public ProtocolException(String message) {
+ super(message);
+ }
+
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/RandomScheduler.java b/media/mca/filterfw/java/android/filterfw/core/RandomScheduler.java
new file mode 100644
index 0000000..087f5db
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/RandomScheduler.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import java.util.Random;
+import java.util.Vector;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.Scheduler;
+
+/**
+ * @hide
+ */
+public class RandomScheduler extends Scheduler {
+
+ private Random mRand = new Random();
+
+ public RandomScheduler(FilterGraph graph) {
+ super(graph);
+ }
+
+ @Override
+ public void reset() {
+ }
+
+ @Override
+ public Filter scheduleNextNode() {
+ Vector<Filter> candidates = new Vector<Filter>();
+ for (Filter filter : getGraph().getFilters()) {
+ if (filter.canProcess())
+ candidates.add(filter);
+ }
+ if (candidates.size() > 0) {
+ int r = mRand.nextInt(candidates.size());
+ return candidates.elementAt(r);
+ }
+ return null;
+ }
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/RoundRobinScheduler.java b/media/mca/filterfw/java/android/filterfw/core/RoundRobinScheduler.java
new file mode 100644
index 0000000..12cbf19
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/RoundRobinScheduler.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import java.util.Set;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.Scheduler;
+
+/**
+ * @hide
+ */
+public class RoundRobinScheduler extends Scheduler {
+
+ private int mLastPos = -1;
+
+ public RoundRobinScheduler(FilterGraph graph) {
+ super(graph);
+ }
+
+ @Override
+ public void reset() {
+ mLastPos = -1;
+ }
+
+ @Override
+ public Filter scheduleNextNode() {
+ Set<Filter> all_filters = getGraph().getFilters();
+ if (mLastPos >= all_filters.size()) mLastPos = -1;
+ int pos = 0;
+ Filter first = null;
+ int firstNdx = -1;
+ for (Filter filter : all_filters) {
+ if (filter.canProcess()) {
+ if (pos <= mLastPos) {
+ if (first == null) {
+ // store the first available filter
+ first = filter;
+ firstNdx = pos;
+ }
+ } else {
+ // return the next available filter since last
+ mLastPos = pos;
+ return filter;
+ }
+ }
+ pos ++;
+ }
+ // going around from the beginning
+ if (first != null ) {
+ mLastPos = firstNdx;
+ return first;
+ }
+ // if there is nothing to be scheduled, still keep the previous
+ // position.
+ return null;
+ }
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/Scheduler.java b/media/mca/filterfw/java/android/filterfw/core/Scheduler.java
new file mode 100644
index 0000000..6f0864a
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/Scheduler.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterGraph;
+
+/**
+ * @hide
+ */
+public abstract class Scheduler {
+ // All methods are core internal methods as Scheduler internals are only used by the GraphRunner.
+
+ private FilterGraph mGraph;
+
+ Scheduler(FilterGraph graph) {
+ mGraph = graph;
+ }
+
+ FilterGraph getGraph() {
+ return mGraph;
+ }
+
+ abstract void reset();
+
+ abstract Filter scheduleNextNode();
+
+ boolean finished() {
+ // TODO: Check that the state of all nodes is FINISHED.
+ return true;
+ }
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/SerializedFrame.java b/media/mca/filterfw/java/android/filterfw/core/SerializedFrame.java
new file mode 100644
index 0000000..f493fd2
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/SerializedFrame.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.FrameManager;
+import android.filterfw.core.NativeBuffer;
+import android.filterfw.format.ObjectFormat;
+import android.graphics.Bitmap;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OptionalDataException;
+import java.io.OutputStream;
+import java.io.StreamCorruptedException;
+import java.lang.reflect.Constructor;
+import java.nio.ByteBuffer;
+
+/**
+ * A frame that serializes any assigned values. Such a frame is used when passing data objects
+ * between threads.
+ *
+ * @hide
+ */
+public class SerializedFrame extends Frame {
+
+ /**
+ * The initial capacity of the serialized data stream.
+ */
+ private final static int INITIAL_CAPACITY = 64;
+
+ /**
+ * The internal data streams.
+ */
+ private DirectByteOutputStream mByteOutputStream;
+ private ObjectOutputStream mObjectOut;
+
+ /**
+ * An unsynchronized output stream that writes data to an accessible byte array. Callers are
+ * responsible for synchronization. This is more efficient than a ByteArrayOutputStream, as
+ * there are no array copies or synchronization involved to read back written data.
+ */
+ private class DirectByteOutputStream extends OutputStream {
+ private byte[] mBuffer = null;
+ private int mOffset = 0;
+ private int mDataOffset = 0;
+
+ public DirectByteOutputStream(int size) {
+ mBuffer = new byte[size];
+ }
+
+ private final void ensureFit(int bytesToWrite) {
+ if (mOffset + bytesToWrite > mBuffer.length) {
+ byte[] oldBuffer = mBuffer;
+ mBuffer = new byte[Math.max(mOffset + bytesToWrite, mBuffer.length * 2)];
+ System.arraycopy(oldBuffer, 0, mBuffer, 0, mOffset);
+ oldBuffer = null;
+ }
+ }
+
+ public final void markHeaderEnd() {
+ mDataOffset = mOffset;
+ }
+
+ public final int getSize() {
+ return mOffset;
+ }
+
+ public byte[] getByteArray() {
+ return mBuffer;
+ }
+
+ @Override
+ public final void write(byte b[]) {
+ write(b, 0, b.length);
+ }
+
+ @Override
+ public final void write(byte b[], int off, int len) {
+ ensureFit(len);
+ System.arraycopy(b, off, mBuffer, mOffset, len);
+ mOffset += len;
+ }
+
+ @Override
+ public final void write(int b) {
+ ensureFit(1);
+ mBuffer[mOffset++] = (byte)b;
+ }
+
+ public final void reset() {
+ mOffset = mDataOffset;
+ }
+
+ public final DirectByteInputStream getInputStream() {
+ return new DirectByteInputStream(mBuffer, mOffset);
+ }
+ }
+
+ /**
+ * An unsynchronized input stream that reads data directly from a provided byte array. Callers
+ * are responsible for synchronization and ensuring that the byte buffer is valid.
+ */
+ private class DirectByteInputStream extends InputStream {
+
+ private byte[] mBuffer;
+ private int mPos = 0;
+ private int mSize;
+
+ public DirectByteInputStream(byte[] buffer, int size) {
+ mBuffer = buffer;
+ mSize = size;
+ }
+
+ @Override
+ public final int available() {
+ return mSize - mPos;
+ }
+
+ @Override
+ public final int read() {
+ return (mPos < mSize) ? (mBuffer[mPos++] & 0xFF) : -1;
+ }
+
+ @Override
+ public final int read(byte[] b, int off, int len) {
+ if (mPos >= mSize) {
+ return -1;
+ }
+ if ((mPos + len) > mSize) {
+ len = mSize - mPos;
+ }
+ System.arraycopy(mBuffer, mPos, b, off, len);
+ mPos += len;
+ return len;
+ }
+
+ @Override
+ public final long skip(long n) {
+ if ((mPos + n) > mSize) {
+ n = mSize - mPos;
+ }
+ if (n < 0) {
+ return 0;
+ }
+ mPos += n;
+ return n;
+ }
+ }
+
+ SerializedFrame(FrameFormat format, FrameManager frameManager) {
+ super(format, frameManager);
+ setReusable(false);
+
+ // Setup streams
+ try {
+ mByteOutputStream = new DirectByteOutputStream(INITIAL_CAPACITY);
+ mObjectOut = new ObjectOutputStream(mByteOutputStream);
+ mByteOutputStream.markHeaderEnd();
+ } catch (IOException e) {
+ throw new RuntimeException("Could not create serialization streams for "
+ + "SerializedFrame!", e);
+ }
+ }
+
+ static SerializedFrame wrapObject(Object object, FrameManager frameManager) {
+ FrameFormat format = ObjectFormat.fromObject(object, FrameFormat.TARGET_SIMPLE);
+ SerializedFrame result = new SerializedFrame(format, frameManager);
+ result.setObjectValue(object);
+ return result;
+ }
+
+ @Override
+ protected boolean hasNativeAllocation() {
+ return false;
+ }
+
+ @Override
+ protected void releaseNativeAllocation() {
+ }
+
+ @Override
+ public Object getObjectValue() {
+ return deserializeObjectValue();
+ }
+
+ @Override
+ public void setInts(int[] ints) {
+ assertFrameMutable();
+ setGenericObjectValue(ints);
+ }
+
+ @Override
+ public int[] getInts() {
+ Object result = deserializeObjectValue();
+ return (result instanceof int[]) ? (int[])result : null;
+ }
+
+ @Override
+ public void setFloats(float[] floats) {
+ assertFrameMutable();
+ setGenericObjectValue(floats);
+ }
+
+ @Override
+ public float[] getFloats() {
+ Object result = deserializeObjectValue();
+ return (result instanceof float[]) ? (float[])result : null;
+ }
+
+ @Override
+ public void setData(ByteBuffer buffer, int offset, int length) {
+ assertFrameMutable();
+ setGenericObjectValue(ByteBuffer.wrap(buffer.array(), offset, length));
+ }
+
+ @Override
+ public ByteBuffer getData() {
+ Object result = deserializeObjectValue();
+ return (result instanceof ByteBuffer) ? (ByteBuffer)result : null;
+ }
+
+ @Override
+ public void setBitmap(Bitmap bitmap) {
+ assertFrameMutable();
+ setGenericObjectValue(bitmap);
+ }
+
+ @Override
+ public Bitmap getBitmap() {
+ Object result = deserializeObjectValue();
+ return (result instanceof Bitmap) ? (Bitmap)result : null;
+ }
+
+ @Override
+ protected void setGenericObjectValue(Object object) {
+ serializeObjectValue(object);
+ }
+
+ private final void serializeObjectValue(Object object) {
+ try {
+ mByteOutputStream.reset();
+ mObjectOut.writeObject(object);
+ mObjectOut.flush();
+ mObjectOut.close();
+ } catch (IOException e) {
+ throw new RuntimeException("Could not serialize object " + object + " in "
+ + this + "!", e);
+ }
+ }
+
+ private final Object deserializeObjectValue() {
+ try {
+ InputStream inputStream = mByteOutputStream.getInputStream();
+ ObjectInputStream objectStream = new ObjectInputStream(inputStream);
+ return objectStream.readObject();
+ } catch (IOException e) {
+ throw new RuntimeException("Could not deserialize object in " + this + "!", e);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("Unable to deserialize object of unknown class in "
+ + this + "!", e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "SerializedFrame (" + getFormat() + ")";
+ }
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/ShaderProgram.java b/media/mca/filterfw/java/android/filterfw/core/ShaderProgram.java
new file mode 100644
index 0000000..a971cb6
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/ShaderProgram.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import android.filterfw.core.Frame;
+import android.filterfw.core.NativeAllocatorTag;
+import android.filterfw.core.Program;
+import android.filterfw.core.StopWatchMap;
+import android.filterfw.core.VertexFrame;
+import android.filterfw.geometry.Quad;
+import android.opengl.GLES20;
+
+/**
+ * @hide
+ */
+public class ShaderProgram extends Program {
+
+ private int shaderProgramId;
+
+ private int mMaxTileSize = 0;
+
+ // Keep a reference to the GL environment, so that it does not get deallocated while there
+ // are still programs living in it.
+ private GLEnvironment mGLEnvironment;
+
+ private StopWatchMap mTimer = null;
+
+ private void setTimer() {
+ mTimer = new StopWatchMap();
+ }
+
+ // Used from native layer for creating empty wrapper only!
+ private ShaderProgram() {
+ }
+
+ private ShaderProgram(NativeAllocatorTag tag) {
+ }
+
+ public ShaderProgram(FilterContext context, String fragmentShader) {
+ mGLEnvironment = getGLEnvironment(context);
+ allocate(mGLEnvironment, null, fragmentShader);
+ if (!compileAndLink()) {
+ throw new RuntimeException("Could not compile and link shader!");
+ }
+ this.setTimer();
+ }
+
+ public ShaderProgram(FilterContext context, String vertexShader, String fragmentShader) {
+ mGLEnvironment = getGLEnvironment(context);
+ allocate(mGLEnvironment, vertexShader, fragmentShader);
+ if (!compileAndLink()) {
+ throw new RuntimeException("Could not compile and link shader!");
+ }
+ this.setTimer();
+ }
+
+ public static ShaderProgram createIdentity(FilterContext context) {
+ ShaderProgram program = nativeCreateIdentity(getGLEnvironment(context));
+ program.setTimer();
+ return program;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ deallocate();
+ }
+
+ public GLEnvironment getGLEnvironment() {
+ return mGLEnvironment;
+ }
+
+ @Override
+ public void process(Frame[] inputs, Frame output) {
+ if (mTimer.LOG_MFF_RUNNING_TIMES) {
+ mTimer.start("glFinish");
+ GLES20.glFinish();
+ mTimer.stop("glFinish");
+ }
+
+ // Get the GL input frames
+ // TODO: We do the same in the NativeProgram... can we find a better way?!
+ GLFrame[] glInputs = new GLFrame[inputs.length];
+ for (int i = 0; i < inputs.length; ++i) {
+ if (inputs[i] instanceof GLFrame) {
+ glInputs[i] = (GLFrame)inputs[i];
+ } else {
+ throw new RuntimeException("ShaderProgram got non-GL frame as input " + i + "!");
+ }
+ }
+
+ // Get the GL output frame
+ GLFrame glOutput = null;
+ if (output instanceof GLFrame) {
+ glOutput = (GLFrame)output;
+ } else {
+ throw new RuntimeException("ShaderProgram got non-GL output frame!");
+ }
+
+ // Adjust tiles to meet maximum tile size requirement
+ if (mMaxTileSize > 0) {
+ int xTiles = (output.getFormat().getWidth() + mMaxTileSize - 1) / mMaxTileSize;
+ int yTiles = (output.getFormat().getHeight() + mMaxTileSize - 1) / mMaxTileSize;
+ setShaderTileCounts(xTiles, yTiles);
+ }
+
+ // Process!
+ if (!shaderProcess(glInputs, glOutput)) {
+ throw new RuntimeException("Error executing ShaderProgram!");
+ }
+
+ if (mTimer.LOG_MFF_RUNNING_TIMES) {
+ GLES20.glFinish();
+ }
+ }
+
+ @Override
+ public void setHostValue(String variableName, Object value) {
+ if (!setUniformValue(variableName, value)) {
+ throw new RuntimeException("Error setting uniform value for variable '" +
+ variableName + "'!");
+ }
+ }
+
+ @Override
+ public Object getHostValue(String variableName) {
+ return getUniformValue(variableName);
+ }
+
+ public void setAttributeValues(String attributeName, float[] data, int componentCount) {
+ if (!setShaderAttributeValues(attributeName, data, componentCount)) {
+ throw new RuntimeException("Error setting attribute value for attribute '" +
+ attributeName + "'!");
+ }
+ }
+
+ public void setAttributeValues(String attributeName,
+ VertexFrame vertexData,
+ int type,
+ int componentCount,
+ int strideInBytes,
+ int offsetInBytes,
+ boolean normalize) {
+ if (!setShaderAttributeVertexFrame(attributeName,
+ vertexData,
+ type,
+ componentCount,
+ strideInBytes,
+ offsetInBytes,
+ normalize)) {
+ throw new RuntimeException("Error setting attribute value for attribute '" +
+ attributeName + "'!");
+ }
+ }
+
+ public void setSourceRegion(Quad region) {
+ setSourceRegion(region.p0.x, region.p0.y,
+ region.p1.x, region.p1.y,
+ region.p2.x, region.p2.y,
+ region.p3.x, region.p3.y);
+ }
+
+ public void setTargetRegion(Quad region) {
+ setTargetRegion(region.p0.x, region.p0.y,
+ region.p1.x, region.p1.y,
+ region.p2.x, region.p2.y,
+ region.p3.x, region.p3.y);
+ }
+
+ public void setSourceRect(float x, float y, float width, float height) {
+ setSourceRegion(x, y, x + width, y, x, y + height, x + width, y + height);
+ }
+
+ public void setTargetRect(float x, float y, float width, float height) {
+ setTargetRegion(x, y, x + width, y, x, y + height, x + width, y + height);
+ }
+
+ public void setClearsOutput(boolean clears) {
+ if (!setShaderClearsOutput(clears)) {
+ throw new RuntimeException("Could not set clears-output flag to " + clears + "!");
+ }
+ }
+
+ public void setClearColor(float r, float g, float b) {
+ if (!setShaderClearColor(r, g, b)) {
+ throw new RuntimeException("Could not set clear color to " + r + "," + g + "," + b + "!");
+ }
+ }
+
+ public void setBlendEnabled(boolean enable) {
+ if (!setShaderBlendEnabled(enable)) {
+ throw new RuntimeException("Could not set Blending " + enable + "!");
+ }
+ }
+
+ public void setBlendFunc(int sfactor, int dfactor) {
+ if (!setShaderBlendFunc(sfactor, dfactor)) {
+ throw new RuntimeException("Could not set BlendFunc " + sfactor +","+ dfactor + "!");
+ }
+ }
+
+ public void setDrawMode(int drawMode) {
+ if (!setShaderDrawMode(drawMode)) {
+ throw new RuntimeException("Could not set GL draw-mode to " + drawMode + "!");
+ }
+ }
+
+ public void setVertexCount(int count) {
+ if (!setShaderVertexCount(count)) {
+ throw new RuntimeException("Could not set GL vertex count to " + count + "!");
+ }
+ }
+
+ public void setMaximumTileSize(int size) {
+ mMaxTileSize = size;
+ }
+
+ public void beginDrawing() {
+ if (!beginShaderDrawing()) {
+ throw new RuntimeException("Could not prepare shader-program for drawing!");
+ }
+ }
+
+ private static GLEnvironment getGLEnvironment(FilterContext context) {
+ GLEnvironment result = context != null ? context.getGLEnvironment() : null;
+ if (result == null) {
+ throw new NullPointerException("Attempting to create ShaderProgram with no GL "
+ + "environment in place!");
+ }
+ return result;
+ }
+
+ static {
+ System.loadLibrary("filterfw");
+ }
+
+ private native boolean allocate(GLEnvironment glEnv,
+ String vertexShader,
+ String fragmentShader);
+
+ private native boolean deallocate();
+
+ private native boolean compileAndLink();
+
+ private native boolean shaderProcess(GLFrame[] inputs, GLFrame output);
+
+ private native boolean setUniformValue(String name, Object value);
+
+ private native Object getUniformValue(String name);
+
+ public native boolean setSourceRegion(float x0, float y0, float x1, float y1,
+ float x2, float y2, float x3, float y3);
+
+ private native boolean setTargetRegion(float x0, float y0, float x1, float y1,
+ float x2, float y2, float x3, float y3);
+
+ private static native ShaderProgram nativeCreateIdentity(GLEnvironment glEnv);
+
+ private native boolean setShaderClearsOutput(boolean clears);
+
+ private native boolean setShaderBlendEnabled(boolean enable);
+
+ private native boolean setShaderBlendFunc(int sfactor, int dfactor);
+
+ private native boolean setShaderClearColor(float r, float g, float b);
+
+ private native boolean setShaderDrawMode(int drawMode);
+
+ private native boolean setShaderTileCounts(int xCount, int yCount);
+
+ private native boolean setShaderVertexCount(int vertexCount);
+
+ private native boolean beginShaderDrawing();
+
+ private native boolean setShaderAttributeValues(String attributeName,
+ float[] data,
+ int componentCount);
+
+ private native boolean setShaderAttributeVertexFrame(String attributeName,
+ VertexFrame vertexData,
+ int type,
+ int componentCount,
+ int strideInBytes,
+ int offsetInBytes,
+ boolean normalize);
+
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/SimpleFrame.java b/media/mca/filterfw/java/android/filterfw/core/SimpleFrame.java
new file mode 100644
index 0000000..534a30d
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/SimpleFrame.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.FrameManager;
+import android.filterfw.core.NativeBuffer;
+import android.filterfw.format.ObjectFormat;
+import android.graphics.Bitmap;
+
+import java.lang.reflect.Constructor;
+import java.nio.ByteBuffer;
+
+/**
+ * @hide
+ */
+public class SimpleFrame extends Frame {
+
+ private Object mObject;
+
+ SimpleFrame(FrameFormat format, FrameManager frameManager) {
+ super(format, frameManager);
+ initWithFormat(format);
+ setReusable(false);
+ }
+
+ static SimpleFrame wrapObject(Object object, FrameManager frameManager) {
+ FrameFormat format = ObjectFormat.fromObject(object, FrameFormat.TARGET_SIMPLE);
+ SimpleFrame result = new SimpleFrame(format, frameManager);
+ result.setObjectValue(object);
+ return result;
+ }
+
+ private void initWithFormat(FrameFormat format) {
+ final int count = format.getLength();
+ final int baseType = format.getBaseType();
+ switch (baseType) {
+ case FrameFormat.TYPE_BYTE:
+ mObject = new byte[count];
+ break;
+ case FrameFormat.TYPE_INT16:
+ mObject = new short[count];
+ break;
+ case FrameFormat.TYPE_INT32:
+ mObject = new int[count];
+ break;
+ case FrameFormat.TYPE_FLOAT:
+ mObject = new float[count];
+ break;
+ case FrameFormat.TYPE_DOUBLE:
+ mObject = new double[count];
+ break;
+ default:
+ mObject = null;
+ break;
+ }
+ }
+
+ @Override
+ protected boolean hasNativeAllocation() {
+ return false;
+ }
+
+ @Override
+ protected void releaseNativeAllocation() {
+ }
+
+ @Override
+ public Object getObjectValue() {
+ return mObject;
+ }
+
+ @Override
+ public void setInts(int[] ints) {
+ assertFrameMutable();
+ setGenericObjectValue(ints);
+ }
+
+ @Override
+ public int[] getInts() {
+ return (mObject instanceof int[]) ? (int[])mObject : null;
+ }
+
+ @Override
+ public void setFloats(float[] floats) {
+ assertFrameMutable();
+ setGenericObjectValue(floats);
+ }
+
+ @Override
+ public float[] getFloats() {
+ return (mObject instanceof float[]) ? (float[])mObject : null;
+ }
+
+ @Override
+ public void setData(ByteBuffer buffer, int offset, int length) {
+ assertFrameMutable();
+ setGenericObjectValue(ByteBuffer.wrap(buffer.array(), offset, length));
+ }
+
+ @Override
+ public ByteBuffer getData() {
+ return (mObject instanceof ByteBuffer) ? (ByteBuffer)mObject : null;
+ }
+
+ @Override
+ public void setBitmap(Bitmap bitmap) {
+ assertFrameMutable();
+ setGenericObjectValue(bitmap);
+ }
+
+ @Override
+ public Bitmap getBitmap() {
+ return (mObject instanceof Bitmap) ? (Bitmap)mObject : null;
+ }
+
+ private void setFormatObjectClass(Class objectClass) {
+ MutableFrameFormat format = getFormat().mutableCopy();
+ format.setObjectClass(objectClass);
+ setFormat(format);
+ }
+
+ @Override
+ protected void setGenericObjectValue(Object object) {
+ // Update the FrameFormat class
+ // TODO: Take this out! FrameFormats should not be modified and convenience formats used
+ // instead!
+ FrameFormat format = getFormat();
+ if (format.getObjectClass() == null) {
+ setFormatObjectClass(object.getClass());
+ } else if (!format.getObjectClass().isAssignableFrom(object.getClass())) {
+ throw new RuntimeException(
+ "Attempting to set object value of type '" + object.getClass() + "' on " +
+ "SimpleFrame of type '" + format.getObjectClass() + "'!");
+ }
+
+ // Set the object value
+ mObject = object;
+ }
+
+ @Override
+ public String toString() {
+ return "SimpleFrame (" + getFormat() + ")";
+ }
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/SimpleFrameManager.java b/media/mca/filterfw/java/android/filterfw/core/SimpleFrameManager.java
new file mode 100644
index 0000000..e2b9047
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/SimpleFrameManager.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.FrameManager;
+import android.filterfw.core.GLFrame;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.SimpleFrame;
+import android.filterfw.core.VertexFrame;
+
+/**
+ * @hide
+ */
+public class SimpleFrameManager extends FrameManager {
+
+ public SimpleFrameManager() {
+ }
+
+ @Override
+ public Frame newFrame(FrameFormat format) {
+ return createNewFrame(format);
+ }
+
+ @Override
+ public Frame newBoundFrame(FrameFormat format, int bindingType, long bindingId) {
+ Frame result = null;
+ switch(format.getTarget()) {
+ case FrameFormat.TARGET_GPU: {
+ GLFrame glFrame = new GLFrame(format, this, bindingType, bindingId);
+ glFrame.init(getGLEnvironment());
+ result = glFrame;
+ break;
+ }
+
+ default:
+ throw new RuntimeException("Attached frames are not supported for target type: "
+ + FrameFormat.targetToString(format.getTarget()) + "!");
+ }
+ return result;
+ }
+
+ private Frame createNewFrame(FrameFormat format) {
+ Frame result = null;
+ switch(format.getTarget()) {
+ case FrameFormat.TARGET_SIMPLE:
+ result = new SimpleFrame(format, this);
+ break;
+
+ case FrameFormat.TARGET_NATIVE:
+ result = new NativeFrame(format, this);
+ break;
+
+ case FrameFormat.TARGET_GPU: {
+ GLFrame glFrame = new GLFrame(format, this);
+ glFrame.init(getGLEnvironment());
+ result = glFrame;
+ break;
+ }
+
+ case FrameFormat.TARGET_VERTEXBUFFER: {
+ result = new VertexFrame(format, this);
+ break;
+ }
+
+ default:
+ throw new RuntimeException("Unsupported frame target type: " +
+ FrameFormat.targetToString(format.getTarget()) + "!");
+ }
+ return result;
+ }
+
+ @Override
+ public Frame retainFrame(Frame frame) {
+ frame.incRefCount();
+ return frame;
+ }
+
+ @Override
+ public Frame releaseFrame(Frame frame) {
+ int refCount = frame.decRefCount();
+ if (refCount == 0 && frame.hasNativeAllocation()) {
+ frame.releaseNativeAllocation();
+ return null;
+ } else if (refCount < 0) {
+ throw new RuntimeException("Frame reference count dropped below 0!");
+ }
+ return frame;
+ }
+
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/SimpleScheduler.java b/media/mca/filterfw/java/android/filterfw/core/SimpleScheduler.java
new file mode 100644
index 0000000..bb4e5ba
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/SimpleScheduler.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.Scheduler;
+
+/**
+ * @hide
+ */
+public class SimpleScheduler extends Scheduler {
+
+ public SimpleScheduler(FilterGraph graph) {
+ super(graph);
+ }
+
+ @Override
+ public void reset() {
+ }
+
+ @Override
+ public Filter scheduleNextNode() {
+ for (Filter filter : getGraph().getFilters()) {
+ if (filter.canProcess())
+ return filter;
+ }
+ return null;
+ }
+
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/StopWatchMap.java b/media/mca/filterfw/java/android/filterfw/core/StopWatchMap.java
new file mode 100644
index 0000000..444a1fc
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/StopWatchMap.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.filterfw.core;
+
+import android.os.SystemClock;
+import android.util.Log;
+import java.util.HashMap;
+
+/**
+ * @hide
+ */
+class StopWatch {
+
+ private int STOP_WATCH_LOGGING_PERIOD = 200;
+ private String TAG = "MFF";
+
+ private String mName;
+ private long mStartTime;
+ private long mTotalTime;
+ private int mNumCalls;
+
+ public StopWatch(String name) {
+ mName = name;
+ mStartTime = -1;
+ mTotalTime = 0;
+ mNumCalls = 0;
+ }
+
+ public void start() {
+ if (mStartTime != -1) {
+ throw new RuntimeException(
+ "Calling start with StopWatch already running");
+ }
+ mStartTime = SystemClock.elapsedRealtime();
+ }
+
+ public void stop() {
+ if (mStartTime == -1) {
+ throw new RuntimeException(
+ "Calling stop with StopWatch already stopped");
+ }
+ long stopTime = SystemClock.elapsedRealtime();
+ mTotalTime += stopTime - mStartTime;
+ ++mNumCalls;
+ mStartTime = -1;
+ if (mNumCalls % STOP_WATCH_LOGGING_PERIOD == 0) {
+ Log.i(TAG, "AVG ms/call " + mName + ": " +
+ String.format("%.1f", mTotalTime * 1.0f / mNumCalls));
+ mTotalTime = 0;
+ mNumCalls = 0;
+ }
+ }
+
+}
+
+public class StopWatchMap {
+
+ public boolean LOG_MFF_RUNNING_TIMES = false;
+
+ private HashMap<String, StopWatch> mStopWatches = null;
+
+ public StopWatchMap() {
+ mStopWatches = new HashMap<String, StopWatch>();
+ }
+
+ public void start(String stopWatchName) {
+ if (!LOG_MFF_RUNNING_TIMES) {
+ return;
+ }
+ if (!mStopWatches.containsKey(stopWatchName)) {
+ mStopWatches.put(stopWatchName, new StopWatch(stopWatchName));
+ }
+ mStopWatches.get(stopWatchName).start();
+ }
+
+ public void stop(String stopWatchName) {
+ if (!LOG_MFF_RUNNING_TIMES) {
+ return;
+ }
+ if (!mStopWatches.containsKey(stopWatchName)) {
+ throw new RuntimeException(
+ "Calling stop with unknown stopWatchName: " + stopWatchName);
+ }
+ mStopWatches.get(stopWatchName).stop();
+ }
+
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/StreamPort.java b/media/mca/filterfw/java/android/filterfw/core/StreamPort.java
new file mode 100644
index 0000000..8520a0b
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/StreamPort.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+/**
+ * @hide
+ */
+public class StreamPort extends InputPort {
+
+ private Frame mFrame;
+ private boolean mPersistent;
+
+ public StreamPort(Filter filter, String name) {
+ super(filter, name);
+ }
+
+ @Override
+ public void clear() {
+ if (mFrame != null) {
+ mFrame.release();
+ mFrame = null;
+ }
+ }
+
+ @Override
+ public void setFrame(Frame frame) {
+ assignFrame(frame, true);
+ }
+
+ @Override
+ public void pushFrame(Frame frame) {
+ assignFrame(frame, false);
+ }
+
+ protected synchronized void assignFrame(Frame frame, boolean persistent) {
+ assertPortIsOpen();
+ checkFrameType(frame, persistent);
+
+ if (persistent) {
+ if (mFrame != null) {
+ mFrame.release();
+ }
+ } else if (mFrame != null) {
+ throw new RuntimeException(
+ "Attempting to push more than one frame on port: " + this + "!");
+ }
+ mFrame = frame.retain();
+ mFrame.markReadOnly();
+ mPersistent = persistent;
+ }
+
+ @Override
+ public synchronized Frame pullFrame() {
+ // Make sure we have a frame
+ if (mFrame == null) {
+ throw new RuntimeException("No frame available to pull on port: " + this + "!");
+ }
+
+ // Return a retained result
+ Frame result = mFrame;
+ if (mPersistent) {
+ mFrame.retain();
+ } else {
+ mFrame = null;
+ }
+ return result;
+ }
+
+ @Override
+ public synchronized boolean hasFrame() {
+ return mFrame != null;
+ }
+
+ @Override
+ public String toString() {
+ return "input " + super.toString();
+ }
+
+ @Override
+ public synchronized void transfer(FilterContext context) {
+ if (mFrame != null) {
+ checkFrameManager(mFrame, context);
+ }
+ }
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/SyncRunner.java b/media/mca/filterfw/java/android/filterfw/core/SyncRunner.java
new file mode 100644
index 0000000..abbd359
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/SyncRunner.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import android.os.ConditionVariable;
+import android.util.Log;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @hide
+ */
+public class SyncRunner extends GraphRunner {
+
+ private Scheduler mScheduler = null;
+
+ private OnRunnerDoneListener mDoneListener = null;
+ private ScheduledThreadPoolExecutor mWakeExecutor = new ScheduledThreadPoolExecutor(1);
+ private ConditionVariable mWakeCondition = new ConditionVariable();
+
+ private StopWatchMap mTimer = null;
+
+ private final boolean mLogVerbose;
+ private final static String TAG = "SyncRunner";
+
+ // TODO: Provide factory based constructor?
+ public SyncRunner(FilterContext context, FilterGraph graph, Class schedulerClass) {
+ super(context);
+
+ mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+
+ if (mLogVerbose) Log.v(TAG, "Initializing SyncRunner");
+
+ // Create the scheduler
+ if (Scheduler.class.isAssignableFrom(schedulerClass)) {
+ try {
+ Constructor schedulerConstructor = schedulerClass.getConstructor(FilterGraph.class);
+ mScheduler = (Scheduler)schedulerConstructor.newInstance(graph);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException("Scheduler does not have constructor <init>(FilterGraph)!", e);
+ } catch (InstantiationException e) {
+ throw new RuntimeException("Could not instantiate the Scheduler instance!", e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException("Cannot access Scheduler constructor!", e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException("Scheduler constructor threw an exception", e);
+ } catch (Exception e) {
+ throw new RuntimeException("Could not instantiate Scheduler", e);
+ }
+ } else {
+ throw new IllegalArgumentException("Class provided is not a Scheduler subclass!");
+ }
+
+ // Associate this runner and the graph with the context
+ mFilterContext = context;
+ mFilterContext.addGraph(graph);
+
+ mTimer = new StopWatchMap();
+
+ if (mLogVerbose) Log.v(TAG, "Setting up filters");
+
+ // Setup graph filters
+ graph.setupFilters();
+ }
+
+ @Override
+ public FilterGraph getGraph() {
+ return mScheduler != null ? mScheduler.getGraph() : null;
+ }
+
+ public int step() {
+ assertReadyToStep();
+ if (!getGraph().isReady() ) {
+ throw new RuntimeException("Trying to process graph that is not open!");
+ }
+ return performStep() ? RESULT_RUNNING : determinePostRunState();
+ }
+
+ public void beginProcessing() {
+ mScheduler.reset();
+ getGraph().beginProcessing();
+ }
+
+ public void close() {
+ // Close filters
+ if (mLogVerbose) Log.v(TAG, "Closing graph.");
+ getGraph().closeFilters(mFilterContext);
+ mScheduler.reset();
+ }
+
+ @Override
+ public void run() {
+ if (mLogVerbose) Log.v(TAG, "Beginning run.");
+
+ assertReadyToStep();
+
+ // Preparation
+ beginProcessing();
+ boolean glActivated = activateGlContext();
+
+ // Run
+ boolean keepRunning = true;
+ while (keepRunning) {
+ keepRunning = performStep();
+ }
+
+ // Cleanup
+ if (glActivated) {
+ deactivateGlContext();
+ }
+
+ // Call completion callback if set
+ if (mDoneListener != null) {
+ if (mLogVerbose) Log.v(TAG, "Calling completion listener.");
+ mDoneListener.onRunnerDone(determinePostRunState());
+ }
+ if (mLogVerbose) Log.v(TAG, "Run complete");
+ }
+
+ @Override
+ public boolean isRunning() {
+ return false;
+ }
+
+ @Override
+ public void setDoneCallback(OnRunnerDoneListener listener) {
+ mDoneListener = listener;
+ }
+
+ @Override
+ public void stop() {
+ throw new RuntimeException("SyncRunner does not support stopping a graph!");
+ }
+
+ @Override
+ synchronized public Exception getError() {
+ return null;
+ }
+
+ protected void waitUntilWake() {
+ mWakeCondition.block();
+ }
+
+ protected void processFilterNode(Filter filter) {
+ if (mLogVerbose) Log.v(TAG, "Processing filter node");
+ filter.performProcess(mFilterContext);
+ if (filter.getStatus() == Filter.STATUS_ERROR) {
+ throw new RuntimeException("There was an error executing " + filter + "!");
+ } else if (filter.getStatus() == Filter.STATUS_SLEEPING) {
+ if (mLogVerbose) Log.v(TAG, "Scheduling filter wakeup");
+ scheduleFilterWake(filter, filter.getSleepDelay());
+ }
+ }
+
+ protected void scheduleFilterWake(Filter filter, int delay) {
+ // Close the wake condition
+ mWakeCondition.close();
+
+ // Schedule the wake-up
+ final Filter filterToSchedule = filter;
+ final ConditionVariable conditionToWake = mWakeCondition;
+
+ mWakeExecutor.schedule(new Runnable() {
+ @Override
+ public void run() {
+ filterToSchedule.unsetStatus(Filter.STATUS_SLEEPING);
+ conditionToWake.open();
+ }
+ }, delay, TimeUnit.MILLISECONDS);
+ }
+
+ protected int determinePostRunState() {
+ boolean isBlocked = false;
+ for (Filter filter : mScheduler.getGraph().getFilters()) {
+ if (filter.isOpen()) {
+ if (filter.getStatus() == Filter.STATUS_SLEEPING) {
+ // If ANY node is sleeping, we return our state as sleeping
+ return RESULT_SLEEPING;
+ } else {
+ // If a node is still open, it is blocked (by input or output)
+ return RESULT_BLOCKED;
+ }
+ }
+ }
+ return RESULT_FINISHED;
+ }
+
+ // Core internal methods ///////////////////////////////////////////////////////////////////////
+ boolean performStep() {
+ if (mLogVerbose) Log.v(TAG, "Performing one step.");
+ Filter filter = mScheduler.scheduleNextNode();
+ if (filter != null) {
+ mTimer.start(filter.getName());
+ processFilterNode(filter);
+ mTimer.stop(filter.getName());
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ void assertReadyToStep() {
+ if (mScheduler == null) {
+ throw new RuntimeException("Attempting to run schedule with no scheduler in place!");
+ } else if (getGraph() == null) {
+ throw new RuntimeException("Calling step on scheduler with no graph in place!");
+ }
+ }
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/VertexFrame.java b/media/mca/filterfw/java/android/filterfw/core/VertexFrame.java
new file mode 100644
index 0000000..6982ce3
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/VertexFrame.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.core;
+
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.FrameManager;
+import android.graphics.Bitmap;
+
+import java.nio.ByteBuffer;
+
+/**
+ * @hide
+ */
+public class VertexFrame extends Frame {
+
+ private int vertexFrameId = -1;
+
+ VertexFrame(FrameFormat format, FrameManager frameManager) {
+ super(format, frameManager);
+ if (getFormat().getSize() <= 0) {
+ throw new IllegalArgumentException("Initializing vertex frame with zero size!");
+ } else {
+ if (!nativeAllocate(getFormat().getSize())) {
+ throw new RuntimeException("Could not allocate vertex frame!");
+ }
+ }
+ }
+
+ @Override
+ protected synchronized boolean hasNativeAllocation() {
+ return vertexFrameId != -1;
+ }
+
+ @Override
+ protected synchronized void releaseNativeAllocation() {
+ nativeDeallocate();
+ vertexFrameId = -1;
+ }
+
+ @Override
+ public Object getObjectValue() {
+ throw new RuntimeException("Vertex frames do not support reading data!");
+ }
+
+ @Override
+ public void setInts(int[] ints) {
+ assertFrameMutable();
+ if (!setNativeInts(ints)) {
+ throw new RuntimeException("Could not set int values for vertex frame!");
+ }
+ }
+
+ @Override
+ public int[] getInts() {
+ throw new RuntimeException("Vertex frames do not support reading data!");
+ }
+
+ @Override
+ public void setFloats(float[] floats) {
+ assertFrameMutable();
+ if (!setNativeFloats(floats)) {
+ throw new RuntimeException("Could not set int values for vertex frame!");
+ }
+ }
+
+ @Override
+ public float[] getFloats() {
+ throw new RuntimeException("Vertex frames do not support reading data!");
+ }
+
+ @Override
+ public void setData(ByteBuffer buffer, int offset, int length) {
+ assertFrameMutable();
+ byte[] bytes = buffer.array();
+ if (getFormat().getSize() != bytes.length) {
+ throw new RuntimeException("Data size in setData does not match vertex frame size!");
+ } else if (!setNativeData(bytes, offset, length)) {
+ throw new RuntimeException("Could not set vertex frame data!");
+ }
+ }
+
+ @Override
+ public ByteBuffer getData() {
+ throw new RuntimeException("Vertex frames do not support reading data!");
+ }
+
+ @Override
+ public void setBitmap(Bitmap bitmap) {
+ throw new RuntimeException("Unsupported: Cannot set vertex frame bitmap value!");
+ }
+
+ @Override
+ public Bitmap getBitmap() {
+ throw new RuntimeException("Vertex frames do not support reading data!");
+ }
+
+ @Override
+ public void setDataFromFrame(Frame frame) {
+ // TODO: Optimize
+ super.setDataFromFrame(frame);
+ }
+
+ public int getVboId() {
+ return getNativeVboId();
+ }
+
+ @Override
+ public String toString() {
+ return "VertexFrame (" + getFormat() + ") with VBO ID " + getVboId();
+ }
+
+ static {
+ System.loadLibrary("filterfw");
+ }
+
+ private native boolean nativeAllocate(int size);
+
+ private native boolean nativeDeallocate();
+
+ private native boolean setNativeData(byte[] data, int offset, int length);
+
+ private native boolean setNativeInts(int[] ints);
+
+ private native boolean setNativeFloats(float[] floats);
+
+ private native int getNativeVboId();
+}
diff --git a/media/mca/filterfw/java/android/filterfw/core/package-info.java b/media/mca/filterfw/java/android/filterfw/core/package-info.java
new file mode 100644
index 0000000..4afda1b
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/core/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * @hide
+ */
+package android.filterfw.core;
diff --git a/media/mca/filterfw/java/android/filterfw/format/ImageFormat.java b/media/mca/filterfw/java/android/filterfw/format/ImageFormat.java
new file mode 100644
index 0000000..d57f47c
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/format/ImageFormat.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.format;
+
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.MutableFrameFormat;
+import android.graphics.Bitmap;
+
+/**
+ * @hide
+ */
+public class ImageFormat {
+
+ public static final String COLORSPACE_KEY = "colorspace";
+
+ public static final int COLORSPACE_GRAY = 1;
+ public static final int COLORSPACE_RGB = 2;
+ public static final int COLORSPACE_RGBA = 3;
+ public static final int COLORSPACE_YUV = 4;
+
+ public static MutableFrameFormat create(int width,
+ int height,
+ int colorspace,
+ int bytesPerSample,
+ int target) {
+ MutableFrameFormat result = new MutableFrameFormat(FrameFormat.TYPE_BYTE, target);
+ result.setDimensions(width, height);
+ result.setBytesPerSample(bytesPerSample);
+ result.setMetaValue(COLORSPACE_KEY, colorspace);
+ if (target == FrameFormat.TARGET_SIMPLE) {
+ result.setObjectClass(Bitmap.class);
+ }
+ return result;
+ }
+
+ public static MutableFrameFormat create(int width,
+ int height,
+ int colorspace,
+ int target) {
+ return create(width,
+ height,
+ colorspace,
+ bytesPerSampleForColorspace(colorspace),
+ target);
+ }
+
+ public static MutableFrameFormat create(int colorspace, int target) {
+ return create(FrameFormat.SIZE_UNSPECIFIED,
+ FrameFormat.SIZE_UNSPECIFIED,
+ colorspace,
+ bytesPerSampleForColorspace(colorspace),
+ target);
+ }
+
+ public static MutableFrameFormat create(int colorspace) {
+ return create(FrameFormat.SIZE_UNSPECIFIED,
+ FrameFormat.SIZE_UNSPECIFIED,
+ colorspace,
+ bytesPerSampleForColorspace(colorspace),
+ FrameFormat.TARGET_UNSPECIFIED);
+ }
+
+ public static int bytesPerSampleForColorspace(int colorspace) {
+ switch (colorspace) {
+ case COLORSPACE_GRAY:
+ return 1;
+ case COLORSPACE_RGB:
+ return 3;
+ case COLORSPACE_RGBA:
+ return 4;
+ case COLORSPACE_YUV:
+ return 3;
+ default:
+ throw new RuntimeException("Unknown colorspace id " + colorspace + "!");
+ }
+ }
+}
diff --git a/media/mca/filterfw/java/android/filterfw/format/ObjectFormat.java b/media/mca/filterfw/java/android/filterfw/format/ObjectFormat.java
new file mode 100644
index 0000000..ae39628
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/format/ObjectFormat.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.format;
+
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeBuffer;
+
+/**
+ * @hide
+ */
+public class ObjectFormat {
+
+ public static MutableFrameFormat fromClass(Class clazz, int count, int target) {
+ // Create frame format
+ MutableFrameFormat result = new MutableFrameFormat(FrameFormat.TYPE_OBJECT, target);
+ result.setObjectClass(getBoxedClass(clazz));
+ if (count != FrameFormat.SIZE_UNSPECIFIED) {
+ result.setDimensions(count);
+ }
+ result.setBytesPerSample(bytesPerSampleForClass(clazz, target));
+ return result;
+ }
+
+ public static MutableFrameFormat fromClass(Class clazz, int target) {
+ return fromClass(clazz, FrameFormat.SIZE_UNSPECIFIED, target);
+ }
+
+ public static MutableFrameFormat fromObject(Object object, int target) {
+ return object == null
+ ? new MutableFrameFormat(FrameFormat.TYPE_OBJECT, target)
+ : fromClass(object.getClass(), FrameFormat.SIZE_UNSPECIFIED, target);
+ }
+
+ public static MutableFrameFormat fromObject(Object object, int count, int target) {
+ return object == null
+ ? new MutableFrameFormat(FrameFormat.TYPE_OBJECT, target)
+ : fromClass(object.getClass(), count, target);
+ }
+
+ private static int bytesPerSampleForClass(Class clazz, int target) {
+ // Native targets have objects manifested in a byte buffer. Thus it is important to
+ // correctly determine the size of single element here.
+ if (target == FrameFormat.TARGET_NATIVE) {
+ if (!NativeBuffer.class.isAssignableFrom(clazz)) {
+ throw new IllegalArgumentException("Native object-based formats must be of a " +
+ "NativeBuffer subclass! (Received class: " + clazz + ").");
+ }
+ try {
+ return ((NativeBuffer)clazz.newInstance()).getElementSize();
+ } catch (Exception e) {
+ throw new RuntimeException("Could not determine the size of an element in a "
+ + "native object-based frame of type " + clazz + "! Perhaps it is missing a "
+ + "default constructor?");
+ }
+ } else {
+ return FrameFormat.BYTES_PER_SAMPLE_UNSPECIFIED;
+ }
+ }
+
+ private static Class getBoxedClass(Class type) {
+ // Check if type is primitive
+ if (type.isPrimitive()) {
+ // Yes -> box it
+ if (type == boolean.class) {
+ return java.lang.Boolean.class;
+ } else if (type == byte.class) {
+ return java.lang.Byte.class;
+ } else if (type == char.class) {
+ return java.lang.Character.class;
+ } else if (type == short.class) {
+ return java.lang.Short.class;
+ } else if (type == int.class) {
+ return java.lang.Integer.class;
+ } else if (type == long.class) {
+ return java.lang.Long.class;
+ } else if (type == float.class) {
+ return java.lang.Float.class;
+ } else if (type == double.class) {
+ return java.lang.Double.class;
+ } else {
+ throw new IllegalArgumentException(
+ "Unknown primitive type: " + type.getSimpleName() + "!");
+ }
+ } else {
+ // No -> return it
+ return type;
+ }
+ }
+}
diff --git a/media/mca/filterfw/java/android/filterfw/format/PrimitiveFormat.java b/media/mca/filterfw/java/android/filterfw/format/PrimitiveFormat.java
new file mode 100644
index 0000000..40f07aa
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/format/PrimitiveFormat.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.format;
+
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.MutableFrameFormat;
+
+/**
+ * @hide
+ */
+public class PrimitiveFormat {
+
+ public static MutableFrameFormat createByteFormat(int count, int target) {
+ return createFormat(FrameFormat.TYPE_BYTE, count, target);
+ }
+
+ public static MutableFrameFormat createInt16Format(int count, int target) {
+ return createFormat(FrameFormat.TYPE_INT16, count, target);
+ }
+
+ public static MutableFrameFormat createInt32Format(int count, int target) {
+ return createFormat(FrameFormat.TYPE_INT32, count, target);
+ }
+
+ public static MutableFrameFormat createFloatFormat(int count, int target) {
+ return createFormat(FrameFormat.TYPE_FLOAT, count, target);
+ }
+
+ public static MutableFrameFormat createDoubleFormat(int count, int target) {
+ return createFormat(FrameFormat.TYPE_DOUBLE, count, target);
+ }
+
+ public static MutableFrameFormat createByteFormat(int target) {
+ return createFormat(FrameFormat.TYPE_BYTE, target);
+ }
+
+ public static MutableFrameFormat createInt16Format(int target) {
+ return createFormat(FrameFormat.TYPE_INT16, target);
+ }
+
+ public static MutableFrameFormat createInt32Format(int target) {
+ return createFormat(FrameFormat.TYPE_INT32, target);
+ }
+
+ public static MutableFrameFormat createFloatFormat(int target) {
+ return createFormat(FrameFormat.TYPE_FLOAT, target);
+ }
+
+ public static MutableFrameFormat createDoubleFormat(int target) {
+ return createFormat(FrameFormat.TYPE_DOUBLE, target);
+ }
+
+ private static MutableFrameFormat createFormat(int baseType, int count, int target) {
+ MutableFrameFormat result = new MutableFrameFormat(baseType, target);
+ result.setDimensions(count);
+ return result;
+ }
+
+ private static MutableFrameFormat createFormat(int baseType, int target) {
+ MutableFrameFormat result = new MutableFrameFormat(baseType, target);
+ result.setDimensionCount(1);
+ return result;
+ }
+}
diff --git a/media/mca/filterfw/java/android/filterfw/format/package-info.java b/media/mca/filterfw/java/android/filterfw/format/package-info.java
new file mode 100644
index 0000000..dfd9a3f
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/format/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * @hide
+ */
+package android.filterfw.format;
diff --git a/media/mca/filterfw/java/android/filterfw/geometry/Point.java b/media/mca/filterfw/java/android/filterfw/geometry/Point.java
new file mode 100644
index 0000000..8207c72c
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/geometry/Point.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.geometry;
+
+import java.lang.Math;
+
+/**
+ * @hide
+ */
+public class Point {
+
+ public float x;
+ public float y;
+
+ public Point() {
+ }
+
+ public Point(float x, float y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ public void set(float x, float y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ public boolean IsInUnitRange() {
+ return x >= 0.0f && x <= 1.0f &&
+ y >= 0.0f && y <= 1.0f;
+ }
+
+ public Point plus(float x, float y) {
+ return new Point(this.x + x, this.y + y);
+ }
+
+ public Point plus(Point point) {
+ return this.plus(point.x, point.y);
+ }
+
+ public Point minus(float x, float y) {
+ return new Point(this.x - x, this.y - y);
+ }
+
+ public Point minus(Point point) {
+ return this.minus(point.x, point.y);
+ }
+
+ public Point times(float s) {
+ return new Point(this.x * s, this.y * s);
+ }
+
+ public Point mult(float x, float y) {
+ return new Point(this.x * x, this.y * y);
+ }
+
+ public float length() {
+ return (float)Math.sqrt(x*x + y*y);
+ }
+
+ public float distanceTo(Point p) {
+ return p.minus(this).length();
+ }
+
+ public Point scaledTo(float length) {
+ return this.times(length / this.length());
+ }
+
+ public Point normalize() {
+ return this.scaledTo(1.0f);
+ }
+
+ public Point rotated90(int count) {
+ float nx = this.x;
+ float ny = this.y;
+ for (int i = 0; i < count; ++i) {
+ float ox = nx;
+ nx = ny;
+ ny = -ox;
+ }
+ return new Point(nx, ny);
+ }
+
+ public Point rotated(float radians) {
+ // TODO(renn): Optimize: Keep cache of cos/sin values
+ return new Point((float)(Math.cos(radians) * x - Math.sin(radians) * y),
+ (float)(Math.sin(radians) * x + Math.cos(radians) * y));
+ }
+
+ public Point rotatedAround(Point center, float radians) {
+ return this.minus(center).rotated(radians).plus(center);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + x + ", " + y + ")";
+ }
+}
diff --git a/media/mca/filterfw/java/android/filterfw/geometry/Quad.java b/media/mca/filterfw/java/android/filterfw/geometry/Quad.java
new file mode 100644
index 0000000..ee092fd
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/geometry/Quad.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.geometry;
+
+import android.filterfw.geometry.Point;
+
+import java.lang.Float;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @hide
+ */
+public class Quad {
+
+ public Point p0;
+ public Point p1;
+ public Point p2;
+ public Point p3;
+
+ public Quad() {
+ }
+
+ public Quad(Point p0, Point p1, Point p2, Point p3) {
+ this.p0 = p0;
+ this.p1 = p1;
+ this.p2 = p2;
+ this.p3 = p3;
+ }
+
+ public boolean IsInUnitRange() {
+ return p0.IsInUnitRange() &&
+ p1.IsInUnitRange() &&
+ p2.IsInUnitRange() &&
+ p3.IsInUnitRange();
+ }
+
+ public Quad translated(Point t) {
+ return new Quad(p0.plus(t), p1.plus(t), p2.plus(t), p3.plus(t));
+ }
+
+ public Quad translated(float x, float y) {
+ return new Quad(p0.plus(x, y), p1.plus(x, y), p2.plus(x, y), p3.plus(x, y));
+ }
+
+ public Quad scaled(float s) {
+ return new Quad(p0.times(s), p1.times(s), p2.times(s), p3.times(s));
+ }
+
+ public Quad scaled(float x, float y) {
+ return new Quad(p0.mult(x, y), p1.mult(x, y), p2.mult(x, y), p3.mult(x, y));
+ }
+
+ public Rectangle boundingBox() {
+ List<Float> xs = Arrays.asList(p0.x, p1.x, p2.x, p3.x);
+ List<Float> ys = Arrays.asList(p0.y, p1.y, p2.y, p3.y);
+ float x0 = Collections.min(xs);
+ float y0 = Collections.min(ys);
+ float x1 = Collections.max(xs);
+ float y1 = Collections.max(ys);
+ return new Rectangle(x0, y0, x1 - x0, y1 - y0);
+ }
+
+ public float getBoundingWidth() {
+ List<Float> xs = Arrays.asList(p0.x, p1.x, p2.x, p3.x);
+ return Collections.max(xs) - Collections.min(xs);
+ }
+
+ public float getBoundingHeight() {
+ List<Float> ys = Arrays.asList(p0.y, p1.y, p2.y, p3.y);
+ return Collections.max(ys) - Collections.min(ys);
+ }
+
+ @Override
+ public String toString() {
+ return "{" + p0 + ", " + p1 + ", " + p2 + ", " + p3 + "}";
+ }
+}
diff --git a/media/mca/filterfw/java/android/filterfw/geometry/Rectangle.java b/media/mca/filterfw/java/android/filterfw/geometry/Rectangle.java
new file mode 100644
index 0000000..e4bd622
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/geometry/Rectangle.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.geometry;
+
+import android.filterfw.geometry.Point;
+import android.filterfw.geometry.Quad;
+
+/**
+ * @hide
+ */
+public class Rectangle extends Quad {
+
+ public Rectangle() {
+ }
+
+ public Rectangle(float x, float y, float width, float height) {
+ super(new Point(x, y),
+ new Point(x + width, y),
+ new Point(x, y + height),
+ new Point(x + width, y + height));
+ }
+
+ public Rectangle(Point origin, Point size) {
+ super(origin,
+ origin.plus(size.x, 0.0f),
+ origin.plus(0.0f, size.y),
+ origin.plus(size.x, size.y));
+ }
+
+ public static Rectangle fromRotatedRect(Point center, Point size, float rotation) {
+ Point p0 = new Point(center.x - size.x/2f, center.y - size.y/2f);
+ Point p1 = new Point(center.x + size.x/2f, center.y - size.y/2f);
+ Point p2 = new Point(center.x - size.x/2f, center.y + size.y/2f);
+ Point p3 = new Point(center.x + size.x/2f, center.y + size.y/2f);
+ return new Rectangle(p0.rotatedAround(center, rotation),
+ p1.rotatedAround(center, rotation),
+ p2.rotatedAround(center, rotation),
+ p3.rotatedAround(center, rotation));
+ }
+
+ private Rectangle(Point p0, Point p1, Point p2, Point p3) {
+ super(p0, p1, p2, p3);
+ }
+
+ public static Rectangle fromCenterVerticalAxis(Point center, Point vAxis, Point size) {
+ Point dy = vAxis.scaledTo(size.y / 2.0f);
+ Point dx = vAxis.rotated90(1).scaledTo(size.x / 2.0f);
+ return new Rectangle(center.minus(dx).minus(dy),
+ center.plus(dx).minus(dy),
+ center.minus(dx).plus(dy),
+ center.plus(dx).plus(dy));
+ }
+
+ public float getWidth() {
+ return p1.minus(p0).length();
+ }
+
+ public float getHeight() {
+ return p2.minus(p0).length();
+ }
+
+ public Point center() {
+ return p0.plus(p1).plus(p2).plus(p3).times(0.25f);
+ }
+
+ @Override
+ public Rectangle scaled(float s) {
+ return new Rectangle(p0.times(s), p1.times(s), p2.times(s), p3.times(s));
+ }
+
+ @Override
+ public Rectangle scaled(float x, float y) {
+ return new Rectangle(p0.mult(x, y), p1.mult(x, y), p2.mult(x, y), p3.mult(x, y));
+ }
+
+ //public Rectangle rotated(float radians) {
+ // TODO: Implement this.
+ //}
+
+}
diff --git a/media/mca/filterfw/java/android/filterfw/geometry/package-info.java b/media/mca/filterfw/java/android/filterfw/geometry/package-info.java
new file mode 100644
index 0000000..5547622
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/geometry/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * @hide
+ */
+package android.filterfw.geometry;
diff --git a/media/mca/filterfw/java/android/filterfw/io/GraphIOException.java b/media/mca/filterfw/java/android/filterfw/io/GraphIOException.java
new file mode 100644
index 0000000..940b393
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/io/GraphIOException.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.io;
+
+/**
+ * @hide
+ */
+public class GraphIOException extends Exception {
+
+ public GraphIOException() {
+ super();
+ }
+
+ public GraphIOException(String message) {
+ super(message);
+ }
+
+}
diff --git a/media/mca/filterfw/java/android/filterfw/io/GraphReader.java b/media/mca/filterfw/java/android/filterfw/io/GraphReader.java
new file mode 100644
index 0000000..deb06e2
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/io/GraphReader.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.io;
+
+import android.content.Context;
+import android.filterfw.core.FilterGraph;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.io.GraphIOException;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.StringWriter;
+
+/**
+ * @hide
+ */
+public abstract class GraphReader {
+
+ protected KeyValueMap mReferences = new KeyValueMap();
+
+ public abstract FilterGraph readGraphString(String graphString) throws GraphIOException;
+
+ public abstract KeyValueMap readKeyValueAssignments(String assignments) throws GraphIOException;
+
+ public FilterGraph readGraphResource(Context context, int resourceId) throws GraphIOException {
+ InputStream inputStream = context.getResources().openRawResource(resourceId);
+ InputStreamReader reader = new InputStreamReader(inputStream);
+ StringWriter writer = new StringWriter();
+ char[] buffer = new char[1024];
+ try {
+ int bytesRead;
+ while ((bytesRead = reader.read(buffer, 0, 1024)) > 0) {
+ writer.write(buffer, 0, bytesRead);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Could not read specified resource file!");
+ }
+ return readGraphString(writer.toString());
+ }
+
+ public void addReference(String name, Object object) {
+ mReferences.put(name, object);
+ }
+
+ public void addReferencesByMap(KeyValueMap refs) {
+ mReferences.putAll(refs);
+ }
+
+ public void addReferencesByKeysAndValues(Object... references) {
+ mReferences.setKeyValues(references);
+ }
+
+}
diff --git a/media/mca/filterfw/java/android/filterfw/io/PatternScanner.java b/media/mca/filterfw/java/android/filterfw/io/PatternScanner.java
new file mode 100644
index 0000000..4f1df02
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/io/PatternScanner.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.io;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @hide
+ */
+public class PatternScanner {
+
+ private String mInput;
+ private Pattern mIgnorePattern;
+ private int mOffset = 0;
+ private int mLineNo = 0;
+ private int mStartOfLine = 0;
+
+ public PatternScanner(String input) {
+ mInput = input;
+ }
+
+ public PatternScanner(String input, Pattern ignorePattern) {
+ mInput = input;
+ mIgnorePattern = ignorePattern;
+ skip(mIgnorePattern);
+ }
+
+ public String tryEat(Pattern pattern) {
+ // Skip ignore pattern
+ if (mIgnorePattern != null) {
+ skip(mIgnorePattern);
+ }
+
+ // Create the matcher
+ Matcher matcher = pattern.matcher(mInput);
+ matcher.region(mOffset, mInput.length());
+
+ // Attempt to match
+ String result = null;
+ if (matcher.lookingAt()) {
+ updateLineCount(mOffset, matcher.end());
+ mOffset = matcher.end();
+ result = mInput.substring(matcher.start(), matcher.end());
+ }
+
+ // Skip ignore pattern
+ if (result != null && mIgnorePattern != null) {
+ skip(mIgnorePattern);
+ }
+
+ return result;
+ }
+
+ public String eat(Pattern pattern, String tokenName) {
+ String result = tryEat(pattern);
+ if (result == null) {
+ throw new RuntimeException(unexpectedTokenMessage(tokenName));
+ }
+ return result;
+ }
+
+ public boolean peek(Pattern pattern) {
+ // Skip ignore pattern
+ if (mIgnorePattern != null) {
+ skip(mIgnorePattern);
+ }
+
+ // Create the matcher
+ Matcher matcher = pattern.matcher(mInput);
+ matcher.region(mOffset, mInput.length());
+
+ // Attempt to match
+ return matcher.lookingAt();
+ }
+
+ public void skip(Pattern pattern) {
+ Matcher matcher = pattern.matcher(mInput);
+ matcher.region(mOffset, mInput.length());
+ if (matcher.lookingAt()) {
+ updateLineCount(mOffset, matcher.end());
+ mOffset = matcher.end();
+ }
+ }
+
+ public boolean atEnd() {
+ return mOffset >= mInput.length();
+ }
+
+ public int lineNo() {
+ return mLineNo;
+ }
+
+ public String unexpectedTokenMessage(String tokenName) {
+ String line = mInput.substring(mStartOfLine, mOffset);
+ return "Unexpected token on line " + (mLineNo + 1) + " after '" + line + "' <- Expected " +
+ tokenName + "!";
+ }
+
+ public void updateLineCount(int start, int end) {
+ for (int i = start; i < end; ++i) {
+ if (mInput.charAt(i) == '\n') {
+ ++mLineNo;
+ mStartOfLine = i + 1;
+ }
+ }
+ }
+}
diff --git a/media/mca/filterfw/java/android/filterfw/io/TextGraphReader.java b/media/mca/filterfw/java/android/filterfw/io/TextGraphReader.java
new file mode 100644
index 0000000..366ef82
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/io/TextGraphReader.java
@@ -0,0 +1,489 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterfw.io;
+
+import java.lang.Float;
+import java.lang.Integer;
+import java.lang.String;
+
+import java.util.ArrayList;
+import java.util.regex.Pattern;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterFactory;
+import android.filterfw.core.FilterGraph;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.ProtocolException;
+import android.filterfw.io.GraphReader;
+import android.filterfw.io.GraphIOException;
+import android.filterfw.io.PatternScanner;
+
+/**
+ * @hide
+ */
+public class TextGraphReader extends GraphReader {
+
+ private ArrayList<Command> mCommands = new ArrayList<Command>();
+ private Filter mCurrentFilter;
+ private FilterGraph mCurrentGraph;
+ private KeyValueMap mBoundReferences;
+ private KeyValueMap mSettings;
+ private FilterFactory mFactory;
+
+ private interface Command {
+ public void execute(TextGraphReader reader) throws GraphIOException;
+ }
+
+ private class ImportPackageCommand implements Command {
+ private String mPackageName;
+
+ public ImportPackageCommand(String packageName) {
+ mPackageName = packageName;
+ }
+
+ @Override
+ public void execute(TextGraphReader reader) throws GraphIOException {
+ try {
+ reader.mFactory.addPackage(mPackageName);
+ } catch (IllegalArgumentException e) {
+ throw new GraphIOException(e.getMessage());
+ }
+ }
+ }
+
+ private class AddLibraryCommand implements Command {
+ private String mLibraryName;
+
+ public AddLibraryCommand(String libraryName) {
+ mLibraryName = libraryName;
+ }
+
+ @Override
+ public void execute(TextGraphReader reader) {
+ reader.mFactory.addFilterLibrary(mLibraryName);
+ }
+ }
+
+ private class AllocateFilterCommand implements Command {
+ private String mClassName;
+ private String mFilterName;
+
+ public AllocateFilterCommand(String className, String filterName) {
+ mClassName = className;
+ mFilterName = filterName;
+ }
+
+ public void execute(TextGraphReader reader) throws GraphIOException {
+ // Create the filter
+ Filter filter = null;
+ try {
+ filter = reader.mFactory.createFilterByClassName(mClassName, mFilterName);
+ } catch (IllegalArgumentException e) {
+ throw new GraphIOException(e.getMessage());
+ }
+
+ // Set it as the current filter
+ reader.mCurrentFilter = filter;
+ }
+ }
+
+ private class InitFilterCommand implements Command {
+ private KeyValueMap mParams;
+
+ public InitFilterCommand(KeyValueMap params) {
+ mParams = params;
+ }
+
+ @Override
+ public void execute(TextGraphReader reader) throws GraphIOException {
+ Filter filter = reader.mCurrentFilter;
+ try {
+ filter.initWithValueMap(mParams);
+ } catch (ProtocolException e) {
+ throw new GraphIOException(e.getMessage());
+ }
+ reader.mCurrentGraph.addFilter(mCurrentFilter);
+ }
+ }
+
+ private class ConnectCommand implements Command {
+ private String mSourceFilter;
+ private String mSourcePort;
+ private String mTargetFilter;
+ private String mTargetName;
+
+ public ConnectCommand(String sourceFilter,
+ String sourcePort,
+ String targetFilter,
+ String targetName) {
+ mSourceFilter = sourceFilter;
+ mSourcePort = sourcePort;
+ mTargetFilter = targetFilter;
+ mTargetName = targetName;
+ }
+
+ @Override
+ public void execute(TextGraphReader reader) {
+ reader.mCurrentGraph.connect(mSourceFilter, mSourcePort, mTargetFilter, mTargetName);
+ }
+ }
+
+ @Override
+ public FilterGraph readGraphString(String graphString) throws GraphIOException {
+ FilterGraph result = new FilterGraph();
+
+ reset();
+ mCurrentGraph = result;
+ parseString(graphString);
+ applySettings();
+ executeCommands();
+ reset();
+
+ return result;
+ }
+
+ private void reset() {
+ mCurrentGraph = null;
+ mCurrentFilter = null;
+ mCommands.clear();
+ mBoundReferences = new KeyValueMap();
+ mSettings = new KeyValueMap();
+ mFactory = new FilterFactory();
+ }
+
+ private void parseString(String graphString) throws GraphIOException {
+ final Pattern commandPattern = Pattern.compile("@[a-zA-Z]+");
+ final Pattern curlyClosePattern = Pattern.compile("\\}");
+ final Pattern curlyOpenPattern = Pattern.compile("\\{");
+ final Pattern ignorePattern = Pattern.compile("(\\s+|//[^\\n]*\\n)+");
+ final Pattern packageNamePattern = Pattern.compile("[a-zA-Z\\.]+");
+ final Pattern libraryNamePattern = Pattern.compile("[a-zA-Z\\./:]+");
+ final Pattern portPattern = Pattern.compile("\\[[a-zA-Z0-9\\-_]+\\]");
+ final Pattern rightArrowPattern = Pattern.compile("=>");
+ final Pattern semicolonPattern = Pattern.compile(";");
+ final Pattern wordPattern = Pattern.compile("[a-zA-Z0-9\\-_]+");
+
+ final int STATE_COMMAND = 0;
+ final int STATE_IMPORT_PKG = 1;
+ final int STATE_ADD_LIBRARY = 2;
+ final int STATE_FILTER_CLASS = 3;
+ final int STATE_FILTER_NAME = 4;
+ final int STATE_CURLY_OPEN = 5;
+ final int STATE_PARAMETERS = 6;
+ final int STATE_CURLY_CLOSE = 7;
+ final int STATE_SOURCE_FILTERNAME = 8;
+ final int STATE_SOURCE_PORT = 9;
+ final int STATE_RIGHT_ARROW = 10;
+ final int STATE_TARGET_FILTERNAME = 11;
+ final int STATE_TARGET_PORT = 12;
+ final int STATE_ASSIGNMENT = 13;
+ final int STATE_EXTERNAL = 14;
+ final int STATE_SETTING = 15;
+ final int STATE_SEMICOLON = 16;
+
+ int state = STATE_COMMAND;
+ PatternScanner scanner = new PatternScanner(graphString, ignorePattern);
+
+ String curClassName = null;
+ String curSourceFilterName = null;
+ String curSourcePortName = null;
+ String curTargetFilterName = null;
+ String curTargetPortName = null;
+
+ // State machine main loop
+ while (!scanner.atEnd()) {
+ switch (state) {
+ case STATE_COMMAND: {
+ String curCommand = scanner.eat(commandPattern, "<command>");
+ if (curCommand.equals("@import")) {
+ state = STATE_IMPORT_PKG;
+ } else if (curCommand.equals("@library")) {
+ state = STATE_ADD_LIBRARY;
+ } else if (curCommand.equals("@filter")) {
+ state = STATE_FILTER_CLASS;
+ } else if (curCommand.equals("@connect")) {
+ state = STATE_SOURCE_FILTERNAME;
+ } else if (curCommand.equals("@set")) {
+ state = STATE_ASSIGNMENT;
+ } else if (curCommand.equals("@external")) {
+ state = STATE_EXTERNAL;
+ } else if (curCommand.equals("@setting")) {
+ state = STATE_SETTING;
+ } else {
+ throw new GraphIOException("Unknown command '" + curCommand + "'!");
+ }
+ break;
+ }
+
+ case STATE_IMPORT_PKG: {
+ String packageName = scanner.eat(packageNamePattern, "<package-name>");
+ mCommands.add(new ImportPackageCommand(packageName));
+ state = STATE_SEMICOLON;
+ break;
+ }
+
+ case STATE_ADD_LIBRARY: {
+ String libraryName = scanner.eat(libraryNamePattern, "<library-name>");
+ mCommands.add(new AddLibraryCommand(libraryName));
+ state = STATE_SEMICOLON;
+ break;
+ }
+
+ case STATE_FILTER_CLASS:
+ curClassName = scanner.eat(wordPattern, "<class-name>");
+ state = STATE_FILTER_NAME;
+ break;
+
+ case STATE_FILTER_NAME: {
+ String curFilterName = scanner.eat(wordPattern, "<filter-name>");
+ mCommands.add(new AllocateFilterCommand(curClassName, curFilterName));
+ state = STATE_CURLY_OPEN;
+ break;
+ }
+
+ case STATE_CURLY_OPEN:
+ scanner.eat(curlyOpenPattern, "{");
+ state = STATE_PARAMETERS;
+ break;
+
+ case STATE_PARAMETERS: {
+ KeyValueMap params = readKeyValueAssignments(scanner, curlyClosePattern);
+ mCommands.add(new InitFilterCommand(params));
+ state = STATE_CURLY_CLOSE;
+ break;
+ }
+
+ case STATE_CURLY_CLOSE:
+ scanner.eat(curlyClosePattern, "}");
+ state = STATE_COMMAND;
+ break;
+
+ case STATE_SOURCE_FILTERNAME:
+ curSourceFilterName = scanner.eat(wordPattern, "<source-filter-name>");
+ state = STATE_SOURCE_PORT;
+ break;
+
+ case STATE_SOURCE_PORT: {
+ String portString = scanner.eat(portPattern, "[<source-port-name>]");
+ curSourcePortName = portString.substring(1, portString.length() - 1);
+ state = STATE_RIGHT_ARROW;
+ break;
+ }
+
+ case STATE_RIGHT_ARROW:
+ scanner.eat(rightArrowPattern, "=>");
+ state = STATE_TARGET_FILTERNAME;
+ break;
+
+ case STATE_TARGET_FILTERNAME:
+ curTargetFilterName = scanner.eat(wordPattern, "<target-filter-name>");
+ state = STATE_TARGET_PORT;
+ break;
+
+ case STATE_TARGET_PORT: {
+ String portString = scanner.eat(portPattern, "[<target-port-name>]");
+ curTargetPortName = portString.substring(1, portString.length() - 1);
+ mCommands.add(new ConnectCommand(curSourceFilterName,
+ curSourcePortName,
+ curTargetFilterName,
+ curTargetPortName));
+ state = STATE_SEMICOLON;
+ break;
+ }
+
+ case STATE_ASSIGNMENT: {
+ KeyValueMap assignment = readKeyValueAssignments(scanner, semicolonPattern);
+ mBoundReferences.putAll(assignment);
+ state = STATE_SEMICOLON;
+ break;
+ }
+
+ case STATE_EXTERNAL: {
+ String externalName = scanner.eat(wordPattern, "<external-identifier>");
+ bindExternal(externalName);
+ state = STATE_SEMICOLON;
+ break;
+ }
+
+ case STATE_SETTING: {
+ KeyValueMap setting = readKeyValueAssignments(scanner, semicolonPattern);
+ mSettings.putAll(setting);
+ state = STATE_SEMICOLON;
+ break;
+ }
+
+ case STATE_SEMICOLON:
+ scanner.eat(semicolonPattern, ";");
+ state = STATE_COMMAND;
+ break;
+ }
+ }
+
+ // Make sure end of input was expected
+ if (state != STATE_SEMICOLON && state != STATE_COMMAND) {
+ throw new GraphIOException("Unexpected end of input!");
+ }
+ }
+
+ @Override
+ public KeyValueMap readKeyValueAssignments(String assignments) throws GraphIOException {
+ final Pattern ignorePattern = Pattern.compile("\\s+");
+ PatternScanner scanner = new PatternScanner(assignments, ignorePattern);
+ return readKeyValueAssignments(scanner, null);
+ }
+
+ private KeyValueMap readKeyValueAssignments(PatternScanner scanner,
+ Pattern endPattern) throws GraphIOException {
+ // Our parser is a state-machine, and these are our states
+ final int STATE_IDENTIFIER = 0;
+ final int STATE_EQUALS = 1;
+ final int STATE_VALUE = 2;
+ final int STATE_POST_VALUE = 3;
+
+ final Pattern equalsPattern = Pattern.compile("=");
+ final Pattern semicolonPattern = Pattern.compile(";");
+ final Pattern wordPattern = Pattern.compile("[a-zA-Z]+[a-zA-Z0-9]*");
+ final Pattern stringPattern = Pattern.compile("'[^']*'|\\\"[^\\\"]*\\\"");
+ final Pattern intPattern = Pattern.compile("[0-9]+");
+ final Pattern floatPattern = Pattern.compile("[0-9]*\\.[0-9]+f?");
+ final Pattern referencePattern = Pattern.compile("\\$[a-zA-Z]+[a-zA-Z0-9]");
+ final Pattern booleanPattern = Pattern.compile("true|false");
+
+ int state = STATE_IDENTIFIER;
+ KeyValueMap newVals = new KeyValueMap();
+ String curKey = null;
+ String curValue = null;
+
+ while (!scanner.atEnd() && !(endPattern != null && scanner.peek(endPattern))) {
+ switch (state) {
+ case STATE_IDENTIFIER:
+ curKey = scanner.eat(wordPattern, "<identifier>");
+ state = STATE_EQUALS;
+ break;
+
+ case STATE_EQUALS:
+ scanner.eat(equalsPattern, "=");
+ state = STATE_VALUE;
+ break;
+
+ case STATE_VALUE:
+ if ((curValue = scanner.tryEat(stringPattern)) != null) {
+ newVals.put(curKey, curValue.substring(1, curValue.length() - 1));
+ } else if ((curValue = scanner.tryEat(referencePattern)) != null) {
+ String refName = curValue.substring(1, curValue.length());
+ Object referencedObject = mBoundReferences != null
+ ? mBoundReferences.get(refName)
+ : null;
+ if (referencedObject == null) {
+ throw new GraphIOException(
+ "Unknown object reference to '" + refName + "'!");
+ }
+ newVals.put(curKey, referencedObject);
+ } else if ((curValue = scanner.tryEat(booleanPattern)) != null) {
+ newVals.put(curKey, Boolean.parseBoolean(curValue));
+ } else if ((curValue = scanner.tryEat(floatPattern)) != null) {
+ newVals.put(curKey, Float.parseFloat(curValue));
+ } else if ((curValue = scanner.tryEat(intPattern)) != null) {
+ newVals.put(curKey, Integer.parseInt(curValue));
+ } else {
+ throw new GraphIOException(scanner.unexpectedTokenMessage("<value>"));
+ }
+ state = STATE_POST_VALUE;
+ break;
+
+ case STATE_POST_VALUE:
+ scanner.eat(semicolonPattern, ";");
+ state = STATE_IDENTIFIER;
+ break;
+ }
+ }
+
+ // Make sure end is expected
+ if (state != STATE_IDENTIFIER && state != STATE_POST_VALUE) {
+ throw new GraphIOException(
+ "Unexpected end of assignments on line " + scanner.lineNo() + "!");
+ }
+
+ return newVals;
+ }
+
+ private void bindExternal(String name) throws GraphIOException {
+ if (mReferences.containsKey(name)) {
+ Object value = mReferences.get(name);
+ mBoundReferences.put(name, value);
+ } else {
+ throw new GraphIOException("Unknown external variable '" + name + "'! "
+ + "You must add a reference to this external in the host program using "
+ + "addReference(...)!");
+ }
+ }
+
+ /**
+ * Unused for now: Often you may want to declare references that are NOT in a certain graph,
+ * e.g. when reading multiple graphs with the same reader. We could print a warning, but even
+ * that may be too much.
+ **/
+ private void checkReferences() throws GraphIOException {
+ for (String reference : mReferences.keySet()) {
+ if (!mBoundReferences.containsKey(reference)) {
+ throw new GraphIOException(
+ "Host program specifies reference to '" + reference + "', which is not "
+ + "declared @external in graph file!");
+ }
+ }
+ }
+
+ private void applySettings() throws GraphIOException {
+ for (String setting : mSettings.keySet()) {
+ Object value = mSettings.get(setting);
+ if (setting.equals("autoBranch")) {
+ expectSettingClass(setting, value, String.class);
+ if (value.equals("synced")) {
+ mCurrentGraph.setAutoBranchMode(FilterGraph.AUTOBRANCH_SYNCED);
+ } else if (value.equals("unsynced")) {
+ mCurrentGraph.setAutoBranchMode(FilterGraph.AUTOBRANCH_UNSYNCED);
+ } else if (value.equals("off")) {
+ mCurrentGraph.setAutoBranchMode(FilterGraph.AUTOBRANCH_OFF);
+ } else {
+ throw new GraphIOException("Unknown autobranch setting: " + value + "!");
+ }
+ } else if (setting.equals("discardUnconnectedOutputs")) {
+ expectSettingClass(setting, value, Boolean.class);
+ mCurrentGraph.setDiscardUnconnectedOutputs((Boolean)value);
+ } else {
+ throw new GraphIOException("Unknown @setting '" + setting + "'!");
+ }
+ }
+ }
+
+ private void expectSettingClass(String setting,
+ Object value,
+ Class expectedClass) throws GraphIOException {
+ if (value.getClass() != expectedClass) {
+ throw new GraphIOException("Setting '" + setting + "' must have a value of type "
+ + expectedClass.getSimpleName() + ", but found a value of type "
+ + value.getClass().getSimpleName() + "!");
+ }
+ }
+
+ private void executeCommands() throws GraphIOException {
+ for (Command command : mCommands) {
+ command.execute(this);
+ }
+ }
+}
diff --git a/media/mca/filterfw/java/android/filterfw/io/package-info.java b/media/mca/filterfw/java/android/filterfw/io/package-info.java
new file mode 100644
index 0000000..ea3e70f
--- /dev/null
+++ b/media/mca/filterfw/java/android/filterfw/io/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * @hide
+ */
+package android.filterfw.io;
diff --git a/media/mca/filterfw/jni/Android.mk b/media/mca/filterfw/jni/Android.mk
new file mode 100644
index 0000000..5aa5af1
--- /dev/null
+++ b/media/mca/filterfw/jni/Android.mk
@@ -0,0 +1,51 @@
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+#####################
+# Build module libfilterfw_jni
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE = libfilterfw_jni
+
+LOCAL_SRC_FILES = jni_init.cpp \
+ jni_gl_environment.cpp \
+ jni_gl_frame.cpp \
+ jni_native_buffer.cpp \
+ jni_native_frame.cpp \
+ jni_native_program.cpp \
+ jni_shader_program.cpp \
+ jni_util.cpp \
+ jni_vertex_frame.cpp
+
+# Need FilterFW lib
+include $(LOCAL_PATH)/../native/libfilterfw.mk
+
+# Also need the JNI headers.
+LOCAL_C_INCLUDES += \
+ $(JNI_H_INCLUDE) \
+ $(LOCAL_PATH)/..
+
+# Don't prelink this library. For more efficient code, you may want
+# to add this library to the prelink map and set this to true. However,
+# it's difficult to do this for applications that are not supplied as
+# part of a system image.
+LOCAL_PRELINK_MODULE := false
+
+include $(BUILD_STATIC_LIBRARY)
+
diff --git a/media/mca/filterfw/jni/jni_gl_environment.cpp b/media/mca/filterfw/jni/jni_gl_environment.cpp
new file mode 100644
index 0000000..3c596a4
--- /dev/null
+++ b/media/mca/filterfw/jni/jni_gl_environment.cpp
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// #define LOG_NDEBUG 0
+
+#include <stdint.h>
+#include <android/native_window_jni.h>
+
+#include "jni/jni_gl_environment.h"
+#include "jni/jni_util.h"
+#include <media/mediarecorder.h>
+#include "native/core/gl_env.h"
+
+#include <gui/ISurfaceTexture.h>
+#include <gui/SurfaceTextureClient.h>
+#include <utils/Errors.h>
+#include <system/window.h>
+
+
+using android::filterfw::GLEnv;
+using android::filterfw::WindowHandle;
+using android::MediaRecorder;
+using android::sp;
+using android::ISurfaceTexture;
+using android::SurfaceTextureClient;
+
+
+class NativeWindowHandle : public WindowHandle {
+ public:
+ NativeWindowHandle(ANativeWindow* window) : window_(window) {
+ }
+
+ virtual ~NativeWindowHandle() {
+ }
+
+ virtual void Destroy() {
+ ALOGI("Releasing ANativeWindow!");
+ ANativeWindow_release(window_);
+ }
+
+ virtual const void* InternalHandle() const {
+ return window_;
+ }
+
+ virtual void* InternalHandle() {
+ return window_;
+ }
+
+ private:
+ ANativeWindow* window_;
+};
+
+jboolean Java_android_filterfw_core_GLEnvironment_nativeAllocate(JNIEnv* env, jobject thiz) {
+ return ToJBool(WrapObjectInJava(new GLEnv(), env, thiz, true));
+}
+
+jboolean Java_android_filterfw_core_GLEnvironment_nativeDeallocate(JNIEnv* env, jobject thiz) {
+ return ToJBool(DeleteNativeObject<GLEnv>(env, thiz));
+}
+
+jboolean Java_android_filterfw_core_GLEnvironment_nativeInitWithNewContext(JNIEnv* env,
+ jobject thiz) {
+ GLEnv* gl_env = ConvertFromJava<GLEnv>(env, thiz);
+ return gl_env ? ToJBool(gl_env->InitWithNewContext()) : JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_GLEnvironment_nativeInitWithCurrentContext(JNIEnv* env,
+ jobject thiz) {
+ GLEnv* gl_env = ConvertFromJava<GLEnv>(env, thiz);
+ return gl_env ? ToJBool(gl_env->InitWithCurrentContext()) : JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_GLEnvironment_nativeIsActive(JNIEnv* env, jobject thiz) {
+ GLEnv* gl_env = ConvertFromJava<GLEnv>(env, thiz);
+ return gl_env ? ToJBool(gl_env->IsActive()) : JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_GLEnvironment_nativeIsContextActive(JNIEnv* env, jobject thiz) {
+ GLEnv* gl_env = ConvertFromJava<GLEnv>(env, thiz);
+ return gl_env ? ToJBool(gl_env->IsContextActive()) : JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_GLEnvironment_nativeIsAnyContextActive(JNIEnv* env,
+ jclass clazz) {
+ return ToJBool(GLEnv::IsAnyContextActive());
+}
+
+jboolean Java_android_filterfw_core_GLEnvironment_nativeActivate(JNIEnv* env, jobject thiz) {
+ GLEnv* gl_env = ConvertFromJava<GLEnv>(env, thiz);
+ return gl_env ? ToJBool(gl_env->Activate()) : JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_GLEnvironment_nativeDeactivate(JNIEnv* env, jobject thiz) {
+ GLEnv* gl_env = ConvertFromJava<GLEnv>(env, thiz);
+ return gl_env ? ToJBool(gl_env->Deactivate()) : JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_GLEnvironment_nativeSwapBuffers(JNIEnv* env, jobject thiz) {
+ GLEnv* gl_env = ConvertFromJava<GLEnv>(env, thiz);
+ return gl_env ? ToJBool(gl_env->SwapBuffers()) : JNI_FALSE;
+}
+
+// Get the native mediarecorder object corresponding to the java object
+static sp<MediaRecorder> getMediaRecorder(JNIEnv* env, jobject jmediarecorder) {
+ jclass clazz = env->FindClass("android/media/MediaRecorder");
+ if (clazz == NULL) {
+ return NULL;
+ }
+
+ jfieldID context = env->GetFieldID(clazz, "mNativeContext", "I");
+ if (context == NULL) {
+ return NULL;
+ }
+
+ MediaRecorder* const p = (MediaRecorder*)env->GetIntField(jmediarecorder, context);
+ env->DeleteLocalRef(clazz);
+ return sp<MediaRecorder>(p);
+}
+
+
+jint Java_android_filterfw_core_GLEnvironment_nativeAddSurface(JNIEnv* env,
+ jobject thiz,
+ jobject surface) {
+ GLEnv* gl_env = ConvertFromJava<GLEnv>(env, thiz);
+ if (!surface) {
+ ALOGE("GLEnvironment: Null Surface passed!");
+ return -1;
+ } else if (gl_env) {
+ // Get the ANativeWindow
+ ANativeWindow* window = ANativeWindow_fromSurface(env, surface);
+ if (!window) {
+ ALOGE("GLEnvironment: Error creating window!");
+ return -1;
+ }
+
+ NativeWindowHandle* winHandle = new NativeWindowHandle(window);
+ int result = gl_env->FindSurfaceIdForWindow(winHandle);
+ if (result == -1) {
+ // Configure surface
+ EGLConfig config;
+ EGLint numConfigs = -1;
+ EGLint configAttribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_RECORDABLE_ANDROID, EGL_TRUE,
+ EGL_NONE
+ };
+
+
+
+ eglChooseConfig(gl_env->display(), configAttribs, &config, 1, &numConfigs);
+ if (numConfigs < 1) {
+ ALOGE("GLEnvironment: No suitable EGL configuration found for surface!");
+ return -1;
+ }
+
+ // Create the EGL surface
+ EGLSurface egl_surface = eglCreateWindowSurface(gl_env->display(),
+ config,
+ window,
+ NULL);
+
+ if (GLEnv::CheckEGLError("eglCreateWindowSurface")) {
+ ALOGE("GLEnvironment: Error creating window surface!");
+ return -1;
+ }
+
+ // Add it to GL Env and assign ID
+ result = gl_env->AddWindowSurface(egl_surface, winHandle);
+ } else {
+ delete winHandle;
+ }
+ return result;
+ }
+ return -1;
+}
+
+jint Java_android_filterfw_core_GLEnvironment_nativeAddSurfaceWidthHeight(JNIEnv* env,
+ jobject thiz,
+ jobject surface,
+ jint width,
+ jint height) {
+ GLEnv* gl_env = ConvertFromJava<GLEnv>(env, thiz);
+ if (!surface) {
+ ALOGE("GLEnvironment: Null SurfaceTexture passed!");
+ return -1;
+ } else if (gl_env) {
+ // Get the ANativeWindow
+ ANativeWindow* window = ANativeWindow_fromSurface(env, surface);
+ if (!window) {
+ ALOGE("GLEnvironment: Error creating window!");
+ return -1;
+ }
+
+ // Don't care about format (will get overridden by SurfaceTexture
+ // anyway), but do care about width and height
+ // TODO: Probably, this should be just be
+ // ANativeWindow_setBuffersDimensions. The pixel format is
+ // set during the eglCreateWindowSurface
+ ANativeWindow_setBuffersGeometry(window, width, height, 0);
+
+ NativeWindowHandle* winHandle = new NativeWindowHandle(window);
+ int result = gl_env->FindSurfaceIdForWindow(winHandle);
+ if (result == -1) {
+ // Configure surface
+ EGLConfig config;
+ EGLint numConfigs = -1;
+ EGLint configAttribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_RECORDABLE_ANDROID, EGL_TRUE,
+ EGL_NONE
+ };
+
+
+
+ eglChooseConfig(gl_env->display(), configAttribs, &config, 1, &numConfigs);
+ if (numConfigs < 1) {
+ ALOGE("GLEnvironment: No suitable EGL configuration found for surface texture!");
+ return -1;
+ }
+
+ // Create the EGL surface
+ EGLSurface egl_surface = eglCreateWindowSurface(gl_env->display(),
+ config,
+ window,
+ NULL);
+
+ if (GLEnv::CheckEGLError("eglCreateWindowSurface")) {
+ ALOGE("GLEnvironment: Error creating window surface!");
+ return -1;
+ }
+
+ // Add it to GL Env and assign ID
+ result = gl_env->AddWindowSurface(egl_surface, winHandle);
+ } else {
+ delete winHandle;
+ }
+ return result;
+ }
+ return -1;
+}
+
+// nativeAddSurfaceFromMediaRecorder gets an EGLSurface
+// using a MediaRecorder object.
+// When Mediarecorder is used for recording GL Frames, it
+// will have a reference to a Native Handle (a SurfaceTexureClient)
+// which talks to the StageFrightRecorder in mediaserver via
+// a binder interface.
+jint Java_android_filterfw_core_GLEnvironment_nativeAddSurfaceFromMediaRecorder(
+ JNIEnv* env,
+ jobject thiz,
+ jobject jmediarecorder) {
+ ALOGV("GLEnv Jni: nativeAddSurfaceFromMediaRecorder");
+ GLEnv* gl_env = ConvertFromJava<GLEnv>(env, thiz);
+ if (!gl_env) {
+ return -1;
+ }
+ // get a native mediarecorder object from the java object
+ sp<MediaRecorder> mr = getMediaRecorder(env, jmediarecorder);
+ if (mr == NULL) {
+ ALOGE("GLEnvironment: Error- MediaRecorder could not be initialized!");
+ return -1;
+ }
+
+ // Ask the mediarecorder to return a handle to a surfacemediasource
+ // This will talk to the StageFrightRecorder via MediaRecorderClient
+ // over binder calls
+ sp<ISurfaceTexture> surfaceMS = mr->querySurfaceMediaSourceFromMediaServer();
+ if (surfaceMS == NULL) {
+ ALOGE("GLEnvironment: Error- MediaRecorder returned a null \
+ <ISurfaceTexture> handle.");
+ return -1;
+ }
+ sp<SurfaceTextureClient> surfaceTC = new SurfaceTextureClient(surfaceMS);
+ // Get the ANativeWindow
+ sp<ANativeWindow> window = surfaceTC;
+
+
+ if (window == NULL) {
+ ALOGE("GLEnvironment: Error creating window!");
+ return -1;
+ }
+ window->incStrong((void*)ANativeWindow_acquire);
+
+ // In case of encoding, no need to set the dimensions
+ // on the buffers. The dimensions for the final encoding are set by
+ // the consumer side.
+ // The pixel format is dictated by the GL, and set during the
+ // eglCreateWindowSurface
+
+ NativeWindowHandle* winHandle = new NativeWindowHandle(window.get());
+ int result = gl_env->FindSurfaceIdForWindow(winHandle);
+ // If we find a surface with that window handle, just return that id
+ if (result != -1) {
+ delete winHandle;
+ return result;
+ }
+ // If we do not find a surface with that window handle, create
+ // one and assign to it the handle
+ // Configure surface
+ EGLConfig config;
+ EGLint numConfigs = -1;
+ EGLint configAttribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_RECORDABLE_ANDROID, EGL_TRUE,
+ EGL_NONE
+ };
+
+
+ eglChooseConfig(gl_env->display(), configAttribs, &config, 1, &numConfigs);
+ if (numConfigs < 1) {
+ ALOGE("GLEnvironment: No suitable EGL configuration found for surface texture!");
+ delete winHandle;
+ return -1;
+ }
+
+ // Create the EGL surface
+ EGLSurface egl_surface = eglCreateWindowSurface(gl_env->display(),
+ config,
+ window.get(),
+ NULL);
+
+ if (GLEnv::CheckEGLError("eglCreateWindowSurface")) {
+ ALOGE("GLEnvironment: Error creating window surface!");
+ delete winHandle;
+ return -1;
+ }
+
+ // Add it to GL Env and assign ID
+ result = gl_env->AddWindowSurface(egl_surface, winHandle);
+ return result;
+}
+
+jboolean Java_android_filterfw_core_GLEnvironment_nativeActivateSurfaceId(JNIEnv* env,
+ jobject thiz,
+ jint surfaceId) {
+ GLEnv* gl_env = ConvertFromJava<GLEnv>(env, thiz);
+ return gl_env ? ToJBool(gl_env->SwitchToSurfaceId(surfaceId) && gl_env->Activate()) : JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_GLEnvironment_nativeRemoveSurfaceId(JNIEnv* env,
+ jobject thiz,
+ jint surfaceId) {
+ GLEnv* gl_env = ConvertFromJava<GLEnv>(env, thiz);
+ return gl_env ? ToJBool(gl_env->ReleaseSurfaceId(surfaceId)) : JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_GLEnvironment_nativeSetSurfaceTimestamp(JNIEnv* env,
+ jobject thiz,
+ jlong timestamp) {
+ GLEnv* gl_env = ConvertFromJava<GLEnv>(env, thiz);
+ int64_t timestamp_native = timestamp;
+ return gl_env ? ToJBool(gl_env->SetSurfaceTimestamp(timestamp_native)) : JNI_FALSE;
+}
diff --git a/media/mca/filterfw/jni/jni_gl_environment.h b/media/mca/filterfw/jni/jni_gl_environment.h
new file mode 100644
index 0000000..af9c744
--- /dev/null
+++ b/media/mca/filterfw/jni/jni_gl_environment.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FILTERFW_JNI_GL_ENVIRONMENT_H
+#define ANDROID_FILTERFW_JNI_GL_ENVIRONMENT_H
+
+#include <jni.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLEnvironment_nativeAllocate(JNIEnv* env, jobject thiz);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLEnvironment_nativeDeallocate(JNIEnv* env, jobject thiz);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLEnvironment_nativeInitWithNewContext(JNIEnv* env, jobject thiz);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLEnvironment_nativeInitWithCurrentContext(JNIEnv* env, jobject thiz);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLEnvironment_nativeIsActive(JNIEnv* env, jobject thiz);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLEnvironment_nativeIsContextActive(JNIEnv* env, jobject thiz);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLEnvironment_nativeIsAnyContextActive(JNIEnv* env, jclass clazz);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLEnvironment_nativeActivate(JNIEnv* env, jobject thiz);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLEnvironment_nativeDeactivate(JNIEnv* env, jobject thiz);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLEnvironment_nativeSwapBuffers(JNIEnv* env, jobject thiz);
+
+JNIEXPORT jint JNICALL
+Java_android_filterfw_core_GLEnvironment_nativeAddSurface(JNIEnv* env,
+ jobject thiz,
+ jobject surface);
+
+JNIEXPORT jint JNICALL
+Java_android_filterfw_core_GLEnvironment_nativeAddSurfaceWidthHeight(JNIEnv* env,
+ jobject thiz,
+ jobject surface,
+ jint width,
+ jint height);
+
+// The call to hook up the SurfaceMediaSource (in MediaServer) to the GL.
+// We get a sp<ISurfaceTexure> from the MediaServer and talks to MediaServer
+// over a binder interface. GL hooked up to the MediaServer by using the native
+// window created using the <ISurfaceTexture> handle
+JNIEXPORT jint JNICALL
+Java_android_filterfw_core_GLEnvironment_nativeAddSurfaceFromMediaRecorder(
+ JNIEnv* env,
+ jobject thiz,
+ jobject mediarecorder);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLEnvironment_nativeActivateSurfaceId(JNIEnv* env,
+ jobject thiz,
+ jint surfaceId);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLEnvironment_nativeRemoveSurfaceId(JNIEnv* env,
+ jobject thiz,
+ jint surfaceId);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLEnvironment_nativeSetSurfaceTimestamp(JNIEnv* env,
+ jobject thiz,
+ jlong timestamp);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ANDROID_FILTERFW_JNI_GL_ENVIRONMENT_H
diff --git a/media/mca/filterfw/jni/jni_gl_frame.cpp b/media/mca/filterfw/jni/jni_gl_frame.cpp
new file mode 100644
index 0000000..61340f9
--- /dev/null
+++ b/media/mca/filterfw/jni/jni_gl_frame.cpp
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android/bitmap.h"
+
+#include "jni/jni_gl_frame.h"
+#include "jni/jni_util.h"
+
+#include "native/core/gl_env.h"
+#include "native/core/gl_frame.h"
+#include "native/core/native_frame.h"
+
+using android::filterfw::GLEnv;
+using android::filterfw::GLFrame;
+using android::filterfw::NativeFrame;
+
+// Helper functions ////////////////////////////////////////////////////////////////////////////////
+void ConvertFloatsToRGBA(const float* floats, int length, uint8_t* result) {
+ for (int i = 0; i < length; ++i) {
+ result[i] = static_cast<uint8_t>(floats[i] * 255.0);
+ }
+}
+
+void ConvertRGBAToFloats(const uint8_t* rgba, int length, float* result) {
+ for (int i = 0; i < length; ++i) {
+ result[i] = rgba[i] / 255.0;
+ }
+}
+
+// GLFrame JNI implementation //////////////////////////////////////////////////////////////////////
+jboolean Java_android_filterfw_core_GLFrame_nativeAllocate(JNIEnv* env,
+ jobject thiz,
+ jobject gl_env,
+ jint width,
+ jint height) {
+ GLEnv* gl_env_ptr = ConvertFromJava<GLEnv>(env, gl_env);
+ if (!gl_env_ptr) return JNI_FALSE;
+ GLFrame* frame = new GLFrame(gl_env_ptr);
+ if (frame->Init(width, height)) {
+ return ToJBool(WrapObjectInJava(frame, env, thiz, true));
+ } else {
+ delete frame;
+ return JNI_FALSE;
+ }
+}
+
+jboolean Java_android_filterfw_core_GLFrame_nativeAllocateWithTexture(JNIEnv* env,
+ jobject thiz,
+ jobject gl_env,
+ jint tex_id,
+ jint width,
+ jint height) {
+ GLEnv* gl_env_ptr = ConvertFromJava<GLEnv>(env, gl_env);
+ if (!gl_env_ptr) return JNI_FALSE;
+ GLFrame* frame = new GLFrame(gl_env_ptr);
+ if (frame->InitWithTexture(tex_id, width, height)) {
+ return ToJBool(WrapObjectInJava(frame, env, thiz, true));
+ } else {
+ delete frame;
+ return JNI_FALSE;
+ }
+}
+
+jboolean Java_android_filterfw_core_GLFrame_nativeAllocateWithFbo(JNIEnv* env,
+ jobject thiz,
+ jobject gl_env,
+ jint fbo_id,
+ jint width,
+ jint height) {
+ GLEnv* gl_env_ptr = ConvertFromJava<GLEnv>(env, gl_env);
+ if (!gl_env_ptr) return JNI_FALSE;
+ GLFrame* frame = new GLFrame(gl_env_ptr);
+ if (frame->InitWithFbo(fbo_id, width, height)) {
+ return ToJBool(WrapObjectInJava(frame, env, thiz, true));
+ } else {
+ delete frame;
+ return JNI_FALSE;
+ }
+}
+
+jboolean Java_android_filterfw_core_GLFrame_nativeAllocateExternal(JNIEnv* env,
+ jobject thiz,
+ jobject gl_env) {
+ GLEnv* gl_env_ptr = ConvertFromJava<GLEnv>(env, gl_env);
+ if (!gl_env_ptr) return JNI_FALSE;
+ GLFrame* frame = new GLFrame(gl_env_ptr);
+ if (frame->InitWithExternalTexture()) {
+ return ToJBool(WrapObjectInJava(frame, env, thiz, true));
+ } else {
+ delete frame;
+ return JNI_FALSE;
+ }
+}
+
+jboolean Java_android_filterfw_core_GLFrame_nativeDeallocate(JNIEnv* env, jobject thiz) {
+ return ToJBool(DeleteNativeObject<GLFrame>(env, thiz));
+}
+
+jboolean Java_android_filterfw_core_GLFrame_setNativeData(JNIEnv* env,
+ jobject thiz,
+ jbyteArray data,
+ jint offset,
+ jint length) {
+ GLFrame* frame = ConvertFromJava<GLFrame>(env, thiz);
+ if (frame && data) {
+ jbyte* bytes = env->GetByteArrayElements(data, NULL);
+ if (bytes) {
+ const bool success = frame->WriteData(reinterpret_cast<const uint8_t*>(bytes + offset), length);
+ env->ReleaseByteArrayElements(data, bytes, JNI_ABORT);
+ return ToJBool(success);
+ }
+ }
+ return JNI_FALSE;
+}
+
+jbyteArray Java_android_filterfw_core_GLFrame_getNativeData(JNIEnv* env, jobject thiz) {
+ GLFrame* frame = ConvertFromJava<GLFrame>(env, thiz);
+ if (frame && frame->Size() > 0) {
+ jbyteArray result = env->NewByteArray(frame->Size());
+ jbyte* data = env->GetByteArrayElements(result, NULL);
+ frame->CopyDataTo(reinterpret_cast<uint8_t*>(data), frame->Size());
+ env->ReleaseByteArrayElements(result, data, 0);
+ return result;
+ }
+ return NULL;
+}
+
+jboolean Java_android_filterfw_core_GLFrame_setNativeInts(JNIEnv* env,
+ jobject thiz,
+ jintArray ints) {
+ GLFrame* frame = ConvertFromJava<GLFrame>(env, thiz);
+ if (frame && ints) {
+ jint* int_ptr = env->GetIntArrayElements(ints, NULL);
+ const int length = env->GetArrayLength(ints);
+ if (int_ptr) {
+ const bool success = frame->WriteData(reinterpret_cast<const uint8_t*>(int_ptr),
+ length * sizeof(jint));
+ env->ReleaseIntArrayElements(ints, int_ptr, JNI_ABORT);
+ return ToJBool(success);
+ }
+ }
+ return JNI_FALSE;
+}
+
+jintArray Java_android_filterfw_core_GLFrame_getNativeInts(JNIEnv* env, jobject thiz) {
+ GLFrame* frame = ConvertFromJava<GLFrame>(env, thiz);
+ if (frame && frame->Size() > 0 && (frame->Size() % sizeof(jint) == 0)) {
+ jintArray result = env->NewIntArray(frame->Size() / sizeof(jint));
+ jint* data = env->GetIntArrayElements(result, NULL);
+ frame->CopyDataTo(reinterpret_cast<uint8_t*>(data), frame->Size());
+ env->ReleaseIntArrayElements(result, data, 0);
+ return result;
+ }
+ return NULL;
+}
+
+jboolean Java_android_filterfw_core_GLFrame_setNativeFloats(JNIEnv* env,
+ jobject thiz,
+ jfloatArray floats) {
+ GLFrame* frame = ConvertFromJava<GLFrame>(env, thiz);
+ if (frame && floats) {
+ jfloat* float_ptr = env->GetFloatArrayElements(floats, NULL);
+ const int length = env->GetArrayLength(floats);
+ if (float_ptr) {
+ // Convert floats to RGBA buffer
+ uint8_t* rgba_buffer = new uint8_t[length];
+ ConvertFloatsToRGBA(float_ptr, length, rgba_buffer);
+ env->ReleaseFloatArrayElements(floats, float_ptr, JNI_ABORT);
+
+ // Write RGBA buffer to frame
+ const bool success = frame->WriteData(rgba_buffer, length);
+
+ // Clean-up
+ delete[] rgba_buffer;
+ return ToJBool(success);
+ }
+ }
+ return JNI_FALSE;
+}
+
+jfloatArray Java_android_filterfw_core_GLFrame_getNativeFloats(JNIEnv* env, jobject thiz) {
+ GLFrame* frame = ConvertFromJava<GLFrame>(env, thiz);
+ if (frame && frame->Size() > 0) {
+ // Create the result array
+ jfloatArray result = env->NewFloatArray(frame->Size());
+ jfloat* float_array = env->GetFloatArrayElements(result, NULL);
+
+ // Read the frame pixels
+ uint8_t* pixels = new uint8_t[frame->Size()];
+ frame->CopyDataTo(pixels, frame->Size());
+
+ // Convert them to floats
+ ConvertRGBAToFloats(pixels, frame->Size(), float_array);
+
+ // Clean-up
+ delete[] pixels;
+ env->ReleaseFloatArrayElements(result, float_array, 0);
+ return result;
+ }
+ return NULL;
+}
+
+jboolean Java_android_filterfw_core_GLFrame_setNativeBitmap(JNIEnv* env,
+ jobject thiz,
+ jobject bitmap,
+ jint size) {
+ GLFrame* frame = ConvertFromJava<GLFrame>(env, thiz);
+ if (frame && bitmap) {
+ uint8_t* pixels;
+ const int result = AndroidBitmap_lockPixels(env, bitmap, reinterpret_cast<void**>(&pixels));
+ if (result == ANDROID_BITMAP_RESUT_SUCCESS) {
+ const bool success = frame->WriteData(pixels, size);
+ return ToJBool(success &&
+ AndroidBitmap_unlockPixels(env, bitmap) == ANDROID_BITMAP_RESUT_SUCCESS);
+ }
+ }
+ return JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_GLFrame_getNativeBitmap(JNIEnv* env,
+ jobject thiz,
+ jobject bitmap) {
+ GLFrame* frame = ConvertFromJava<GLFrame>(env, thiz);
+ if (frame && bitmap) {
+ uint8_t* pixels;
+ const int result = AndroidBitmap_lockPixels(env, bitmap, reinterpret_cast<void**>(&pixels));
+ if (result == ANDROID_BITMAP_RESUT_SUCCESS) {
+ frame->CopyDataTo(pixels, frame->Size());
+ return (AndroidBitmap_unlockPixels(env, bitmap) == ANDROID_BITMAP_RESUT_SUCCESS);
+ }
+ }
+ return JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_GLFrame_setNativeViewport(JNIEnv* env,
+ jobject thiz,
+ jint x,
+ jint y,
+ jint width,
+ jint height) {
+ GLFrame* frame = ConvertFromJava<GLFrame>(env, thiz);
+ return frame ? ToJBool(frame->SetViewport(x, y, width, height)) : JNI_FALSE;
+}
+
+jint Java_android_filterfw_core_GLFrame_getNativeTextureId(JNIEnv* env, jobject thiz) {
+ GLFrame* frame = ConvertFromJava<GLFrame>(env, thiz);
+ return frame ? frame->GetTextureId() : -1;
+}
+
+jint Java_android_filterfw_core_GLFrame_getNativeFboId(JNIEnv* env, jobject thiz) {
+ GLFrame* frame = ConvertFromJava<GLFrame>(env, thiz);
+ return frame ? frame->GetFboId() : -1;
+}
+
+jboolean Java_android_filterfw_core_GLFrame_generateNativeMipMap(JNIEnv* env, jobject thiz) {
+ GLFrame* frame = ConvertFromJava<GLFrame>(env, thiz);
+ return frame ? ToJBool(frame->GenerateMipMap()) : JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_GLFrame_setNativeTextureParam(JNIEnv* env,
+ jobject thiz,
+ jint param,
+ jint value) {
+ GLFrame* frame = ConvertFromJava<GLFrame>(env, thiz);
+ return frame ? ToJBool(frame->SetTextureParameter(param, value)) : JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_GLFrame_nativeResetParams(JNIEnv* env, jobject thiz) {
+ GLFrame* frame = ConvertFromJava<GLFrame>(env, thiz);
+ return frame ? ToJBool(frame->ResetTexParameters()) : JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_GLFrame_nativeCopyFromNative(JNIEnv* env,
+ jobject thiz,
+ jobject frame) {
+ GLFrame* this_frame = ConvertFromJava<GLFrame>(env, thiz);
+ NativeFrame* other_frame = ConvertFromJava<NativeFrame>(env, frame);
+ if (this_frame && other_frame) {
+ return ToJBool(this_frame->WriteData(other_frame->Data(), other_frame->Size()));
+ }
+ return JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_GLFrame_nativeCopyFromGL(JNIEnv* env,
+ jobject thiz,
+ jobject frame) {
+ GLFrame* this_frame = ConvertFromJava<GLFrame>(env, thiz);
+ GLFrame* other_frame = ConvertFromJava<GLFrame>(env, frame);
+ if (this_frame && other_frame) {
+ return ToJBool(this_frame->CopyPixelsFrom(other_frame));
+ }
+ return JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_GLFrame_nativeFocus(JNIEnv* env, jobject thiz) {
+ GLFrame* frame = ConvertFromJava<GLFrame>(env, thiz);
+ return ToJBool(frame && frame->FocusFrameBuffer());
+}
+
+jboolean Java_android_filterfw_core_GLFrame_nativeReattachTexToFbo(JNIEnv* env, jobject thiz) {
+ GLFrame* frame = ConvertFromJava<GLFrame>(env, thiz);
+ return ToJBool(frame && frame->ReattachTextureToFbo());
+}
+
+jboolean Java_android_filterfw_core_GLFrame_nativeDetachTexFromFbo(JNIEnv* env, jobject thiz) {
+ GLFrame* frame = ConvertFromJava<GLFrame>(env, thiz);
+ return ToJBool(frame && frame->DetachTextureFromFbo());
+}
+
diff --git a/media/mca/filterfw/jni/jni_gl_frame.h b/media/mca/filterfw/jni/jni_gl_frame.h
new file mode 100644
index 0000000..8a25aea
--- /dev/null
+++ b/media/mca/filterfw/jni/jni_gl_frame.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FILTERFW_JNI_GL_FRAME_H
+#define ANDROID_FILTERFW_JNI_GL_FRAME_H
+
+#include <jni.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLFrame_nativeAllocate(JNIEnv* env,
+ jobject thiz,
+ jobject gl_env,
+ jint width,
+ jint height);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLFrame_nativeAllocateWithTexture(JNIEnv* env,
+ jobject thiz,
+ jobject gl_env,
+ jint tex_id,
+ jint width,
+ jint height);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLFrame_nativeAllocateWithFbo(JNIEnv* env,
+ jobject thiz,
+ jobject gl_env,
+ jint fbo_id,
+ jint width,
+ jint height);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLFrame_nativeAllocateExternal(JNIEnv* env,
+ jobject thiz,
+ jobject gl_env);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLFrame_nativeDeallocate(JNIEnv* env, jobject thiz);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLFrame_setNativeInts(JNIEnv* env, jobject thiz, jintArray ints);
+
+JNIEXPORT jintArray JNICALL
+Java_android_filterfw_core_GLFrame_getNativeInts(JNIEnv* env, jobject thiz);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLFrame_setNativeFloats(JNIEnv* env, jobject thiz, jfloatArray ints);
+
+JNIEXPORT jfloatArray JNICALL
+Java_android_filterfw_core_GLFrame_getNativeFloats(JNIEnv* env, jobject thiz);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLFrame_setNativeData(JNIEnv* env,
+ jobject thiz,
+ jbyteArray data,
+ jint offset,
+ jint length);
+
+JNIEXPORT jbyteArray JNICALL
+Java_android_filterfw_core_GLFrame_getNativeData(JNIEnv* env, jobject thiz);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLFrame_setNativeBitmap(JNIEnv* env,
+ jobject thiz,
+ jobject bitmap,
+ jint size);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLFrame_getNativeBitmap(JNIEnv* env, jobject thiz, jobject bitmap);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLFrame_setNativeViewport(JNIEnv* env,
+ jobject thiz,
+ jint x,
+ jint y,
+ jint width,
+ jint height);
+
+JNIEXPORT jint JNICALL
+Java_android_filterfw_core_GLFrame_getNativeTextureId(JNIEnv* env, jobject thiz);
+
+JNIEXPORT jint JNICALL
+Java_android_filterfw_core_GLFrame_getNativeFboId(JNIEnv* env, jobject thiz);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLFrame_generateNativeMipMap(JNIEnv* env, jobject thiz);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLFrame_setNativeTextureParam(JNIEnv* env,
+ jobject thiz,
+ jint param,
+ jint value);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLFrame_nativeResetParams(JNIEnv* env, jobject thiz);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLFrame_nativeCopyFromNative(JNIEnv* env,
+ jobject thiz,
+ jobject frame);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLFrame_nativeCopyFromGL(JNIEnv* env,
+ jobject thiz,
+ jobject frame);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLFrame_nativeFocus(JNIEnv* env, jobject thiz);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLFrame_nativeReattachTexToFbo(JNIEnv* env, jobject thiz);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_GLFrame_nativeDetachTexFromFbo(JNIEnv* env, jobject thiz);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ANDROID_FILTERFW_JNI_GL_FRAME_H */
diff --git a/media/mca/filterfw/jni/jni_init.cpp b/media/mca/filterfw/jni/jni_init.cpp
new file mode 100644
index 0000000..3b131f1
--- /dev/null
+++ b/media/mca/filterfw/jni/jni_init.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jni/jni_util.h"
+
+#include "native/core/native_frame.h"
+#include "native/core/native_program.h"
+#include "native/core/gl_env.h"
+#include "native/core/gl_frame.h"
+#include "native/core/shader_program.h"
+#include "native/core/vertex_frame.h"
+
+using namespace android::filterfw;
+
+JavaVM* g_current_java_vm_ = NULL;
+
+jint JNI_OnLoad(JavaVM* vm, void* reserved) {
+ // Set the current vm pointer
+ g_current_java_vm_ = vm;
+
+ // Initialize object pools
+ ObjectPool<NativeFrame>::Setup("android/filterfw/core/NativeFrame", "nativeFrameId");
+ ObjectPool<NativeProgram>::Setup("android/filterfw/core/NativeProgram", "nativeProgramId");
+ ObjectPool<GLFrame>::Setup("android/filterfw/core/GLFrame", "glFrameId");
+ ObjectPool<ShaderProgram>::Setup("android/filterfw/core/ShaderProgram", "shaderProgramId");
+ ObjectPool<GLEnv>::Setup("android/filterfw/core/GLEnvironment", "glEnvId");
+ ObjectPool<VertexFrame>::Setup("android/filterfw/core/VertexFrame", "vertexFrameId");
+
+ return JNI_VERSION_1_4;
+}
diff --git a/media/mca/filterfw/jni/jni_native_buffer.cpp b/media/mca/filterfw/jni/jni_native_buffer.cpp
new file mode 100644
index 0000000..097c145
--- /dev/null
+++ b/media/mca/filterfw/jni/jni_native_buffer.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jni/jni_native_buffer.h"
+#include "jni/jni_util.h"
+
+char* GetJBufferData(JNIEnv* env, jobject buffer, int* size) {
+ jclass base_class = env->FindClass("android/filterfw/core/NativeBuffer");
+
+ // Get fields
+ jfieldID ptr_field = env->GetFieldID(base_class, "mDataPointer", "J");
+ jfieldID size_field = env->GetFieldID(base_class, "mSize", "I");
+
+ // Get their values
+ char* data = reinterpret_cast<char*>(env->GetLongField(buffer, ptr_field));
+ if (size) {
+ *size = env->GetIntField(buffer, size_field);
+ }
+
+ // Clean-up
+ env->DeleteLocalRef(base_class);
+
+ return data;
+}
+
+bool AttachDataToJBuffer(JNIEnv* env, jobject buffer, char* data, int size) {
+ jclass base_class = env->FindClass("android/filterfw/core/NativeBuffer");
+
+ // Get fields
+ jfieldID ptr_field = env->GetFieldID(base_class, "mDataPointer", "J");
+ jfieldID size_field = env->GetFieldID(base_class, "mSize", "I");
+
+ // Set their values
+ env->SetLongField(buffer, ptr_field, reinterpret_cast<jlong>(data));
+ env->SetIntField(buffer, size_field, size);
+
+ return true;
+}
+
+jboolean Java_android_filterfw_core_NativeBuffer_allocate(JNIEnv* env, jobject thiz, jint size) {
+ char* data = new char[size];
+ return ToJBool(AttachDataToJBuffer(env, thiz, data, size));
+}
+
+jboolean Java_android_filterfw_core_NativeBuffer_deallocate(JNIEnv* env,
+ jobject thiz,
+ jboolean owns_data) {
+ if (ToCppBool(owns_data)) {
+ char* data = GetJBufferData(env, thiz, NULL);
+ delete[] data;
+ }
+ return JNI_TRUE;
+}
+
+jboolean Java_android_filterfw_core_NativeBuffer_nativeCopyTo(JNIEnv* env,
+ jobject thiz,
+ jobject new_buffer) {
+ // Get source buffer
+ int size;
+ char* source_data = GetJBufferData(env, thiz, &size);
+
+ // Make copy
+ char* target_data = new char[size];
+ memcpy(target_data, source_data, size);
+
+ // Attach it to new buffer
+ AttachDataToJBuffer(env, new_buffer, target_data, size);
+
+ return JNI_TRUE;
+}
+
diff --git a/media/mca/filterfw/jni/jni_native_buffer.h b/media/mca/filterfw/jni/jni_native_buffer.h
new file mode 100644
index 0000000..73c12be
--- /dev/null
+++ b/media/mca/filterfw/jni/jni_native_buffer.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FILTEFW_JNI_NATIVE_BUFFER_H
+#define ANDROID_FILTEFW_JNI_NATIVE_BUFFER_H
+
+#include <jni.h>
+
+// Internal Buffer Unwrapping functions ////////////////////////////////////////////////////////////
+/**
+ * Given a Java NativeBuffer instance, get access to the underlying C pointer and its size. The
+ * size argument may be NULL, in which case the object is not queried for its size.
+ **/
+char* GetJBufferData(JNIEnv* env, jobject buffer, int* size);
+
+/**
+ * Attach a given C data buffer and its size to a given allocated Java NativeBuffer instance. After
+ * this call, the java instance will have the given C buffer as its backing. Note, that the Java
+ * instance contains the flag on whether or not it owns the buffer or not, so make sure it is what
+ * you expect.
+ **/
+bool AttachDataToJBuffer(JNIEnv* env, jobject buffer, char* data, int size);
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// JNI Wrappers ////////////////////////////////////////////////////////////////////////////////////
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_NativeBuffer_allocate(JNIEnv* env, jobject thiz, jint size);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_NativeBuffer_deallocate(JNIEnv* env, jobject thiz, jboolean owns_data);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_NativeBuffer_nativeCopyTo(JNIEnv* env, jobject thiz, jobject new_buffer);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ANDROID_FILTEFW_JNI_NATIVE_BUFFER_H
diff --git a/media/mca/filterfw/jni/jni_native_frame.cpp b/media/mca/filterfw/jni/jni_native_frame.cpp
new file mode 100644
index 0000000..1dfa3e6
--- /dev/null
+++ b/media/mca/filterfw/jni/jni_native_frame.cpp
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android/bitmap.h"
+
+#include "jni/jni_native_frame.h"
+#include "jni/jni_native_buffer.h"
+#include "jni/jni_util.h"
+
+#include "native/base/logging.h"
+#include "native/core/gl_frame.h"
+#include "native/core/native_frame.h"
+
+using android::filterfw::NativeFrame;
+using android::filterfw::GLFrame;
+
+typedef union {
+ uint32_t value;
+ uint8_t rgba[4];
+} Pixel;
+
+jboolean Java_android_filterfw_core_NativeFrame_nativeAllocate(JNIEnv* env,
+ jobject thiz,
+ jint size) {
+ return ToJBool(WrapObjectInJava(new NativeFrame(size), env, thiz, true));
+}
+
+jboolean Java_android_filterfw_core_NativeFrame_nativeDeallocate(JNIEnv* env, jobject thiz) {
+ return ToJBool(DeleteNativeObject<NativeFrame>(env, thiz));
+}
+
+jint Java_android_filterfw_core_NativeFrame_nativeIntSize(JNIEnv*, jclass) {
+ return sizeof(jint);
+}
+
+jint Java_android_filterfw_core_NativeFrame_nativeFloatSize(JNIEnv*, jclass) {
+ return sizeof(jfloat);
+}
+
+jboolean Java_android_filterfw_core_NativeFrame_setNativeData(JNIEnv* env,
+ jobject thiz,
+ jbyteArray data,
+ jint offset,
+ jint length) {
+ NativeFrame* frame = ConvertFromJava<NativeFrame>(env, thiz);
+ if (frame && data) {
+ jbyte* bytes = env->GetByteArrayElements(data, NULL);
+ if (bytes) {
+ const bool success = frame->WriteData(reinterpret_cast<const uint8_t*>(bytes + offset),
+ 0,
+ length);
+ env->ReleaseByteArrayElements(data, bytes, JNI_ABORT);
+ return ToJBool(success);
+ }
+ }
+ return JNI_FALSE;
+}
+
+jbyteArray Java_android_filterfw_core_NativeFrame_getNativeData(JNIEnv* env,
+ jobject thiz,
+ jint size) {
+ NativeFrame* frame = ConvertFromJava<NativeFrame>(env, thiz);
+ if (frame) {
+ const uint8_t* data = frame->Data();
+ if (!data || size > frame->Size())
+ return NULL;
+ jbyteArray result = env->NewByteArray(size);
+ env->SetByteArrayRegion(result, 0, size, reinterpret_cast<const jbyte*>(data));
+ return result;
+ }
+ return NULL;
+}
+
+jboolean Java_android_filterfw_core_NativeFrame_getNativeBuffer(JNIEnv* env,
+ jobject thiz,
+ jobject buffer) {
+ NativeFrame* frame = ConvertFromJava<NativeFrame>(env, thiz);
+ if (frame) {
+ char* data = reinterpret_cast<char*>(frame->MutableData());
+ return ToJBool(AttachDataToJBuffer(env, buffer, data, frame->Size()));
+ }
+ return JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_NativeFrame_setNativeInts(JNIEnv* env,
+ jobject thiz,
+ jintArray ints) {
+ NativeFrame* frame = ConvertFromJava<NativeFrame>(env, thiz);
+ if (frame && ints) {
+ jint* int_ptr = env->GetIntArrayElements(ints, NULL);
+ const int length = env->GetArrayLength(ints);
+ if (int_ptr) {
+ const bool success = frame->WriteData(reinterpret_cast<const uint8_t*>(int_ptr),
+ 0,
+ length * sizeof(jint));
+ env->ReleaseIntArrayElements(ints, int_ptr, JNI_ABORT);
+ return ToJBool(success);
+ }
+ }
+ return JNI_FALSE;
+}
+
+jintArray Java_android_filterfw_core_NativeFrame_getNativeInts(JNIEnv* env,
+ jobject thiz,
+ jint size) {
+ NativeFrame* frame = ConvertFromJava<NativeFrame>(env, thiz);
+ if (frame) {
+ const uint8_t* data = frame->Data();
+ if (!data || size > frame->Size() || (size % sizeof(jint)) != 0)
+ return NULL;
+ const int count = size / sizeof(jint);
+ jintArray result = env->NewIntArray(count);
+ env->SetIntArrayRegion(result, 0, count, reinterpret_cast<const jint*>(data));
+ return result;
+ }
+ return NULL;
+}
+
+jboolean Java_android_filterfw_core_NativeFrame_setNativeFloats(JNIEnv* env,
+ jobject thiz,
+ jfloatArray floats) {
+ NativeFrame* frame = ConvertFromJava<NativeFrame>(env, thiz);
+ if (frame && floats) {
+ jfloat* float_ptr = env->GetFloatArrayElements(floats, NULL);
+ const int length = env->GetArrayLength(floats);
+ if (float_ptr) {
+ const bool success = frame->WriteData(reinterpret_cast<const uint8_t*>(float_ptr),
+ 0,
+ length * sizeof(jfloat));
+ env->ReleaseFloatArrayElements(floats, float_ptr, JNI_ABORT);
+ return ToJBool(success);
+ }
+ }
+ return JNI_FALSE;
+}
+
+jfloatArray Java_android_filterfw_core_NativeFrame_getNativeFloats(JNIEnv* env,
+ jobject thiz,
+ jint size) {
+ NativeFrame* frame = ConvertFromJava<NativeFrame>(env, thiz);
+ if (frame) {
+ const uint8_t* data = frame->Data();
+ if (!data || size > frame->Size() || (size % sizeof(jfloat)) != 0)
+ return NULL;
+ const int count = size / sizeof(jfloat);
+ jfloatArray result = env->NewFloatArray(count);
+ env->SetFloatArrayRegion(result, 0, count, reinterpret_cast<const jfloat*>(data));
+ return result;
+ }
+ return NULL;
+}
+
+jboolean Java_android_filterfw_core_NativeFrame_setNativeBitmap(JNIEnv* env,
+ jobject thiz,
+ jobject bitmap,
+ jint size,
+ jint bytes_per_sample) {
+ NativeFrame* frame = ConvertFromJava<NativeFrame>(env, thiz);
+ if (frame && bitmap) {
+ // Make sure frame size matches bitmap size
+ if ((size / 4) != (frame->Size() / bytes_per_sample)) {
+ ALOGE("Size mismatch in native setBitmap()!");
+ return JNI_FALSE;
+ }
+
+ Pixel* src_ptr;
+ const int result = AndroidBitmap_lockPixels(env, bitmap, reinterpret_cast<void**>(&src_ptr));
+ if (result == ANDROID_BITMAP_RESUT_SUCCESS) {
+ // Create destination pointers
+ uint8_t* dst_ptr = reinterpret_cast<uint8_t*>(frame->MutableData());
+ const uint8_t* end_ptr = dst_ptr + frame->Size();
+ switch (bytes_per_sample) {
+ case 1: { // RGBA -> GRAY
+ while (dst_ptr < end_ptr) {
+ const Pixel pixel = *(src_ptr++);
+ *(dst_ptr++) = (pixel.rgba[0] + pixel.rgba[1] + pixel.rgba[2]) / 3;
+ }
+ break;
+ }
+ case 3: { // RGBA -> RGB
+ while (dst_ptr < end_ptr) {
+ const Pixel pixel = *(src_ptr++);
+ *(dst_ptr++) = pixel.rgba[0];
+ *(dst_ptr++) = pixel.rgba[1];
+ *(dst_ptr++) = pixel.rgba[2];
+ }
+ break;
+ }
+ case 4: { // RGBA -> RGBA
+ memcpy(dst_ptr, src_ptr, frame->Size());
+ break;
+ }
+ default:
+ ALOGE("Unsupported bytes-per-pixel %d in setBitmap!", bytes_per_sample);
+ break;
+ }
+ return (AndroidBitmap_unlockPixels(env, bitmap) == ANDROID_BITMAP_RESUT_SUCCESS);
+ }
+ }
+ return JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_NativeFrame_getNativeBitmap(JNIEnv* env,
+ jobject thiz,
+ jobject bitmap,
+ jint size,
+ jint bytes_per_sample) {
+ NativeFrame* frame = ConvertFromJava<NativeFrame>(env, thiz);
+ if (frame && bitmap) {
+ Pixel* dst_ptr;
+ const int result = AndroidBitmap_lockPixels(env, bitmap, reinterpret_cast<void**>(&dst_ptr));
+ if (result == ANDROID_BITMAP_RESUT_SUCCESS) {
+ // Make sure frame size matches bitmap size
+ if ((size / 4) != (frame->Size() / bytes_per_sample)) {
+ ALOGE("Size mismatch in native getBitmap()!");
+ return JNI_FALSE;
+ }
+
+ const uint8_t* src_ptr = frame->Data();
+ const uint8_t* end_ptr = src_ptr + frame->Size();
+ switch (bytes_per_sample) {
+ case 1: { // GRAY -> RGBA
+ while (src_ptr < end_ptr) {
+ const uint8_t value = *(src_ptr++);
+ dst_ptr->rgba[0] = dst_ptr->rgba[1] = dst_ptr->rgba[2] = value;
+ dst_ptr->rgba[3] = 255;
+ ++dst_ptr;
+ }
+ break;
+ }
+ case 3: { // RGB -> RGBA
+ while (src_ptr < end_ptr) {
+ dst_ptr->rgba[0] = *(src_ptr++);
+ dst_ptr->rgba[1] = *(src_ptr++);
+ dst_ptr->rgba[2] = *(src_ptr++);
+ dst_ptr->rgba[3] = 255;
+ ++dst_ptr;
+ }
+ break;
+ }
+ case 4: { // RGBA -> RGBA
+ memcpy(dst_ptr, src_ptr, frame->Size());
+ break;
+ }
+ default:
+ ALOGE("Unsupported bytes-per-pixel %d in getBitmap!", bytes_per_sample);
+ break;
+ }
+ return (AndroidBitmap_unlockPixels(env, bitmap) == ANDROID_BITMAP_RESUT_SUCCESS);
+ }
+ }
+ return JNI_FALSE;
+}
+
+jint Java_android_filterfw_core_NativeFrame_getNativeCapacity(JNIEnv* env, jobject thiz) {
+ NativeFrame* frame = ConvertFromJava<NativeFrame>(env, thiz);
+ return frame ? frame->Capacity() : -1;
+}
+
+jboolean Java_android_filterfw_core_NativeFrame_nativeCopyFromNative(JNIEnv* env,
+ jobject thiz,
+ jobject frame) {
+ NativeFrame* this_frame = ConvertFromJava<NativeFrame>(env, thiz);
+ NativeFrame* other_frame = ConvertFromJava<NativeFrame>(env, frame);
+ if (this_frame && other_frame) {
+ return ToJBool(this_frame->WriteData(other_frame->Data(), 0, other_frame->Size()));
+ }
+ return JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_NativeFrame_nativeCopyFromGL(JNIEnv* env,
+ jobject thiz,
+ jobject frame) {
+ NativeFrame* this_frame = ConvertFromJava<NativeFrame>(env, thiz);
+ GLFrame* other_frame = ConvertFromJava<GLFrame>(env, frame);
+ if (this_frame && other_frame) {
+ return ToJBool(other_frame->CopyDataTo(this_frame->MutableData(), this_frame->Size()));
+ }
+ return JNI_FALSE;
+}
diff --git a/media/mca/filterfw/jni/jni_native_frame.h b/media/mca/filterfw/jni/jni_native_frame.h
new file mode 100644
index 0000000..ecd9f82
--- /dev/null
+++ b/media/mca/filterfw/jni/jni_native_frame.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FILTERFW_JNI_NATIVE_FRAME_H
+#define ANDROID_FILTERFW_JNI_NATIVE_FRAME_H
+
+#include <jni.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_NativeFrame_nativeAllocate(JNIEnv* env, jobject thiz, jint size);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_NativeFrame_nativeDeallocate(JNIEnv* env, jobject thiz);
+
+JNIEXPORT jint JNICALL
+Java_android_filterfw_core_NativeFrame_nativeIntSize(JNIEnv* env, jclass clazz);
+
+JNIEXPORT jint JNICALL
+Java_android_filterfw_core_NativeFrame_nativeFloatSize(JNIEnv* env, jclass clazz);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_NativeFrame_setNativeInts(JNIEnv* env, jobject thiz, jintArray ints);
+
+JNIEXPORT jintArray JNICALL
+Java_android_filterfw_core_NativeFrame_getNativeInts(JNIEnv* env, jobject thiz, jint size);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_NativeFrame_setNativeFloats(JNIEnv* env, jobject thiz, jfloatArray ints);
+
+JNIEXPORT jfloatArray JNICALL
+Java_android_filterfw_core_NativeFrame_getNativeFloats(JNIEnv* env, jobject thiz, jint size);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_NativeFrame_setNativeData(JNIEnv* env,
+ jobject thiz,
+ jbyteArray data,
+ jint offset,
+ jint length);
+
+JNIEXPORT jbyteArray JNICALL
+Java_android_filterfw_core_NativeFrame_getNativeData(JNIEnv* env, jobject thiz, jint size);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_NativeFrame_getNativeBuffer(JNIEnv* env, jobject thiz, jobject buffer);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_NativeFrame_setNativeBitmap(JNIEnv* env,
+ jobject thiz,
+ jobject bitmap,
+ jint size,
+ jint bytes_per_sample);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_NativeFrame_getNativeBitmap(JNIEnv* env,
+ jobject thiz,
+ jobject bitmap,
+ jint size,
+ jint bytes_per_sample);
+
+JNIEXPORT jint JNICALL
+Java_android_filterfw_core_NativeFrame_getNativeCapacity(JNIEnv* env, jobject thiz);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_NativeFrame_nativeCopyFromNative(JNIEnv* env,
+ jobject thiz,
+ jobject frame);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_NativeFrame_nativeCopyFromGL(JNIEnv* env,
+ jobject thiz,
+ jobject frame);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ANDROID_FILTERFW_JNI_NATIVE_FRAME_H
diff --git a/media/mca/filterfw/jni/jni_native_program.cpp b/media/mca/filterfw/jni/jni_native_program.cpp
new file mode 100644
index 0000000..b30b769
--- /dev/null
+++ b/media/mca/filterfw/jni/jni_native_program.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+#include <vector>
+
+#include "jni/jni_native_program.h"
+#include "jni/jni_util.h"
+
+#include "native/base/logging.h"
+#include "native/core/native_frame.h"
+#include "native/core/native_program.h"
+
+using android::filterfw::NativeFrame;
+using android::filterfw::NativeProgram;
+
+jboolean Java_android_filterfw_core_NativeProgram_allocate(JNIEnv* env, jobject thiz) {
+ return ToJBool(WrapObjectInJava(new NativeProgram(), env, thiz, true));
+}
+
+jboolean Java_android_filterfw_core_NativeProgram_deallocate(JNIEnv* env, jobject thiz) {
+ return ToJBool(DeleteNativeObject<NativeProgram>(env, thiz));
+}
+
+jboolean Java_android_filterfw_core_NativeProgram_nativeInit(JNIEnv* env, jobject thiz) {
+ NativeProgram* program = ConvertFromJava<NativeProgram>(env, thiz);
+ return ToJBool(program && program->CallInit());
+}
+
+jboolean Java_android_filterfw_core_NativeProgram_openNativeLibrary(JNIEnv* env,
+ jobject thiz,
+ jstring lib_name) {
+ NativeProgram* program = ConvertFromJava<NativeProgram>(env, thiz);
+ return ToJBool(program && lib_name && program->OpenLibrary(ToCppString(env, lib_name)));
+}
+
+jboolean Java_android_filterfw_core_NativeProgram_bindInitFunction(JNIEnv* env,
+ jobject thiz,
+ jstring func_name) {
+ NativeProgram* program = ConvertFromJava<NativeProgram>(env, thiz);
+ return ToJBool(program && func_name && program->BindInitFunction(ToCppString(env, func_name)));
+}
+
+jboolean Java_android_filterfw_core_NativeProgram_bindSetValueFunction(JNIEnv* env,
+ jobject thiz,
+ jstring func_name) {
+ NativeProgram* program = ConvertFromJava<NativeProgram>(env, thiz);
+ return ToJBool(program &&
+ func_name &&
+ program->BindSetValueFunction(ToCppString(env, func_name)));
+}
+
+jboolean Java_android_filterfw_core_NativeProgram_bindGetValueFunction(JNIEnv* env,
+ jobject thiz,
+ jstring func_name) {
+ NativeProgram* program = ConvertFromJava<NativeProgram>(env, thiz);
+ return ToJBool(program &&
+ func_name &&
+ program->BindGetValueFunction(ToCppString(env, func_name)));
+}
+
+jboolean Java_android_filterfw_core_NativeProgram_bindProcessFunction(JNIEnv* env,
+ jobject thiz,
+ jstring func_name) {
+ NativeProgram* program = ConvertFromJava<NativeProgram>(env, thiz);
+ return ToJBool(program && func_name && program->BindProcessFunction(ToCppString(env, func_name)));
+}
+
+jboolean Java_android_filterfw_core_NativeProgram_bindResetFunction(JNIEnv* env,
+ jobject thiz,
+ jstring func_name) {
+ NativeProgram* program = ConvertFromJava<NativeProgram>(env, thiz);
+ return ToJBool(program &&
+ func_name &&
+ program->BindResetFunction(ToCppString(env, func_name)));
+}
+
+jboolean Java_android_filterfw_core_NativeProgram_bindTeardownFunction(JNIEnv* env,
+ jobject thiz,
+ jstring func_name) {
+ NativeProgram* program = ConvertFromJava<NativeProgram>(env, thiz);
+ return ToJBool(program &&
+ func_name &&
+ program->BindTeardownFunction(ToCppString(env, func_name)));
+}
+
+jboolean Java_android_filterfw_core_NativeProgram_callNativeInit(JNIEnv* env, jobject thiz) {
+ NativeProgram* program = ConvertFromJava<NativeProgram>(env, thiz);
+ return ToJBool(program && program->CallInit());
+}
+
+jboolean Java_android_filterfw_core_NativeProgram_callNativeSetValue(JNIEnv* env,
+ jobject thiz,
+ jstring key,
+ jstring value) {
+ if (!value) {
+ ALOGE("Native Program: Attempting to set null value for key %s!",
+ ToCppString(env, key).c_str());
+ }
+ NativeProgram* program = ConvertFromJava<NativeProgram>(env, thiz);
+ const std::string c_value = ToCppString(env, value);
+ const std::string c_key = ToCppString(env, key);
+ return ToJBool(program && program->CallSetValue(c_key, c_value));
+}
+
+jstring Java_android_filterfw_core_NativeProgram_callNativeGetValue(JNIEnv* env,
+ jobject thiz,
+ jstring key) {
+ NativeProgram* program = ConvertFromJava<NativeProgram>(env, thiz);
+ const std::string c_key = ToCppString(env, key);
+ if (program) {
+ return ToJString(env, program->CallGetValue(c_key));
+ }
+ return JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_NativeProgram_callNativeProcess(JNIEnv* env,
+ jobject thiz,
+ jobjectArray inputs,
+ jobject output) {
+ NativeProgram* program = ConvertFromJava<NativeProgram>(env, thiz);
+
+ // Sanity checks
+ if (!program || !inputs) {
+ return JNI_FALSE;
+ }
+
+ // Get the input buffers
+ const int input_count = env->GetArrayLength(inputs);
+ std::vector<const char*> input_buffers(input_count, NULL);
+ std::vector<int> input_sizes(input_count, 0);
+ for (int i = 0 ; i < input_count; ++i) {
+ const char* input_data = NULL;
+ int input_size = 0;
+ jobject input = env->GetObjectArrayElement(inputs, i);
+ if (input) {
+ NativeFrame* native_frame = ConvertFromJava<NativeFrame>(env, input);
+ if (!native_frame) {
+ ALOGE("NativeProgram: Could not grab NativeFrame input %d!", i);
+ return JNI_FALSE;
+ }
+ input_data = reinterpret_cast<const char*>(native_frame->Data());
+ input_size = native_frame->Size();
+ }
+ input_buffers[i] = input_data;
+ input_sizes[i] = input_size;
+ }
+
+ // Get the output buffer
+ char* output_data = NULL;
+ int output_size = 0;
+ if (output) {
+ NativeFrame* output_frame = ConvertFromJava<NativeFrame>(env, output);
+ if (!output_frame) {
+ ALOGE("NativeProgram: Could not grab NativeFrame output!");
+ return JNI_FALSE;
+ }
+ output_data = reinterpret_cast<char*>(output_frame->MutableData());
+ output_size = output_frame->Size();
+ }
+
+ // Process the frames!
+ return ToJBool(program->CallProcess(input_buffers, input_sizes, output_data, output_size));
+}
+
+jboolean Java_android_filterfw_core_NativeProgram_callNativeReset(JNIEnv* env, jobject thiz) {
+ NativeProgram* program = ConvertFromJava<NativeProgram>(env, thiz);
+ return ToJBool(program && program->CallReset());
+}
+
+jboolean Java_android_filterfw_core_NativeProgram_callNativeTeardown(JNIEnv* env, jobject thiz) {
+ NativeProgram* program = ConvertFromJava<NativeProgram>(env, thiz);
+ return ToJBool(program && program->CallTeardown());
+}
diff --git a/media/mca/filterfw/jni/jni_native_program.h b/media/mca/filterfw/jni/jni_native_program.h
new file mode 100644
index 0000000..fa97c39
--- /dev/null
+++ b/media/mca/filterfw/jni/jni_native_program.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FILTERFW_JNI_NATIVE_PROGRAM_H
+#define ANDROID_FILTERFW_JNI_NATIVE_PROGRAM_H
+
+#include <jni.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_NativeProgram_allocate(JNIEnv* env, jobject thiz);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_NativeProgram_deallocate(JNIEnv* env, jobject thiz);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_NativeProgram_nativeInit(JNIEnv* env, jobject thiz);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_NativeProgram_openNativeLibrary(JNIEnv* env,
+ jobject thiz,
+ jstring lib_name);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_NativeProgram_bindInitFunction(JNIEnv* env,
+ jobject thiz,
+ jstring func_name);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_NativeProgram_bindSetValueFunction(JNIEnv* env,
+ jobject thiz,
+ jstring func_name);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_NativeProgram_bindGetValueFunction(JNIEnv* env,
+ jobject thiz,
+ jstring func_name);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_NativeProgram_bindProcessFunction(JNIEnv* env,
+ jobject thiz,
+ jstring func_name);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_NativeProgram_bindResetFunction(JNIEnv* env,
+ jobject thiz,
+ jstring func_name);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_NativeProgram_bindTeardownFunction(JNIEnv* env,
+ jobject thiz,
+ jstring func_name);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_NativeProgram_callNativeInit(JNIEnv* env, jobject thiz);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_NativeProgram_callNativeSetValue(JNIEnv* env,
+ jobject thiz,
+ jstring key,
+ jstring value);
+
+JNIEXPORT jstring JNICALL
+Java_android_filterfw_core_NativeProgram_callNativeGetValue(JNIEnv* env,
+ jobject thiz,
+ jstring key);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_NativeProgram_callNativeProcess(JNIEnv* env,
+ jobject thiz,
+ jobjectArray inputs,
+ jobject output);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_NativeProgram_callNativeReset(JNIEnv* env, jobject thiz);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_NativeProgram_callNativeTeardown(JNIEnv* env, jobject thiz);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ANDROID_FILTERFW_JNI_NATIVE_PROGRAM_H
diff --git a/media/mca/filterfw/jni/jni_shader_program.cpp b/media/mca/filterfw/jni/jni_shader_program.cpp
new file mode 100644
index 0000000..19f43cd
--- /dev/null
+++ b/media/mca/filterfw/jni/jni_shader_program.cpp
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+#include <vector>
+
+#include "jni/jni_shader_program.h"
+#include "jni/jni_util.h"
+
+#include "native/base/logging.h"
+#include "native/core/geometry.h"
+#include "native/core/gl_env.h"
+#include "native/core/gl_frame.h"
+#include "native/core/shader_program.h"
+#include "native/core/vertex_frame.h"
+
+using android::filterfw::GLEnv;
+using android::filterfw::GLFrame;
+using android::filterfw::Point;
+using android::filterfw::ProgramVar;
+using android::filterfw::Quad;
+using android::filterfw::ShaderProgram;
+using android::filterfw::VertexFrame;
+
+jboolean Java_android_filterfw_core_ShaderProgram_allocate(JNIEnv* env,
+ jobject thiz,
+ jobject gl_env,
+ jstring vertex_shader,
+ jstring fragment_shader) {
+ // Get the GLEnv pointer
+ GLEnv* gl_env_ptr = ConvertFromJava<GLEnv>(env, gl_env);
+
+ // Create the shader
+ if (!fragment_shader || !gl_env_ptr)
+ return false;
+ else if (!vertex_shader)
+ return ToJBool(WrapObjectInJava(new ShaderProgram(
+ gl_env_ptr,
+ ToCppString(env, fragment_shader)),
+ env,
+ thiz,
+ true));
+ else
+ return ToJBool(WrapObjectInJava(new ShaderProgram(
+ gl_env_ptr,
+ ToCppString(env, vertex_shader),
+ ToCppString(env, fragment_shader)),
+ env,
+ thiz,
+ true));
+}
+
+jboolean Java_android_filterfw_core_ShaderProgram_deallocate(JNIEnv* env, jobject thiz) {
+ return ToJBool(DeleteNativeObject<ShaderProgram>(env, thiz));
+}
+
+jboolean Java_android_filterfw_core_ShaderProgram_compileAndLink(JNIEnv* env, jobject thiz) {
+ ShaderProgram* program = ConvertFromJava<ShaderProgram>(env, thiz);
+ return program ? ToJBool(program->CompileAndLink()) : JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_ShaderProgram_setUniformValue(JNIEnv* env,
+ jobject thiz,
+ jstring key,
+ jobject value) {
+ ShaderProgram* program = ConvertFromJava<ShaderProgram>(env, thiz);
+ const Value c_value = ToCValue(env, value);
+ const std::string c_key = ToCppString(env, key);
+ if (c_value.value) {
+ return ToJBool(program && program->SetUniformValue(c_key, c_value));
+ } else {
+ ALOGE("ShaderProgram: Could not convert java object value passed for key '%s'!", c_key.c_str());
+ return JNI_FALSE;
+ }
+}
+
+jobject Java_android_filterfw_core_ShaderProgram_getUniformValue(JNIEnv* env,
+ jobject thiz,
+ jstring key) {
+ ShaderProgram* program = ConvertFromJava<ShaderProgram>(env, thiz);
+ const std::string c_key = ToCppString(env, key);
+ return program ? ToJObject(env, program->GetUniformValue(c_key)) : JNI_NULL;
+}
+
+jboolean Java_android_filterfw_core_ShaderProgram_shaderProcess(JNIEnv* env,
+ jobject thiz,
+ jobjectArray inputs,
+ jobject output) {
+ ShaderProgram* program = ConvertFromJava<ShaderProgram>(env, thiz);
+ std::vector<const GLFrame*> input_frames;
+ if (program && inputs && output) {
+ // Get the input frames
+ const int input_count = env->GetArrayLength(inputs);
+ for (int i = 0; i < input_count; ++i) {
+ jobject input = env->GetObjectArrayElement(inputs, i);
+ const GLFrame* input_frame = ConvertFromJava<GLFrame>(env, input);
+ if (!input || !input_frame) {
+ ALOGE("ShaderProgram: invalid input frame %d!", i);
+ return JNI_FALSE;
+ }
+ input_frames.push_back(input_frame);
+ }
+
+ // Get the output frame
+ GLFrame* output_frame = ConvertFromJava<GLFrame>(env, output);
+ if (!output_frame) {
+ ALOGE("ShaderProgram: no output frame found!");
+ return JNI_FALSE;
+ }
+
+ // Process the frames!
+ if (!program->Process(input_frames, output_frame)) {
+ ALOGE("ShaderProgram: error processing shader!");
+ return JNI_FALSE;
+ }
+
+ return JNI_TRUE;
+ }
+ return JNI_FALSE;
+}
+
+jobject Java_android_filterfw_core_ShaderProgram_nativeCreateIdentity(JNIEnv* env,
+ jclass,
+ jobject gl_env) {
+ GLEnv* gl_env_ptr = ConvertFromJava<GLEnv>(env, gl_env);
+ ShaderProgram* program = gl_env_ptr ? ShaderProgram::CreateIdentity(gl_env_ptr) : NULL;
+ return program ? WrapNewObjectInJava(program, env, false) : NULL;
+}
+
+jboolean Java_android_filterfw_core_ShaderProgram_setSourceRegion(JNIEnv* env,
+ jobject thiz,
+ jfloat x0,
+ jfloat y0,
+ jfloat x1,
+ jfloat y1,
+ jfloat x2,
+ jfloat y2,
+ jfloat x3,
+ jfloat y3) {
+ ShaderProgram* program = ConvertFromJava<ShaderProgram>(env, thiz);
+ if (program) {
+ program->SetSourceRegion(Quad(Point(x0, y0), Point(x1, y1), Point(x2, y2), Point(x3, y3)));
+ return JNI_TRUE;
+ }
+ return JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_ShaderProgram_setTargetRegion(JNIEnv* env,
+ jobject thiz,
+ jfloat x0,
+ jfloat y0,
+ jfloat x1,
+ jfloat y1,
+ jfloat x2,
+ jfloat y2,
+ jfloat x3,
+ jfloat y3) {
+ ShaderProgram* program = ConvertFromJava<ShaderProgram>(env, thiz);
+ if (program) {
+ program->SetTargetRegion(Quad(Point(x0, y0), Point(x1, y1), Point(x2, y2), Point(x3, y3)));
+ return JNI_TRUE;
+ }
+ return JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_ShaderProgram_setShaderClearsOutput(JNIEnv* env,
+ jobject thiz,
+ jboolean clears) {
+ ShaderProgram* program = ConvertFromJava<ShaderProgram>(env, thiz);
+ if (program) {
+ program->SetClearsOutput(ToCppBool(clears));
+ return JNI_TRUE;
+ }
+ return JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_ShaderProgram_setShaderBlendEnabled(JNIEnv* env,
+ jobject thiz,
+ jboolean enable) {
+ ShaderProgram* program = ConvertFromJava<ShaderProgram>(env, thiz);
+ if (program) {
+ program->SetBlendEnabled(ToCppBool(enable));
+ return JNI_TRUE;
+ }
+ return JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_ShaderProgram_setShaderBlendFunc(JNIEnv* env,
+ jobject thiz,
+ jint sfactor,
+ jint dfactor) {
+ ShaderProgram* program = ConvertFromJava<ShaderProgram>(env, thiz);
+ if (program) {
+ program->SetBlendFunc(sfactor, dfactor);
+ return JNI_TRUE;
+ }
+ return JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_ShaderProgram_setShaderClearColor(JNIEnv* env,
+ jobject thiz,
+ jfloat r,
+ jfloat g,
+ jfloat b) {
+ ShaderProgram* program = ConvertFromJava<ShaderProgram>(env, thiz);
+ if (program) {
+ program->SetClearColor(r, g, b, 1.0f);
+ return JNI_TRUE;
+ }
+ return JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_ShaderProgram_setShaderDrawMode(JNIEnv* env,
+ jobject thiz,
+ jint draw_mode) {
+ ShaderProgram* program = ConvertFromJava<ShaderProgram>(env, thiz);
+ if (program) {
+ program->SetDrawMode(draw_mode);
+ return JNI_TRUE;
+ }
+ return JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_ShaderProgram_setShaderTileCounts(JNIEnv* env,
+ jobject thiz,
+ jint x_count,
+ jint y_count) {
+ ShaderProgram* program = ConvertFromJava<ShaderProgram>(env, thiz);
+ if (program) {
+ program->SetTileCounts(x_count, y_count);
+ return JNI_TRUE;
+ }
+ return JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_ShaderProgram_setShaderVertexCount(JNIEnv* env,
+ jobject thiz,
+ jint vertex_count) {
+ ShaderProgram* program = ConvertFromJava<ShaderProgram>(env, thiz);
+ if (program) {
+ program->SetVertexCount(vertex_count);
+ return JNI_TRUE;
+ }
+ return JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_ShaderProgram_beginShaderDrawing(JNIEnv* env, jobject thiz) {
+ ShaderProgram* program = ConvertFromJava<ShaderProgram>(env, thiz);
+ return ToJBool(program && program->BeginDraw());
+}
+
+jboolean Java_android_filterfw_core_ShaderProgram_setShaderAttributeValues(
+ JNIEnv* env,
+ jobject thiz,
+ jstring attr_name,
+ jfloatArray values,
+ jint component_count) {
+ ShaderProgram* program = ConvertFromJava<ShaderProgram>(env, thiz);
+ if (program) {
+ // Get the floats to set
+ jfloat* float_ptr = env->GetFloatArrayElements(values, NULL);
+ const int length = env->GetArrayLength(values);
+
+ // Get the program variable to set
+ const std::string attr_string = ToCppString(env, attr_name);
+ ProgramVar program_var = program->GetAttribute(attr_string);
+
+ // Set the variable
+ if (float_ptr && ShaderProgram::IsVarValid(program_var)) {
+ const bool success = program->SetAttributeValues(program_var,
+ reinterpret_cast<float*>(float_ptr),
+ length,
+ component_count);
+ env->ReleaseFloatArrayElements(values, float_ptr, JNI_ABORT);
+ return ToJBool(success);
+ }
+ }
+ return JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_ShaderProgram_setShaderAttributeVertexFrame(
+ JNIEnv* env,
+ jobject thiz,
+ jstring attr_name,
+ jobject vertex_frame,
+ jint type,
+ jint component_count,
+ jint stride,
+ jint offset,
+ jboolean normalize) {
+ ShaderProgram* program = ConvertFromJava<ShaderProgram>(env, thiz);
+ if (program) {
+ // Get the vertex frame
+ VertexFrame* v_frame = ConvertFromJava<VertexFrame>(env, vertex_frame);
+
+ // Get the program variable to set
+ const std::string attr_string = ToCppString(env, attr_name);
+ ProgramVar program_var = program->GetAttribute(attr_string);
+
+ // Set the variable
+ if (v_frame && ShaderProgram::IsVarValid(program_var)) {
+ const bool success = program->SetAttributeValues(program_var,
+ v_frame,
+ type,
+ component_count,
+ stride,
+ offset,
+ ToCppBool(normalize));
+ return ToJBool(success);
+ }
+ }
+ return JNI_FALSE;
+}
diff --git a/media/mca/filterfw/jni/jni_shader_program.h b/media/mca/filterfw/jni/jni_shader_program.h
new file mode 100644
index 0000000..94a1dd4
--- /dev/null
+++ b/media/mca/filterfw/jni/jni_shader_program.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FILTERFW_JNI_SHADER_PROGRAM_H
+#define ANDROID_FILTERFW_JNI_SHADER_PROGRAM_H
+
+#include <jni.h>
+
+#include "native/core/value.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_ShaderProgram_allocate(JNIEnv* env,
+ jobject thiz,
+ jobject gl_env,
+ jstring vertex_shader,
+ jstring fragment_shader);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_ShaderProgram_deallocate(JNIEnv* env, jobject thiz);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_ShaderProgram_compileAndLink(JNIEnv* env, jobject thiz);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_ShaderProgram_setUniformValue(JNIEnv* env,
+ jobject thiz,
+ jstring key,
+ jobject value);
+
+JNIEXPORT jobject JNICALL
+Java_android_filterfw_core_ShaderProgram_getUniformValue(JNIEnv* env,
+ jobject thiz,
+ jstring key);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_ShaderProgram_shaderProcess(JNIEnv* env,
+ jobject thiz,
+ jobjectArray inputs,
+ jobject output);
+
+JNIEXPORT jobject JNICALL
+Java_android_filterfw_core_ShaderProgram_nativeCreateIdentity(JNIEnv* env,
+ jclass clazz,
+ jobject gl_env);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_ShaderProgram_setSourceRegion(JNIEnv* env,
+ jobject thiz,
+ jfloat x0,
+ jfloat y0,
+ jfloat x1,
+ jfloat y1,
+ jfloat x2,
+ jfloat y2,
+ jfloat x3,
+ jfloat y3);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_ShaderProgram_setTargetRegion(JNIEnv* env,
+ jobject thiz,
+ jfloat x0,
+ jfloat y0,
+ jfloat x1,
+ jfloat y1,
+ jfloat x2,
+ jfloat y2,
+ jfloat x3,
+ jfloat y3);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_ShaderProgram_setShaderClearsOutput(JNIEnv* env,
+ jobject thiz,
+ jboolean clears);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_ShaderProgram_setShaderClearColor(JNIEnv* env,
+ jobject thiz,
+ jfloat r,
+ jfloat g,
+ jfloat b);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_ShaderProgram_setShaderBlendEnabled(JNIEnv* env,
+ jobject thiz,
+ jboolean enable);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_ShaderProgram_setShaderBlendFunc(JNIEnv* env,
+ jobject thiz,
+ jint sfactor,
+ jint dfactor);
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_ShaderProgram_setShaderDrawMode(JNIEnv* env,
+ jobject thiz,
+ jint draw_mode);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_ShaderProgram_setShaderTileCounts(JNIEnv* env,
+ jobject thiz,
+ jint x_count,
+ jint y_count);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_ShaderProgram_setShaderVertexCount(JNIEnv* env,
+ jobject thiz,
+ jint vertex_count);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_ShaderProgram_beginShaderDrawing(JNIEnv* env, jobject thiz);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_ShaderProgram_setShaderAttributeValues(JNIEnv* env,
+ jobject thiz,
+ jstring attr_name,
+ jfloatArray values,
+ jint component_count);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_ShaderProgram_setShaderAttributeVertexFrame(JNIEnv* env,
+ jobject thiz,
+ jstring attr_name,
+ jobject vertex_frame,
+ jint type,
+ jint component_count,
+ jint stride,
+ jint offset,
+ jboolean normalize);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ANDROID_FILTERFW_JNI_SHADER_PROGRAM_H
diff --git a/media/mca/filterfw/jni/jni_util.cpp b/media/mca/filterfw/jni/jni_util.cpp
new file mode 100644
index 0000000..30c0898
--- /dev/null
+++ b/media/mca/filterfw/jni/jni_util.cpp
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+
+#include "jni/jni_util.h"
+
+#include "base/logging.h"
+
+#if 0
+JavaObject::JavaObject()
+ : object_(JNI_NULL),
+ ref_count_(new int(0)) {
+}
+
+JavaObject::JavaObject(const JavaObject& java_obj)
+ : object_(java_obj.object_),
+ ref_count_(java_obj.ref_count_) {
+ Retain();
+}
+
+JavaObject::JavaObject(jobject object, JNIEnv* env)
+ : object_(NULL),
+ ref_count_(new int(0)) {
+ Retain();
+ object_ = env->NewGlobalRef(object_);
+}
+
+JavaObject::~JavaObject() {
+ Release();
+}
+
+JavaObject& JavaObject::operator=(const JavaObject& java_obj) {
+ Release();
+ object_ = java_obj.object_;
+ ref_count_ = java_obj.ref_count_;
+ Retain();
+ return *this;
+}
+
+void JavaObject::Retain() {
+ if (ref_count_)
+ ++(*ref_count_);
+ else
+ ALOGE("JavaObject: Reference count is NULL! JavaObject may be corrupted.");
+}
+
+void JavaObject::Release() {
+ if (ref_count_) {
+ if (*ref_count_ > 0)
+ --(*ref_count_);
+ if (*ref_count_ == 0) {
+ JNIEnv* env = GetCurrentJNIEnv();
+ if (!env)
+ ALOGE("JavaObject: Releasing outside of Java thread. Will just leak!");
+ else if (object_)
+ env->DeleteGlobalRef(object_);
+ delete ref_count_;
+ ref_count_ = NULL;
+ }
+ } else {
+ ALOGE("JavaObject: Reference count is NULL! JavaObject may be corrupted.");
+ }
+}
+
+void JavaObject::Reset() {
+ Release();
+ object_ = NULL;
+ ref_count_ = new int(0);
+}
+
+JavaVM* GetCurrentJavaVM() {
+ return g_current_java_vm_;
+}
+
+JNIEnv* GetCurrentJNIEnv() {
+ JavaVM* vm = GetCurrentJavaVM();
+ JNIEnv* env = NULL;
+ const jint result = vm->GetEnv(reinterpret_cast<void**>(&env),
+ JNI_VERSION_1_4);
+ return result == JNI_OK ? env : NULL;
+}
+#endif
+
+jstring ToJString(JNIEnv* env, const std::string& value) {
+ return env->NewStringUTF(value.c_str());
+}
+
+std::string ToCppString(JNIEnv* env, jstring value) {
+ jboolean isCopy;
+ const char* c_value = env->GetStringUTFChars(value, &isCopy);
+ std::string result(c_value);
+ if (isCopy == JNI_TRUE)
+ env->ReleaseStringUTFChars(value, c_value);
+ return result;
+}
+
+jboolean ToJBool(bool value) {
+ return value ? JNI_TRUE : JNI_FALSE;
+}
+
+bool ToCppBool(jboolean value) {
+ return value == JNI_TRUE;
+}
+
+// TODO: We actually shouldn't use such a function as it requires a class name lookup at every
+// invocation. Instead, store the class objects and use those.
+bool IsJavaInstanceOf(JNIEnv* env, jobject object, const std::string& class_name) {
+ jclass clazz = env->FindClass(class_name.c_str());
+ return clazz ? env->IsInstanceOf(object, clazz) == JNI_TRUE : false;
+}
+
+template<typename T>
+jobject CreateJObject(JNIEnv* env, const std::string& class_name, const std::string& signature, T value) {
+ jobject result = JNI_NULL;
+
+ return result;
+}
+
+Value ToCValue(JNIEnv* env, jobject object) {
+ Value result = MakeNullValue();
+ if (object != NULL) {
+ if (IsJavaInstanceOf(env, object, "java/lang/Boolean")) {
+ jmethodID method = env->GetMethodID(env->GetObjectClass(object), "booleanValue", "()Z");
+ result = MakeIntValue(env->CallBooleanMethod(object, method) == JNI_TRUE ? 1 : 0);
+ } else if (IsJavaInstanceOf(env, object, "java/lang/Integer")) {
+ jmethodID method = env->GetMethodID(env->GetObjectClass(object), "intValue", "()I");
+ result = MakeIntValue(env->CallIntMethod(object, method));
+ } else if (IsJavaInstanceOf(env, object, "java/lang/Float")) {
+ jmethodID method = env->GetMethodID(env->GetObjectClass(object), "floatValue", "()F");
+ result = MakeFloatValue(env->CallFloatMethod(object, method));
+ } else if (IsJavaInstanceOf(env, object, "java/lang/String")) {
+ result = MakeStringValue(ToCppString(env, static_cast<jstring>(object)).c_str());
+ } else if (IsJavaInstanceOf(env, object, "[I")) {
+ jint* elems = env->GetIntArrayElements(static_cast<jintArray>(object), NULL);
+ const jint count = env->GetArrayLength(static_cast<jintArray>(object));
+ result = MakeIntArrayValue(elems, count);
+ env->ReleaseIntArrayElements(static_cast<jintArray>(object), elems, JNI_ABORT);
+ } else if (IsJavaInstanceOf(env, object, "[F")) {
+ jfloat* elems = env->GetFloatArrayElements(static_cast<jfloatArray>(object), NULL);
+ const jint count = env->GetArrayLength(static_cast<jfloatArray>(object));
+ result = MakeFloatArrayValue(elems, count);
+ env->ReleaseFloatArrayElements(static_cast<jfloatArray>(object), elems, JNI_ABORT);
+ }
+ }
+ return result;
+}
+
+jobject ToJObject(JNIEnv* env, const Value& value) {
+ jobject result = JNI_NULL;
+ if (ValueIsInt(value)) {
+ jclass clazz = env->FindClass("java/lang/Integer");
+ jmethodID constructorID = env->GetMethodID(clazz, "<init>", "(I)V");
+ result = env->NewObject(clazz, constructorID, GetIntValue(value));
+ } else if (ValueIsFloat(value)) {
+ jclass clazz = env->FindClass("java/lang/Float");
+ jmethodID constructorID = env->GetMethodID(clazz, "<init>", "(F)V");
+ result = env->NewObject(clazz, constructorID, GetFloatValue(value));
+ } else if (ValueIsString(value)) {
+ result = ToJString(env, GetStringValue(value));
+ } else if (ValueIsIntArray(value)) {
+ result = env->NewIntArray(GetValueCount(value));
+ env->SetIntArrayRegion(static_cast<jintArray>(result),
+ 0,
+ GetValueCount(value),
+ reinterpret_cast<const jint*>(GetIntArrayValue(value)));
+ } else if (ValueIsFloatArray(value)) {
+ result = env->NewFloatArray(GetValueCount(value));
+ env->SetFloatArrayRegion(static_cast<jfloatArray>(result),
+ 0,
+ GetValueCount(value),
+ reinterpret_cast<const jfloat*>(GetFloatArrayValue(value)));
+ }
+ return result;
+}
diff --git a/media/mca/filterfw/jni/jni_util.h b/media/mca/filterfw/jni/jni_util.h
new file mode 100644
index 0000000..68ff653
--- /dev/null
+++ b/media/mca/filterfw/jni/jni_util.h
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jni.h>
+
+#include <hash_map>
+#include <string>
+
+#include "base/utilities.h"
+#include "core/value.h"
+
+#ifndef ANDROID_FILTERFW_JNI_JNI_UTIL_H
+#define ANDROID_FILTERFW_JNI_JNI_UTIL_H
+
+// We add this JNI_NULL macro to allow consistent code separation of Java and
+// C++ types.
+#define JNI_NULL NULL
+
+#if 0
+// Pointer to current JavaVM. Do not use this directly. Instead use the funciton
+// GetCurrentJavaVM().
+extern JavaVM* g_current_java_vm_;
+
+// Wrapper around a java object pointer, which includes the environment
+// pointer in which the object "lives". This is used for passing down Java
+// objects from the Java layer to C++.
+// While an instance of this class does not own the underlying java object, it
+// does hold a global reference to it, so that the Java garbage collector does
+// not destroy it. It uses reference counting to determine when it can destroy
+// the reference.
+// TODO: Add multi-thread support!
+class JavaObject {
+ public:
+ // Creates a NULL JavaObject.
+ JavaObject();
+
+ // Creates a wrapper around the given object in the given JNI environment.
+ JavaObject(jobject object, JNIEnv* env);
+
+ // Copy constructor.
+ JavaObject(const JavaObject& java_obj);
+
+ // Destructor.
+ ~JavaObject();
+
+ // Assignment operator.
+ JavaObject& operator=(const JavaObject& java_obj);
+
+ // Access to the object (non-const as JNI functions are non-const).
+ jobject object() const {
+ return object_;
+ }
+
+ // Resets this object to the NULL JavaObject.
+ void Reset();
+
+ private:
+ // Retain the instance, i.e. increase reference count.
+ void Retain();
+
+ // Release the instance, i.e. decrease reference count.
+ void Release();
+
+ // The object pointer (not owned).
+ jobject object_;
+
+ // The reference count of this object
+ int* ref_count_;
+};
+#endif
+
+// ObjectPool template class. This class keeps track of C++ instances that are
+// coupled to Java objects. This is done by using an "id" field in the Java
+// object, which is then mapped to the correct instance here. It should not be
+// necessary to use this class directly. Instead, the convenience functions
+// below can be used.
+template<class T>
+class ObjectPool {
+ public:
+ // Create a new ObjectPool for a specific object type. Pass the path to the
+ // Java equivalent class of the C++ class, and the name of the java member
+ // field that will store the object's ID.
+ static void Setup(const std::string& jclass_name,
+ const std::string& id_fld_name) {
+ instance_ = new ObjectPool<T>(jclass_name, id_fld_name);
+ }
+
+ // Return the shared instance to this type's pool.
+ static ObjectPool* Instance() {
+ return instance_;
+ }
+
+ // Delete this type's pool.
+ static void TearDown() {
+ delete instance_;
+ }
+
+ // Register a new C++ object with the pool. This does not affect the Java
+ // layer. Use WrapObject() instead to perform the necessary Java-side
+ // assignments. Pass true to owns if the object pool owns the object.
+ int RegisterObject(T* object, bool owns) {
+ const int id = next_id_;
+ objects_[id] = object;
+ owns_[id] = owns;
+ ++next_id_;
+ return id;
+ }
+
+ // Return the object in the pool with the specified ID.
+ T* ObjectWithID(int obj_id) const {
+ typename CObjMap::const_iterator iter = objects_.find(obj_id);
+ return iter == objects_.end() ? NULL : iter->second;
+ }
+
+ // Get the ID of a Java object. This ID can be used to look-up the C++
+ // object.
+ int GetObjectID(JNIEnv* env, jobject j_object) {
+ jclass cls = env->GetObjectClass(j_object);
+ jfieldID id_field = env->GetFieldID(cls, id_field_name_.c_str(), "I");
+ const int result = env->GetIntField(j_object, id_field);
+ env->DeleteLocalRef(cls);
+ return result;
+ }
+
+ // Take a C++ object and wrap it with a given Java object. This will
+ // essentially set the ID member of the Java object to the ID of the C++
+ // object. Pass true to owns if the object pool owns the object.
+ bool WrapObject(T* c_object, JNIEnv* env, jobject j_object, bool owns) {
+ const int id = RegisterObject(c_object, owns);
+ jclass cls = env->GetObjectClass(j_object);
+ jfieldID id_field = env->GetFieldID(cls, id_field_name_.c_str(), "I");
+ env->SetIntField(j_object, id_field, id);
+ env->DeleteLocalRef(cls);
+ return true;
+ }
+
+ // Remove the object with the given ID from this pool, and delete it. This
+ // does not affect the Java layer.
+ bool DeleteObjectWithID(int obj_id) {
+ typename CObjMap::iterator iter = objects_.find(obj_id);
+ const bool found = iter != objects_.end();
+ if (found) {
+ if (owns_[obj_id])
+ delete iter->second;
+ objects_.erase(iter);
+ }
+ return found;
+ }
+
+ // Instantiates a new java object for this class. The Java class must have
+ // a default constructor for this to succeed.
+ jobject CreateJavaObject(JNIEnv* env) {
+ jclass cls = env->FindClass(jclass_name_.c_str());
+ jmethodID constructor = env->GetMethodID(
+ cls,
+ "<init>",
+ "(Landroid/filterfw/core/NativeAllocatorTag;)V");
+ jobject result = env->NewObject(cls, constructor, JNI_NULL);
+ env->DeleteLocalRef(cls);
+ return result;
+ }
+
+ int GetObjectCount() const {
+ return objects_.size();
+ }
+
+ const std::string& GetJavaClassName() const {
+ return jclass_name_;
+ }
+
+ private:
+ explicit ObjectPool(const std::string& jclass_name,
+ const std::string& id_fld_name)
+ : jclass_name_(jclass_name),
+ id_field_name_(id_fld_name),
+ next_id_(0) { }
+
+ typedef std::hash_map<int, T*> CObjMap;
+ typedef std::hash_map<int, bool> FlagMap;
+ static ObjectPool* instance_;
+ std::string jclass_name_;
+ std::string id_field_name_;
+ int next_id_;
+ CObjMap objects_;
+ FlagMap owns_;
+
+ DISALLOW_COPY_AND_ASSIGN(ObjectPool);
+};
+
+template<typename T> ObjectPool<T>* ObjectPool<T>::instance_ = NULL;
+
+// Convenience Functions ///////////////////////////////////////////////////////
+
+// This function "links" the C++ instance and the Java instance, so that they
+// can be mapped to one another. This must be called for every C++ instance
+// which is wrapped by a Java front-end interface. Pass true to owns, if the
+// Java layer should own the object.
+template<typename T>
+bool WrapObjectInJava(T* c_object, JNIEnv* env, jobject j_object, bool owns) {
+ ObjectPool<T>* pool = ObjectPool<T>::Instance();
+ return pool ? pool->WrapObject(c_object, env, j_object, owns) : false;
+}
+
+// Creates a new Java instance, which wraps the passed C++ instance. Returns
+// the wrapped object or JNI_NULL if there was an error. Pass true to owns, if
+// the Java layer should own the object.
+template<typename T>
+jobject WrapNewObjectInJava(T* c_object, JNIEnv* env, bool owns) {
+ ObjectPool<T>* pool = ObjectPool<T>::Instance();
+ if (pool) {
+ jobject result = pool->CreateJavaObject(env);
+ if (WrapObjectInJava(c_object, env, result, owns))
+ return result;
+ }
+ return JNI_NULL;
+}
+
+// Use ConvertFromJava to obtain a C++ instance given a Java object. This
+// instance must have been wrapped in Java using the WrapObjectInJava()
+// function.
+template<typename T>
+T* ConvertFromJava(JNIEnv* env, jobject j_object) {
+ ObjectPool<T>* pool = ObjectPool<T>::Instance();
+ return pool && j_object
+ ? pool->ObjectWithID(pool->GetObjectID(env, j_object))
+ : NULL;
+}
+
+// Delete the native object given a Java instance. This should be called from
+// the Java object's finalizer.
+template<typename T>
+bool DeleteNativeObject(JNIEnv* env, jobject j_object) {
+ ObjectPool<T>* pool = ObjectPool<T>::Instance();
+ return pool && j_object
+ ? pool->DeleteObjectWithID(pool->GetObjectID(env, j_object))
+ : false;
+}
+
+#if 0
+// Get the current JNI VM, or NULL if there is no current VM
+JavaVM* GetCurrentJavaVM();
+
+// Get the current JNI environment, or NULL if this is not a JNI thread
+JNIEnv* GetCurrentJNIEnv();
+#endif
+
+// Convert C++ boolean to Java boolean.
+jboolean ToJBool(bool value);
+
+// Convert Java boolean to C++ boolean.
+bool ToCppBool(jboolean value);
+
+// Convert Java String to C++ string.
+jstring ToJString(JNIEnv* env, const std::string& value);
+
+// Convert C++ string to Java String.
+std::string ToCppString(JNIEnv* env, jstring value);
+
+// Convert Java object to a (C) Value object.
+Value ToCValue(JNIEnv* env, jobject object);
+
+// Convert a (C) Value object to a Java object.
+jobject ToJObject(JNIEnv* env, const Value& value);
+
+// Returns true, iff the passed object is an instance of the class specified
+// by its fully qualified class name.
+bool IsJavaInstanceOf(JNIEnv* env, jobject object,
+ const std::string& class_name);
+
+#endif // ANDROID_FILTERFW_JNI_JNI_UTIL_H
diff --git a/media/mca/filterfw/jni/jni_vertex_frame.cpp b/media/mca/filterfw/jni/jni_vertex_frame.cpp
new file mode 100644
index 0000000..caae938
--- /dev/null
+++ b/media/mca/filterfw/jni/jni_vertex_frame.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jni/jni_vertex_frame.h"
+#include "jni/jni_util.h"
+
+#include "native/core/vertex_frame.h"
+
+using android::filterfw::VertexFrame;
+
+jboolean Java_android_filterfw_core_VertexFrame_nativeAllocate(JNIEnv* env,
+ jobject thiz,
+ jint size) {
+ return ToJBool(WrapObjectInJava(new VertexFrame(size), env, thiz, true));
+}
+
+jboolean Java_android_filterfw_core_VertexFrame_nativeDeallocate(JNIEnv* env, jobject thiz) {
+ return ToJBool(DeleteNativeObject<VertexFrame>(env, thiz));
+}
+
+jboolean Java_android_filterfw_core_VertexFrame_setNativeInts(JNIEnv* env,
+ jobject thiz,
+ jintArray ints) {
+
+ VertexFrame* frame = ConvertFromJava<VertexFrame>(env, thiz);
+ if (frame && ints) {
+ jint* int_ptr = env->GetIntArrayElements(ints, NULL);
+ const int length = env->GetArrayLength(ints);
+ if (int_ptr) {
+ const bool success = frame->WriteData(reinterpret_cast<const uint8_t*>(int_ptr),
+ length * sizeof(jint));
+ env->ReleaseIntArrayElements(ints, int_ptr, JNI_ABORT);
+ return ToJBool(success);
+ }
+ }
+ return JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_VertexFrame_setNativeFloats(JNIEnv* env,
+ jobject thiz,
+ jfloatArray floats) {
+ VertexFrame* frame = ConvertFromJava<VertexFrame>(env, thiz);
+ if (frame && floats) {
+ jfloat* float_ptr = env->GetFloatArrayElements(floats, NULL);
+ const int length = env->GetArrayLength(floats);
+ if (float_ptr) {
+ const bool success = frame->WriteData(reinterpret_cast<const uint8_t*>(float_ptr),
+ length * sizeof(jfloat));
+ env->ReleaseFloatArrayElements(floats, float_ptr, JNI_ABORT);
+ return ToJBool(success);
+ }
+ }
+ return JNI_FALSE;
+}
+
+jboolean Java_android_filterfw_core_VertexFrame_setNativeData(JNIEnv* env,
+ jobject thiz,
+ jbyteArray data,
+ jint offset,
+ jint length) {
+ VertexFrame* frame = ConvertFromJava<VertexFrame>(env, thiz);
+ if (frame && data) {
+ jbyte* bytes = env->GetByteArrayElements(data, NULL);
+ if (bytes) {
+ const bool success = frame->WriteData(reinterpret_cast<const uint8_t*>(bytes + offset),
+ length);
+ env->ReleaseByteArrayElements(data, bytes, JNI_ABORT);
+ return ToJBool(success);
+ }
+ }
+ return JNI_FALSE;
+}
+
+jint Java_android_filterfw_core_VertexFrame_getNativeVboId(JNIEnv* env, jobject thiz) {
+ VertexFrame* frame = ConvertFromJava<VertexFrame>(env, thiz);
+ return frame ? frame->GetVboId() : -1;
+}
diff --git a/media/mca/filterfw/jni/jni_vertex_frame.h b/media/mca/filterfw/jni/jni_vertex_frame.h
new file mode 100644
index 0000000..fcd7ea1
--- /dev/null
+++ b/media/mca/filterfw/jni/jni_vertex_frame.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FILTERFW_JNI_VERTEX_FRAME_H
+#define ANDROID_FILTERFW_JNI_VERTEX_FRAME_H
+
+#include <jni.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_VertexFrame_nativeAllocate(JNIEnv* env, jobject thiz, jint size);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_VertexFrame_nativeDeallocate(JNIEnv* env, jobject thiz);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_VertexFrame_setNativeInts(JNIEnv* env, jobject thiz, jintArray ints);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_VertexFrame_setNativeFloats(JNIEnv* env,
+ jobject thiz,
+ jfloatArray floats);
+
+JNIEXPORT jboolean JNICALL
+Java_android_filterfw_core_VertexFrame_setNativeData(JNIEnv* env,
+ jobject thiz,
+ jbyteArray data,
+ jint offset,
+ jint length);
+
+JNIEXPORT jint JNICALL
+Java_android_filterfw_core_VertexFrame_getNativeVboId(JNIEnv* env, jobject thiz);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ANDROID_FILTERFW_JNI_VERTEX_FRAME_H
diff --git a/media/mca/filterfw/native/Android.mk b/media/mca/filterfw/native/Android.mk
new file mode 100644
index 0000000..46ee283
--- /dev/null
+++ b/media/mca/filterfw/native/Android.mk
@@ -0,0 +1,44 @@
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+#####################
+# Build module libfilterfw_static
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE := libfilterfw_native
+
+LOCAL_SRC_FILES += core/geometry.cpp \
+ core/gl_env.cpp \
+ core/gl_frame.cpp \
+ core/native_frame.cpp \
+ core/native_program.cpp \
+ core/shader_program.cpp \
+ core/vertex_frame.cpp \
+ core/value.cpp
+
+# add local includes
+include $(LOCAL_PATH)/libfilterfw.mk
+
+# gcc should always be placed at the end.
+LOCAL_EXPORT_LDLIBS := -llog -lgcc
+
+# TODO: Build a shared library as well?
+include $(BUILD_STATIC_LIBRARY)
+
diff --git a/media/mca/filterfw/native/base/logging.h b/media/mca/filterfw/native/base/logging.h
new file mode 100644
index 0000000..1236d0b
--- /dev/null
+++ b/media/mca/filterfw/native/base/logging.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FILTERFW_BASE_LOGGING_H
+#define ANDROID_FILTERFW_BASE_LOGGING_H
+
+#define LOG_EVERY_FRAME false
+
+#define LOG_FRAME(...) if (LOG_EVERY_FRAME) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
+
+#define LOG_TAG "MCA"
+#include <utils/Log.h>
+
+#endif // ANDROID_FILTERFW_BASE_LOGGING_H
diff --git a/media/mca/filterfw/native/base/utilities.h b/media/mca/filterfw/native/base/utilities.h
new file mode 100644
index 0000000..6bb3b7f
--- /dev/null
+++ b/media/mca/filterfw/native/base/utilities.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FILTERFW_BASE_UTILITIES_H
+#define ANDROID_FILTERFW_BASE_UTILITIES_H
+
+#include <set>
+#include <utility>
+
+namespace android {
+namespace filterfw {
+
+// Convenience Macro to make copy constructor and assignment operator private
+// (thereby disallowing copying and assigning).
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&); \
+ void operator=(const TypeName&)
+
+// A macro to disallow all the implicit constructors, namely the
+// default constructor, copy constructor and operator= functions.
+#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+ TypeName(); \
+ DISALLOW_COPY_AND_ASSIGN(TypeName)
+
+// STLDeleteContainerPointers()
+// For a range within a container of pointers, calls delete
+// (non-array version) on these pointers.
+// NOTE: for these three functions, we could just implement a DeleteObject
+// functor and then call for_each() on the range and functor, but this
+// requires us to pull in all of algorithm.h, which seems expensive.
+// For hash_[multi]set, it is important that this deletes behind the iterator
+// because the hash_set may call the hash function on the iterator when it is
+// advanced, which could result in the hash function trying to deference a
+// stale pointer.
+template <class ForwardIterator>
+void STLDeleteContainerPointers(ForwardIterator begin,
+ ForwardIterator end) {
+ while (begin != end) {
+ ForwardIterator temp = begin;
+ ++begin;
+ delete *temp;
+ }
+}
+
+// Given an STL container consisting of (key, value) pairs, STLDeleteValues
+// deletes all the "value" components and clears the container. Does nothing
+// in the case it's given a NULL pointer.
+template <class T>
+void STLDeleteValues(T *v) {
+ if (!v) return;
+ for (typename T::iterator i = v->begin(); i != v->end(); ++i) {
+ delete i->second;
+ }
+ v->clear();
+}
+
+// Perform a lookup in a map or hash_map.
+// If the key is present a const pointer to the associated value is returned,
+// otherwise a NULL pointer is returned.
+template <class Collection>
+const typename Collection::value_type::second_type*
+FindOrNull(const Collection& collection,
+ const typename Collection::value_type::first_type& key) {
+ typename Collection::const_iterator it = collection.find(key);
+ if (it == collection.end()) {
+ return 0;
+ }
+ return &it->second;
+}
+
+// A simple class that gives checklist functionality: There are essemtially two
+// operations defined on a CheckList:
+// - Adding a new (unchecked) item.
+// - Checking off an item.
+// When checking off the last remaining item CheckItem() returns true.
+template<typename T>
+class CheckList {
+ public:
+ // Add a new unchecked item. Does nothing if item is already in checklist.
+ void AddItem(const T& item);
+
+ // Check off an item in the checklist. Returns true if all items have been
+ // checked.
+ bool CheckItem(const T& item);
+
+ // Clear the checklist.
+ void Clear() {
+ items_.clear();
+ }
+
+ private:
+ std::set<T> items_;
+};
+
+template<typename T>
+void CheckList<T>::AddItem(const T& item) {
+ if (!ContainsKey(items_, item))
+ items_.insert(item);
+}
+
+template<typename T>
+bool CheckList<T>::CheckItem(const T& item) {
+ typename std::set<T>::iterator iter = items_.find(item);
+ if (iter != items_.end())
+ items_.erase(iter);
+ return items_.empty();
+}
+
+// Perform a lookup in a map or hash_map whose values are pointers.
+// If the key is present a const pointer to the associated value is returned,
+// otherwise a NULL pointer is returned.
+// This function does not distinguish between a missing key and a key mapped
+// to a NULL value.
+template <class Collection>
+const typename Collection::value_type::second_type
+FindPtrOrNull(const Collection& collection,
+ const typename Collection::value_type::first_type& key) {
+ typename Collection::const_iterator it = collection.find(key);
+ if (it == collection.end()) {
+ return 0;
+ }
+ return it->second;
+}
+
+// Test to see if a set, map, hash_set or hash_map contains a particular key.
+// Returns true if the key is in the collection.
+template <typename Collection, typename Key>
+bool ContainsKey(const Collection& collection, const Key& key) {
+ return collection.find(key) != collection.end();
+}
+
+// Insert a new key and value into a map or hash_map.
+// If the key is not present in the map the key and value are
+// inserted, otherwise nothing happens. True indicates that an insert
+// took place, false indicates the key was already present.
+template <class Collection, class Key, class Value>
+bool InsertIfNotPresent(Collection * const collection,
+ const Key& key, const Value& value) {
+ std::pair<typename Collection::iterator, bool> ret =
+ collection->insert(typename Collection::value_type(key, value));
+ return ret.second;
+}
+
+} // namespace filterfw
+} // namespace android
+
+#endif // ANDROID_FILTERFW_BASE_UTILITIES_H
diff --git a/media/mca/filterfw/native/core/geometry.cpp b/media/mca/filterfw/native/core/geometry.cpp
new file mode 100644
index 0000000..677b91d
--- /dev/null
+++ b/media/mca/filterfw/native/core/geometry.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "core/geometry.h"
+
+#include <cmath>
+
+#include "base/logging.h"
+
+namespace android {
+namespace filterfw {
+
+float Point::Length() const {
+ return std::sqrt(x_ * x_ + y_ * y_);
+}
+
+bool Point::ScaleTo(float new_length) {
+ float length = Length();
+ if (length == 0.0f) {
+ return false;
+ }
+ x_ *= new_length / length;
+ y_ *= new_length / length;
+ return true;
+}
+
+float Point::Distance(const Point& p0, const Point& p1) {
+ Point diff = p1 - p0;
+ return diff.Length();
+}
+
+Point Point::operator+(const Point& other) const {
+ Point out;
+ out.x_ = x_ + other.x_;
+ out.y_ = y_ + other.y_;
+ return out;
+}
+
+Point Point::operator-(const Point& other) const {
+ Point out;
+ out.x_ = x_ - other.x_;
+ out.y_ = y_ - other.y_;
+ return out;
+}
+
+Point Point::operator*(float factor) const {
+ Point out;
+ out.x_ = factor * x_;
+ out.y_ = factor * y_;
+ return out;
+}
+
+void Point::Rotate90Clockwise() {
+ const float x = x_;
+ x_ = y_;
+ y_ = -x;
+}
+
+bool Rect::ExpandToAspectRatio(float ratio) {
+ if (width <= 0.0f || height <= 0.0f || ratio <= 0.0f) {
+ return false;
+ }
+
+ const float current_ratio = width / height;
+ if (current_ratio < ratio) {
+ const float dx = width * (ratio / current_ratio - 1.0f);
+ x -= dx / 2.0f;
+ width += dx;
+ } else {
+ const float dy = height * (current_ratio / ratio - 1.0f);
+ y -= dy / 2.0f;
+ height += dy;
+ }
+ return true;
+}
+
+bool Rect::ExpandToMinLength(float length) {
+ if (width <= 0.0f || height <= 0.0f || length <= 0.0f) {
+ return false;
+ }
+
+ const float current_length = width > height ? width : height;
+ if (length > current_length) {
+ const float dx = width * (length / current_length - 1.0f);
+ x -= dx / 2.0f;
+ width += dx;
+ const float dy = height * (length / current_length - 1.0f);
+ y -= dy / 2.0f;
+ height += dy;
+ }
+ return true;
+}
+
+bool Rect::ScaleWithLengthLimit(float factor, float max_length) {
+ if (width <= 0.0f || height <= 0.0f || factor <= 0.0f) {
+ return false;
+ }
+
+ const float current_length = width > height ? width : height;
+ if (current_length >= max_length) {
+ return true;
+ }
+
+ float f = factor;
+ if (current_length * f > max_length) {
+ f *= max_length / (current_length * f);
+ }
+
+ const float dx = width * (f - 1.0f);
+ x -= dx / 2.0f;
+ width += dx;
+ const float dy = height * (f - 1.0f);
+ y -= dy / 2.0f;
+ height += dy;
+ return true;
+}
+
+const Point& Quad::point(int ix) const {
+ ALOG_ASSERT(ix < static_cast<int>(points_.size()), "Access out of bounds");
+ return points_[ix];
+}
+
+} // namespace filterfw
+} // namespace android
diff --git a/media/mca/filterfw/native/core/geometry.h b/media/mca/filterfw/native/core/geometry.h
new file mode 100644
index 0000000..7f89eef
--- /dev/null
+++ b/media/mca/filterfw/native/core/geometry.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FILTERFW_CORE_GEOMETRY_H
+#define ANDROID_FILTERFW_CORE_GEOMETRY_H
+
+#include <vector>
+
+namespace android {
+namespace filterfw {
+
+// This is an initial implementation of some geometrical structures. This is
+// likely to grow and become more sophisticated in the future.
+
+class Point {
+ public:
+ Point() : x_(0.0f), y_(0.0f) {}
+ Point(float x, float y) : x_(x), y_(y) {}
+
+ float x() const { return x_; }
+ float y() const { return y_; }
+
+ float Length() const;
+ bool ScaleTo(float new_length);
+ static float Distance(const Point& p0, const Point& p1);
+
+ // Add more of these as needed:
+ Point operator+(const Point& other) const;
+ Point operator-(const Point& other) const;
+ Point operator*(float factor) const;
+
+ void Rotate90Clockwise();
+
+ private:
+ float x_, y_;
+};
+
+class Quad {
+ public:
+ Quad() : points_(4) {}
+ virtual ~Quad() {}
+
+ Quad(const Point& p0, const Point& p1, const Point& p2, const Point& p3)
+ : points_(4) {
+ points_[0] = p0;
+ points_[1] = p1;
+ points_[2] = p2;
+ points_[3] = p3;
+ }
+
+ const std::vector<Point>& points() const { return points_; }
+ const Point& point(int ix) const;
+
+ protected:
+ std::vector<Point> points_;
+};
+
+struct Rect {
+ float x, y, width, height;
+
+ Rect() {
+ x = y = 0.0f;
+ width = height = 1.0f;
+ }
+
+ Rect(float x, float y, float width, float height) {
+ this->x = x;
+ this->y = y;
+ this->width = width;
+ this->height = height;
+ }
+
+ bool ExpandToAspectRatio(float ratio);
+ bool ExpandToMinLength(float length);
+ bool ScaleWithLengthLimit(float factor, float max_length);
+};
+
+} // namespace filterfw
+} // namespace android
+
+#endif // ANDROID_FILTERFW_CORE_GEOMETRY_H
diff --git a/media/mca/filterfw/native/core/gl_buffer_interface.h b/media/mca/filterfw/native/core/gl_buffer_interface.h
new file mode 100644
index 0000000..24b1db9
--- /dev/null
+++ b/media/mca/filterfw/native/core/gl_buffer_interface.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FILTERFW_CORE_GL_BUFFER_INTERFACE_H
+#define ANDROID_FILTERFW_CORE_GL_BUFFER_INTERFACE_H
+
+#include <GLES2/gl2.h>
+
+namespace android {
+namespace filterfw {
+
+class GLTextureHandle {
+ public:
+ virtual ~GLTextureHandle() { }
+
+ // Returns the held texture id.
+ virtual GLuint GetTextureId() const = 0;
+
+ // Binds the held texture. This may result in creating the texture if it
+ // is not yet available.
+ virtual bool FocusTexture() = 0;
+
+ // Generates the mipmap chain of the held texture. Returns true, iff
+ // generating was successful.
+ virtual bool GenerateMipMap() = 0;
+
+ // Set a texture parameter (see glTextureParameter documentation). Returns
+ // true iff the parameter was set successfully.
+ virtual bool SetTextureParameter(GLenum pname, GLint value) = 0;
+
+ // Returns the texture target used.
+ // Texture Target should be: GL_TEXTURE_2D, GL_TEXTURE_EXTERNAL_OES.
+ virtual GLuint GetTextureTarget() const = 0;
+};
+
+class GLFrameBufferHandle {
+ public:
+ virtual ~GLFrameBufferHandle() { }
+
+ // Returns the held FBO id.
+ virtual GLuint GetFboId() const = 0;
+
+ // Binds the held FBO. This may result in creating the FBO if it
+ // is not yet available.
+ virtual bool FocusFrameBuffer() = 0;
+};
+
+// Interface to instances that hold GL textures and frame-buffer-objects.
+// The GLFrame class implements this interface.
+class GLBufferHandle : public GLTextureHandle, public GLFrameBufferHandle {
+ public:
+ virtual ~GLBufferHandle() { }
+};
+
+} // namespace filterfw
+} // namespace android
+
+#endif // ANDROID_FILTERFW_CORE_GL_BUFFER_INTERFACE_H
diff --git a/media/mca/filterfw/native/core/gl_env.cpp b/media/mca/filterfw/native/core/gl_env.cpp
new file mode 100644
index 0000000..738b8e0
--- /dev/null
+++ b/media/mca/filterfw/native/core/gl_env.cpp
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// #define LOG_NDEBUG 0
+
+#include "base/logging.h"
+#include "base/utilities.h"
+#include "core/gl_env.h"
+#include "core/shader_program.h"
+#include "core/vertex_frame.h"
+#include "system/window.h"
+
+#include <map>
+#include <string>
+#include <EGL/eglext.h>
+
+namespace android {
+namespace filterfw {
+
+GLEnv::GLEnv()
+ : display_(EGL_NO_DISPLAY),
+ context_id_(0),
+ surface_id_(0),
+ max_surface_id_(0),
+ created_context_(false),
+ created_surface_(false),
+ initialized_(false) {
+}
+
+GLEnv::~GLEnv() {
+ // Destroy surfaces
+ for (std::map<int, SurfaceWindowPair>::iterator it = surfaces_.begin();
+ it != surfaces_.end();
+ ++it) {
+ if (it->first != 0 || created_surface_) {
+ eglDestroySurface(display(), it->second.first);
+ if (it->second.second) {
+ it->second.second->Destroy();
+ delete it->second.second;
+ }
+ }
+ }
+
+ // Destroy contexts
+ for (std::map<int, EGLContext>::iterator it = contexts_.begin();
+ it != contexts_.end();
+ ++it) {
+ if (it->first != 0 || created_context_)
+ eglDestroyContext(display(), it->second);
+ }
+
+ // Destroy attached shaders and frames
+ STLDeleteValues(&attached_shaders_);
+ STLDeleteValues(&attached_vframes_);
+
+ // Destroy display
+ if (initialized_)
+ eglTerminate(display());
+
+ // Log error if this did not work
+ if (CheckEGLError("TearDown!"))
+ ALOGE("GLEnv: Error tearing down GL Environment!");
+}
+
+bool GLEnv::IsInitialized() const {
+ return (contexts_.size() > 0 &&
+ surfaces_.size() > 0 &&
+ display_ != EGL_NO_DISPLAY);
+}
+
+bool GLEnv::Deactivate() {
+ eglMakeCurrent(display(), EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ return !CheckEGLError("eglMakeCurrent");
+}
+
+bool GLEnv::Activate() {
+ ALOGV("Activate()");
+ if (display() != eglGetCurrentDisplay() ||
+ context() != eglGetCurrentContext() ||
+ surface() != eglGetCurrentSurface(EGL_DRAW)) {
+ // Make sure we are initialized
+ if (context() == EGL_NO_CONTEXT || surface() == EGL_NO_SURFACE)
+ return false;
+
+ // Make our context current
+ ALOGV("eglMakeCurrent");
+ eglMakeCurrent(display(), surface(), surface(), context());
+
+ return !CheckEGLMakeCurrentError();
+ }
+ return true;
+}
+
+bool GLEnv::SwapBuffers() {
+ const bool result = eglSwapBuffers(display(), surface()) == EGL_TRUE;
+ return !CheckEGLError("eglSwapBuffers") && result;
+}
+
+bool GLEnv::InitWithCurrentContext() {
+ if (IsInitialized())
+ return true;
+
+ display_ = eglGetCurrentDisplay();
+ contexts_[0] = eglGetCurrentContext();
+ surfaces_[0] = SurfaceWindowPair(eglGetCurrentSurface(EGL_DRAW), NULL);
+
+ return (context() != EGL_NO_CONTEXT) &&
+ (display() != EGL_NO_DISPLAY) &&
+ (surface() != EGL_NO_SURFACE);
+}
+
+bool GLEnv::InitWithNewContext() {
+ if (IsInitialized()) {
+ ALOGE("GLEnv: Attempting to reinitialize environment!");
+ return false;
+ }
+
+ display_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (CheckEGLError("eglGetDisplay")) return false;
+
+ EGLint majorVersion;
+ EGLint minorVersion;
+ eglInitialize(display(), &majorVersion, &minorVersion);
+ if (CheckEGLError("eglInitialize")) return false;
+ initialized_ = true;
+
+ // Configure context/surface
+ EGLConfig config;
+ EGLint numConfigs = -1;
+
+ // TODO(renn): Do we need the window bit here?
+ // TODO: Currently choosing the config that includes all
+ // This is not needed if the encoding is not being used
+ EGLint configAttribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_RECORDABLE_ANDROID, EGL_TRUE,
+ EGL_NONE
+ };
+
+ eglChooseConfig(display(), configAttribs, &config, 1, &numConfigs);
+ if (numConfigs < 1) {
+ ALOGE("GLEnv::Init: No suitable EGL configuration found!");
+ return false;
+ }
+
+ // Create dummy surface using a SurfaceTexture
+ surfaceTexture_ = new SurfaceTexture(0);
+ window_ = new SurfaceTextureClient(static_cast<sp<ISurfaceTexture> >(
+ surfaceTexture_->getBufferQueue()));
+
+ surfaces_[0] = SurfaceWindowPair(eglCreateWindowSurface(display(), config, window_.get(), NULL), NULL);
+ if (CheckEGLError("eglCreateWindowSurface")) return false;
+
+ // Create context
+ EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
+ contexts_[0] = eglCreateContext(display(),
+ config,
+ EGL_NO_CONTEXT,
+ context_attribs);
+ if (CheckEGLError("eglCreateContext")) return false;
+
+ created_context_ = created_surface_ = true;
+
+ return true;
+}
+
+bool GLEnv::IsActive() const {
+ ALOGV("IsActive()");
+ return context() == eglGetCurrentContext()
+ && display() == eglGetCurrentDisplay()
+ && surface() == eglGetCurrentSurface(EGL_DRAW);
+}
+
+bool GLEnv::IsContextActive() const {
+ return context() == eglGetCurrentContext();
+}
+
+bool GLEnv::IsAnyContextActive() {
+ return eglGetCurrentContext() != EGL_NO_CONTEXT;
+}
+
+int GLEnv::AddWindowSurface(const EGLSurface& surface, WindowHandle* window_handle) {
+ const int id = ++max_surface_id_;
+ surfaces_[id] = SurfaceWindowPair(surface, window_handle);
+ return id;
+}
+
+int GLEnv::AddSurface(const EGLSurface& surface) {
+ return AddWindowSurface(surface, NULL);
+}
+
+bool GLEnv::SwitchToSurfaceId(int surface_id) {
+ ALOGV("SwitchToSurfaceId");
+ if (surface_id_ != surface_id) {
+ const SurfaceWindowPair* surface = FindOrNull(surfaces_, surface_id);
+ if (surface) {
+ bool wasActive = IsActive();
+ surface_id_ = surface_id;
+ return wasActive ? Activate() : true;
+ }
+ return false;
+ }
+ return true;
+}
+
+bool GLEnv::ReleaseSurfaceId(int surface_id) {
+ if (surface_id > 0) {
+ const SurfaceWindowPair* surface_window_pair = FindOrNull(surfaces_, surface_id);
+ if (surface_window_pair) {
+ if (surface_id_ == surface_id)
+ SwitchToSurfaceId(0);
+ eglDestroySurface(display(), surface_window_pair->first);
+ if (surface_window_pair->second) {
+ surface_window_pair->second->Destroy();
+ delete surface_window_pair->second;
+ }
+ surfaces_.erase(surface_id);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool GLEnv::SetSurfaceTimestamp(int64_t timestamp) {
+ if (surface_id_ > 0) {
+ const SurfaceWindowPair* surface_window_pair = FindOrNull(surfaces_,
+ surface_id_);
+ if (surface_window_pair) {
+ ANativeWindow *window = static_cast<ANativeWindow*>(
+ surface_window_pair->second->InternalHandle());
+ native_window_set_buffers_timestamp(window, timestamp);
+ return true;
+ }
+ }
+ return false;
+}
+
+int GLEnv::FindSurfaceIdForWindow(const WindowHandle* window_handle) {
+ for (std::map<int, SurfaceWindowPair>::iterator it = surfaces_.begin();
+ it != surfaces_.end();
+ ++it) {
+ const WindowHandle* my_handle = it->second.second;
+ if (my_handle && my_handle->Equals(window_handle)) {
+ return it->first;
+ }
+ }
+ return -1;
+}
+
+
+int GLEnv::AddContext(const EGLContext& context) {
+ const int id = contexts_.size();
+ contexts_[id] = context;
+ return id;
+}
+
+bool GLEnv::SwitchToContextId(int context_id) {
+ const EGLContext* context = FindOrNull(contexts_, context_id);
+ if (context) {
+ if (context_id_ != context_id) {
+ context_id_ = context_id;
+ return Activate();
+ }
+ return true;
+ }
+ return false;
+}
+
+void GLEnv::ReleaseContextId(int context_id) {
+ if (context_id > 0) {
+ const EGLContext* context = FindOrNull(contexts_, context_id);
+ if (context) {
+ contexts_.erase(context_id);
+ if (context_id_ == context_id && IsActive())
+ SwitchToContextId(0);
+ eglDestroyContext(display(), *context);
+ }
+ }
+}
+
+bool GLEnv::CheckGLError(const std::string& op) {
+ bool err = false;
+ for (GLint error = glGetError(); error; error = glGetError()) {
+ ALOGE("GL Error: Operation '%s' caused GL error (0x%x)\n",
+ op.c_str(),
+ error);
+ err = true;
+ }
+ return err;
+}
+
+bool GLEnv::CheckEGLError(const std::string& op) {
+ bool err = false;
+ for (EGLint error = eglGetError();
+ error != EGL_SUCCESS;
+ error = eglGetError()) {
+ ALOGE("EGL Error: Operation '%s' caused EGL error (0x%x)\n",
+ op.c_str(),
+ error);
+ err = true;
+ }
+ return err;
+}
+
+bool GLEnv::CheckEGLMakeCurrentError() {
+ bool err = false;
+ for (EGLint error = eglGetError();
+ error != EGL_SUCCESS;
+ error = eglGetError()) {
+ switch (error) {
+ case EGL_BAD_DISPLAY:
+ ALOGE("EGL Error: Attempting to activate context with bad display!");
+ break;
+ case EGL_BAD_SURFACE:
+ ALOGE("EGL Error: Attempting to activate context with bad surface!");
+ break;
+ case EGL_BAD_ACCESS:
+ ALOGE("EGL Error: Attempting to activate context, which is "
+ "already active in another thread!");
+ break;
+ default:
+ ALOGE("EGL Error: Making EGL rendering context current caused "
+ "error: 0x%x\n", error);
+ }
+ err = true;
+ }
+ return err;
+}
+
+GLuint GLEnv::GetCurrentProgram() {
+ GLint result;
+ glGetIntegerv(GL_CURRENT_PROGRAM, &result);
+ ALOG_ASSERT(result >= 0);
+ return static_cast<GLuint>(result);
+}
+
+EGLDisplay GLEnv::GetCurrentDisplay() {
+ return eglGetCurrentDisplay();
+}
+
+int GLEnv::NumberOfComponents(GLenum type) {
+ switch (type) {
+ case GL_BOOL:
+ case GL_FLOAT:
+ case GL_INT:
+ return 1;
+ case GL_BOOL_VEC2:
+ case GL_FLOAT_VEC2:
+ case GL_INT_VEC2:
+ return 2;
+ case GL_INT_VEC3:
+ case GL_FLOAT_VEC3:
+ case GL_BOOL_VEC3:
+ return 3;
+ case GL_BOOL_VEC4:
+ case GL_FLOAT_VEC4:
+ case GL_INT_VEC4:
+ case GL_FLOAT_MAT2:
+ return 4;
+ case GL_FLOAT_MAT3:
+ return 9;
+ case GL_FLOAT_MAT4:
+ return 16;
+ default:
+ return 0;
+ }
+}
+
+void GLEnv::AttachShader(int key, ShaderProgram* shader) {
+ ShaderProgram* existingShader = ShaderWithKey(key);
+ if (existingShader)
+ delete existingShader;
+ attached_shaders_[key] = shader;
+}
+
+void GLEnv::AttachVertexFrame(int key, VertexFrame* frame) {
+ VertexFrame* existingFrame = VertexFrameWithKey(key);
+ if (existingFrame)
+ delete existingFrame;
+ attached_vframes_[key] = frame;
+}
+
+ShaderProgram* GLEnv::ShaderWithKey(int key) {
+ return FindPtrOrNull(attached_shaders_, key);
+}
+
+VertexFrame* GLEnv::VertexFrameWithKey(int key) {
+ return FindPtrOrNull(attached_vframes_, key);
+}
+
+} // namespace filterfw
+} // namespace android
diff --git a/media/mca/filterfw/native/core/gl_env.h b/media/mca/filterfw/native/core/gl_env.h
new file mode 100644
index 0000000..b61785f
--- /dev/null
+++ b/media/mca/filterfw/native/core/gl_env.h
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FILTERFW_CORE_GL_ENV_H
+#define ANDROID_FILTERFW_CORE_GL_ENV_H
+
+#include <string>
+#include <utility>
+#include <map>
+
+#include "base/logging.h"
+#include "base/utilities.h"
+
+#include <GLES2/gl2.h>
+#include <EGL/egl.h>
+
+#include <gui/ISurfaceTexture.h>
+#include <gui/SurfaceTextureClient.h>
+
+namespace android {
+namespace filterfw {
+
+class ShaderProgram;
+class VertexFrame;
+
+class WindowHandle {
+ public:
+ virtual ~WindowHandle() {
+ }
+
+ virtual void Destroy() = 0;
+
+ virtual bool Equals(const WindowHandle* window) const {
+ return InternalHandle() == window->InternalHandle();
+ }
+
+ virtual const void* InternalHandle() const = 0;
+
+ virtual void* InternalHandle() = 0;
+};
+
+// The GLEnv class provides functionality related to the EGL environment, which
+// includes the display, context, and surface. It is possible to either create
+// a new environment or base it off the currently active EGL environment. In
+// order to do the latter, an EGL environment must be setup already (though not
+// necessarily through this class), and have an active display, context, and
+// surface.
+class GLEnv {
+ public:
+ // Constructing and Activating /////////////////////////////////////////////
+ // Constructs a new GLEnv object. This does not create a GL context.
+ GLEnv();
+
+ // Destructor. Tears down and deallocates any GL objects that were created
+ // by this instance.
+ ~GLEnv();
+
+ // Inits a new GL environment, including a new surface and context. You
+ // must call Activate() before performing any GL operations.
+ bool InitWithNewContext();
+
+ // Inits the GL environment from the current GL environment. Use this when
+ // there is already a display, surface and context available (possibly
+ // created by the host application). You do not need to call Activate() as
+ // this context is active already.
+ bool InitWithCurrentContext();
+
+ // Activates the environment, and makes the associated GL context the
+ // current context. Creates the environment, if it has not been created
+ // already. Returns true if the activation was successful.
+ bool Activate();
+
+ // Deactivates the environment. Returns true if the deactivation was
+ // successful. You may want to call this when moving a context to another
+ // thread. In this case, deactivate the GLEnv in the old thread, and
+ // reactivate it in the new thread.
+ bool Deactivate();
+
+ // When rendering to a visible surface, call this to swap between the
+ // offscreen and onscreen buffers. Returns true if the buffer swap was
+ // successful.
+ bool SwapBuffers();
+
+ // Working with Surfaces ///////////////////////////////////////////////////
+
+ // Add a surface to the environment. This surface will now be managed (and
+ // owned) by the GLEnv instance. Returns the id of the surface.
+ int AddSurface(const EGLSurface& surface);
+
+ // Add a window surface to the environment. The window is passed in as
+ // an opaque window handle.
+ // This surface will now be managed (and owned) by the GLEnv instance.
+ // Returns the id of the surface.
+ int AddWindowSurface(const EGLSurface& surface, WindowHandle* window_handle);
+
+ // Switch to the surface with the specified id. This will make the surface
+ // active, if it is not active already. Specify an ID of 0 if you would like
+ // to switch to the default surface. Returns true if successful.
+ bool SwitchToSurfaceId(int surface_id);
+
+ // Release the surface with the specified id. This will deallocate the
+ // surface. If this is the active surface, the environment will switch to
+ // the default surface (0) first. You cannot release the default surface.
+ bool ReleaseSurfaceId(int surface_id);
+
+ // Set the timestamp for the current surface. Must be called
+ // before swapBuffers to associate the timestamp with the frame
+ // resulting from swapBuffers.
+ bool SetSurfaceTimestamp(int64_t timestamp);
+
+ // Looks for a surface with the associated window handle. Returns -1 if no
+ // surface with such a window was found.
+ int FindSurfaceIdForWindow(const WindowHandle* window_handle);
+
+ // Obtain the environment's EGL surface.
+ const EGLSurface& surface() const {
+ return surfaces_.find(surface_id_)->second.first;
+ }
+
+ // Working with Contexts ///////////////////////////////////////////////////
+
+ // Add a context to the environment. This context will now be managed (and
+ // owned) by the GLEnv instance. Returns the id of the context.
+ int AddContext(const EGLContext& context);
+
+ // Switch to the context with the specified id. This will make the context
+ // active, if it is not active already. Specify an ID of 0 if you would like
+ // to switch to the default context. Returns true if successful.
+ bool SwitchToContextId(int context_id);
+
+ // Release the context with the specified id. This will deallocate the
+ // context. If this is the active context, the environment will switch to
+ // the default context (0) first. You cannot release the default context.
+ void ReleaseContextId(int context_id);
+
+ // Obtain the environment's EGL context.
+ const EGLContext& context() const {
+ return contexts_.find(context_id_)->second;
+ }
+
+ // Working with the Display ////////////////////////////////////////////////
+
+ // Obtain the environment's EGL display.
+ const EGLDisplay& display() const {
+ return display_;
+ }
+
+ // Inspecting the environment //////////////////////////////////////////////
+ // Returns true if the environment is active in the current thread.
+ bool IsActive() const;
+
+ // Returns true if the environment's context is active in the curent thread.
+ bool IsContextActive() const;
+
+ // Returns true if there is any EGL context active in the current thread.
+ // This need not be a context created by a GLEnv instance.
+ static bool IsAnyContextActive();
+
+ // Attaching GL objects ////////////////////////////////////////////////////
+
+ // Attach a shader to the environment. The environment takes ownership of
+ // the shader.
+ void AttachShader(int key, ShaderProgram* shader);
+
+ // Attach a vertex frame to the environment. The environment takes ownership
+ // of the frame.
+ void AttachVertexFrame(int key, VertexFrame* frame);
+
+ // Return the shader with the specified key, or NULL if there is no such
+ // shader attached to this environment.
+ ShaderProgram* ShaderWithKey(int key);
+
+ // Return the vertex frame with the specified key, or NULL if there is no
+ // such frame attached to this environment.
+ VertexFrame* VertexFrameWithKey(int key);
+
+ // Static methods //////////////////////////////////////////////////////////
+ // These operate on the currently active environment!
+
+ // Checks if the current environment is in a GL error state. If so, it will
+ // output an error message referencing the given operation string. Returns
+ // true if there was at least one error.
+ static bool CheckGLError(const std::string& operation);
+
+ // Checks if the current environment is in an EGL error state. If so, it
+ // will output an error message referencing the given operation string.
+ // Returns true if there was at least one error.
+ static bool CheckEGLError(const std::string& operation);
+
+ // Get the currently used (shader) program.
+ static GLuint GetCurrentProgram();
+
+ // Get the currently active display.
+ static EGLDisplay GetCurrentDisplay();
+
+ // Returns the number of components for a given GL type. For instance,
+ // returns 4 for vec4, and 16 for mat4.
+ static int NumberOfComponents(GLenum type);
+
+ private:
+ typedef std::pair<EGLSurface, WindowHandle*> SurfaceWindowPair;
+
+ // Initializes a new GL environment.
+ bool Init();
+
+ // Returns true if one of the Inits has been called successfully on this
+ // instance.
+ bool IsInitialized() const;
+
+ // Outputs error messages specific to the operation eglMakeCurrent().
+ // Returns true if there was at least one error.
+ static bool CheckEGLMakeCurrentError();
+
+ // The EGL display, contexts, and surfaces.
+ EGLDisplay display_;
+ std::map<int, EGLContext> contexts_;
+ std::map<int, SurfaceWindowPair> surfaces_;
+
+ // The currently active context and surface ids.
+ int context_id_;
+ int surface_id_;
+
+ // Dummy surface for context
+ sp<ANativeWindow> window_;
+
+ // Dummy SurfaceTexture for context
+ sp<SurfaceTexture> surfaceTexture_;
+
+ // The maximum surface id used.
+ int max_surface_id_;
+
+ // These bools keep track of which objects this GLEnv has created (and
+ // owns).
+ bool created_context_;
+ bool created_surface_;
+ bool initialized_;
+
+ // Attachments that GL objects can add to the environment.
+ std::map<int, ShaderProgram*> attached_shaders_;
+ std::map<int, VertexFrame*> attached_vframes_;
+
+ DISALLOW_COPY_AND_ASSIGN(GLEnv);
+};
+
+} // namespace filterfw
+} // namespace android
+
+#endif // ANDROID_FILTERFW_CORE_GL_ENV_H
diff --git a/media/mca/filterfw/native/core/gl_frame.cpp b/media/mca/filterfw/native/core/gl_frame.cpp
new file mode 100644
index 0000000..0f8b4a1
--- /dev/null
+++ b/media/mca/filterfw/native/core/gl_frame.cpp
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/logging.h"
+
+#include "core/gl_env.h"
+#include "core/gl_frame.h"
+#include "core/shader_program.h"
+
+#include <vector>
+
+namespace android {
+namespace filterfw {
+
+static const int kIdentityShaderKey = 1;
+
+//
+// A GLFrame stores pixel data on the GPU. It uses two kinds of GL data
+// containers for this: Textures and Frame Buffer Objects (FBOs). Textures are
+// used whenever pixel data needs to be read into a shader or the host program,
+// and when pixel data is uploaded to a GLFrame. The FBO is used as a rendering
+// target for shaders.
+//
+
+GLFrame::GLFrame(GLEnv* gl_env)
+ : gl_env_(gl_env),
+ width_(0),
+ height_(0),
+ vp_x_(0),
+ vp_y_(0),
+ vp_width_(0),
+ vp_height_(0),
+ texture_id_(0),
+ fbo_id_(0),
+ texture_target_(GL_TEXTURE_2D),
+ texture_state_(kStateUninitialized),
+ fbo_state_(kStateUninitialized),
+ owns_texture_(false),
+ owns_fbo_(false) {
+ SetDefaultTexParameters();
+}
+
+bool GLFrame::Init(int width, int height) {
+ // Make sure we haven't been initialized already
+ if (width_ == 0 && height_ == 0) {
+ InitDimensions(width, height);
+ return true;
+ }
+ return false;
+}
+
+bool GLFrame::InitWithTexture(GLint texture_id, int width, int height) {
+ texture_id_ = texture_id;
+ texture_state_ = glIsTexture(texture_id) ? kStateComplete : kStateGenerated;
+ InitDimensions(width, height);
+ return true;
+}
+
+bool GLFrame::InitWithFbo(GLint fbo_id, int width, int height) {
+ fbo_id_ = fbo_id;
+ fbo_state_ = glIsFramebuffer(fbo_id) ? kStateComplete : kStateGenerated;
+ texture_state_ = kStateUnmanaged;
+ InitDimensions(width, height);
+ return true;
+}
+
+bool GLFrame::InitWithExternalTexture() {
+ texture_target_ = GL_TEXTURE_EXTERNAL_OES;
+ width_ = 0;
+ height_ = 0;
+ return GenerateTextureName();
+}
+
+void GLFrame::InitDimensions(int width, int height) {
+ width_ = width;
+ height_ = height;
+ vp_width_ = width;
+ vp_height_ = height;
+}
+
+GLFrame::~GLFrame() {
+ // Delete texture
+ if (owns_texture_) {
+ // Bind FBO so that texture is unbound from it during deletion
+ if (fbo_state_ == kStateComplete) {
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_);
+ }
+ glDeleteTextures(1, &texture_id_);
+ }
+
+ // Delete FBO
+ if (owns_fbo_) {
+ glDeleteFramebuffers(1, &fbo_id_);
+ }
+}
+
+bool GLFrame::GenerateMipMap() {
+ if (FocusTexture()) {
+ glGenerateMipmap(GL_TEXTURE_2D);
+ return !GLEnv::CheckGLError("Generating MipMap!");
+ }
+ return false;
+}
+
+bool GLFrame::SetTextureParameter(GLenum pname, GLint value) {
+ if (value != tex_params_[pname]) {
+ if (FocusTexture()) {
+ glTexParameteri(GL_TEXTURE_2D, pname, value);
+ if (!GLEnv::CheckGLError("Setting texture parameter!")) {
+ tex_params_[pname] = value;
+ return true;
+ }
+ }
+ } else {
+ return true;
+ }
+ return false;
+}
+
+bool GLFrame::UpdateTexParameters() {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, tex_params_[GL_TEXTURE_MAG_FILTER]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, tex_params_[GL_TEXTURE_MIN_FILTER]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, tex_params_[GL_TEXTURE_WRAP_S]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, tex_params_[GL_TEXTURE_WRAP_T]);
+ return !GLEnv::CheckGLError("Resetting texture parameters!");
+}
+
+bool GLFrame::TexParametersModifed() {
+ return tex_params_[GL_TEXTURE_MAG_FILTER] != GL_LINEAR
+ || tex_params_[GL_TEXTURE_MIN_FILTER] != GL_LINEAR
+ || tex_params_[GL_TEXTURE_WRAP_S] != GL_CLAMP_TO_EDGE
+ || tex_params_[GL_TEXTURE_WRAP_T] != GL_CLAMP_TO_EDGE;
+}
+
+void GLFrame::SetDefaultTexParameters() {
+ tex_params_[GL_TEXTURE_MAG_FILTER] = GL_LINEAR;
+ tex_params_[GL_TEXTURE_MIN_FILTER] = GL_LINEAR;
+ tex_params_[GL_TEXTURE_WRAP_S] = GL_CLAMP_TO_EDGE;
+ tex_params_[GL_TEXTURE_WRAP_T] = GL_CLAMP_TO_EDGE;
+}
+
+bool GLFrame::ResetTexParameters() {
+ if (TexParametersModifed()) {
+ if (BindTexture()) {
+ SetDefaultTexParameters();
+ return UpdateTexParameters();
+ }
+ return false;
+ }
+ return true;
+}
+
+bool GLFrame::CopyDataTo(uint8_t* buffer, int size) {
+ return (size >= Size())
+ ? CopyPixelsTo(buffer)
+ : false;
+}
+
+bool GLFrame::CopyPixelsTo(uint8_t* buffer) {
+ // Use one of the pixel reading methods below, ordered from most
+ // efficient to least efficient.
+ if (fbo_state_ == kStateComplete)
+ return ReadFboPixels(buffer);
+ else if (texture_state_ == kStateComplete)
+ return ReadTexturePixels(buffer);
+ else
+ return false;
+}
+
+bool GLFrame::WriteData(const uint8_t* data, int data_size) {
+ return (data_size == Size()) ? UploadTexturePixels(data) : false;
+}
+
+bool GLFrame::SetViewport(int x, int y, int width, int height) {
+ vp_x_ = x;
+ vp_y_ = y;
+ vp_width_ = width;
+ vp_height_ = height;
+ return true;
+}
+
+GLFrame* GLFrame::Clone() const {
+ GLFrame* target = new GLFrame(gl_env_);
+ target->Init(width_, height_);
+ target->CopyPixelsFrom(this);
+ return target;
+}
+
+bool GLFrame::CopyPixelsFrom(const GLFrame* frame) {
+ if (frame == this) {
+ return true;
+ } else if (frame && frame->width_ == width_ && frame->height_ == height_) {
+ std::vector<const GLFrame*> sources;
+ sources.push_back(frame);
+ GetIdentity()->Process(sources, this);
+ return true;
+ }
+ return false;
+}
+
+int GLFrame::Size() const {
+ return width_ * height_ * 4;
+}
+
+ShaderProgram* GLFrame::GetIdentity() const {
+ ShaderProgram* stored_shader = gl_env_->ShaderWithKey(kIdentityShaderKey);
+ if (!stored_shader) {
+ stored_shader = ShaderProgram::CreateIdentity(gl_env_);
+ gl_env_->AttachShader(kIdentityShaderKey, stored_shader);
+ }
+ return stored_shader;
+}
+
+bool GLFrame::BindFrameBuffer() const {
+ // Bind the FBO
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_);
+ if (GLEnv::CheckGLError("FBO Binding")) return false;
+
+ // Set viewport
+ glViewport(vp_x_, vp_y_, vp_width_, vp_height_);
+ if (GLEnv::CheckGLError("ViewPort Setup")) return false;
+
+ return true;
+}
+
+bool GLFrame::FocusFrameBuffer() {
+ // Create texture backing if necessary
+ if (texture_state_ == kStateUninitialized) {
+ if (!GenerateTextureName())
+ return false;
+ }
+
+ // Create and bind FBO to texture if necessary
+ if (fbo_state_ != kStateComplete) {
+ if (!GenerateFboName() || !AttachTextureToFbo())
+ return false;
+ }
+
+ // And bind it.
+ return BindFrameBuffer();
+}
+
+bool GLFrame::BindTexture() const {
+ glBindTexture(GL_TEXTURE_2D, texture_id_);
+ return !GLEnv::CheckGLError("Texture Binding");
+}
+
+GLuint GLFrame::GetTextureId() const {
+ return texture_id_;
+}
+
+// Returns the held FBO id. Only call this if the GLFrame holds an FBO. You
+// can check this by calling HoldsFbo().
+GLuint GLFrame::GetFboId() const {
+ return fbo_id_;
+}
+
+bool GLFrame::FocusTexture() {
+ // Make sure we have a texture
+ if (!GenerateTextureName())
+ return false;
+
+ // Bind the texture
+ if (!BindTexture())
+ return false;
+
+ return !GLEnv::CheckGLError("Texture Binding");
+}
+
+bool GLFrame::GenerateTextureName() {
+ if (texture_state_ == kStateUninitialized) {
+ // Make sure texture not in use already
+ if (glIsTexture(texture_id_)) {
+ ALOGE("GLFrame: Cannot generate texture id %d, as it is in use already!", texture_id_);
+ return false;
+ }
+
+ // Generate the texture
+ glGenTextures (1, &texture_id_);
+ if (GLEnv::CheckGLError("Texture Generation"))
+ return false;
+ texture_state_ = kStateGenerated;
+ owns_texture_ = true;
+ }
+
+ return true;
+}
+
+bool GLFrame::AllocateTexture() {
+ // Allocate or re-allocate (if texture was deleted externally).
+ if (texture_state_ == kStateGenerated || TextureWasDeleted()) {
+ LOG_FRAME("GLFrame: Allocating texture: %d", texture_id_);
+ glBindTexture(GL_TEXTURE_2D, texture_id_);
+ glTexImage2D(GL_TEXTURE_2D,
+ 0,
+ GL_RGBA,
+ width_,
+ height_,
+ 0,
+ GL_RGBA,
+ GL_UNSIGNED_BYTE,
+ NULL);
+ if (!GLEnv::CheckGLError("Texture Allocation")) {
+ UpdateTexParameters();
+ texture_state_ = kStateComplete;
+ }
+ }
+ return texture_state_ == kStateComplete;
+}
+
+bool GLFrame::TextureWasDeleted() const {
+ return texture_state_ == kStateComplete && !glIsTexture(texture_id_);
+}
+
+bool GLFrame::GenerateFboName() {
+ if (fbo_state_ == kStateUninitialized) {
+ // Make sure FBO not in use already
+ if (glIsFramebuffer(fbo_id_)) {
+ ALOGE("GLFrame: Cannot generate FBO id %d, as it is in use already!", fbo_id_);
+ return false;
+ }
+
+ // Create FBO
+ glGenFramebuffers(1, &fbo_id_);
+ if (GLEnv::CheckGLError("FBO Generation"))
+ return false;
+ fbo_state_ = kStateGenerated;
+ owns_fbo_ = true;
+ }
+
+ return true;
+}
+
+bool GLFrame::ReadFboPixels(uint8_t* pixels) const {
+ if (fbo_state_ == kStateComplete) {
+ BindFrameBuffer();
+ glReadPixels(0,
+ 0,
+ width_,
+ height_,
+ GL_RGBA,
+ GL_UNSIGNED_BYTE,
+ pixels);
+ return !GLEnv::CheckGLError("FBO Pixel Readout");
+ }
+ return false;
+}
+
+bool GLFrame::ReadTexturePixels(uint8_t* pixels) const {
+ // Read pixels from texture if we do not have an FBO
+ // NOTE: OpenGL ES does NOT support glGetTexImage() for reading out texture
+ // data. The only way for us to get texture data is to create a new FBO and
+ // render the current texture frame into it. As this is quite inefficient,
+ // and unnecessary (this can only happen if the user is reading out data
+ // that was just set, and not run through a filter), we warn the user about
+ // this here.
+ ALOGW("Warning: Reading pixel data from unfiltered GL frame. This is highly "
+ "inefficient. Please consider using your original pixel buffer "
+ "instead!");
+
+ // Create source frame set (unfortunately this requires an ugly const-cast,
+ // as we need to wrap ourselves in a frame-set. Still, as this set is used
+ // as input only, we are certain we will not be modified).
+ std::vector<const GLFrame*> sources;
+ sources.push_back(this);
+
+ // Create target frame
+ GLFrame target(gl_env_);
+ target.Init(width_, height_);
+
+ // Render the texture to the target
+ GetIdentity()->Process(sources, &target);
+
+ // Get the pixel data
+ return target.ReadFboPixels(pixels);
+}
+
+bool GLFrame::AttachTextureToFbo() {
+ // Check FBO and texture state. We do not do anything if we are not managing the texture.
+ if (fbo_state_ == kStateComplete || texture_state_ == kStateUnmanaged) {
+ return true;
+ } else if (fbo_state_ != kStateGenerated) {
+ ALOGE("Attempting to attach texture to FBO with no FBO in place!");
+ return false;
+ }
+
+ // If texture has been generated, make sure it is allocated.
+ if (!AllocateTexture())
+ return false;
+
+ // Bind the frame buffer, and check if we it is already bound
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_);
+
+ // Bind the texture to the frame buffer
+ LOG_FRAME("Attaching tex %d w %d h %d to fbo %d", texture_id_, width_, height_, fbo_id_);
+ glFramebufferTexture2D(GL_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D,
+ texture_id_,
+ 0);
+
+ // Cleanup
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ if (GLEnv::CheckGLError("Texture Binding to FBO"))
+ return false;
+ else
+ fbo_state_ = kStateComplete;
+
+ return true;
+}
+
+bool GLFrame::ReattachTextureToFbo() {
+ return (fbo_state_ == kStateGenerated) ? AttachTextureToFbo() : true;
+}
+
+bool GLFrame::DetachTextureFromFbo() {
+ if (fbo_state_ == kStateComplete && texture_state_ == kStateComplete) {
+ LOG_FRAME("Detaching tex %d w %d h %d from fbo %d", texture_id_, width_, height_, fbo_id_);
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_);
+ glFramebufferTexture2D(GL_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D,
+ 0,
+ 0);
+ if (GLEnv::CheckGLError("Detaching texture to FBO"))
+ return false;
+ else
+ fbo_state_ = kStateGenerated;
+ }
+ return true;
+}
+
+bool GLFrame::UploadTexturePixels(const uint8_t* pixels) {
+ // Bind the texture object
+ FocusTexture();
+
+ // Load mipmap level 0
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width_, height_,
+ 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+
+ // Set the user specified texture parameters
+ UpdateTexParameters();
+
+ if (GLEnv::CheckGLError("Texture Pixel Upload"))
+ return false;
+
+ texture_state_ = kStateComplete;
+ return true;
+}
+
+} // namespace filterfw
+} // namespace android
diff --git a/media/mca/filterfw/native/core/gl_frame.h b/media/mca/filterfw/native/core/gl_frame.h
new file mode 100644
index 0000000..f2a1ad5
--- /dev/null
+++ b/media/mca/filterfw/native/core/gl_frame.h
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FILTERFW_CORE_GL_FRAME_H
+#define ANDROID_FILTERFW_CORE_GL_FRAME_H
+
+#include <map>
+
+#include <GLES2/gl2.h>
+
+#include "core/gl_buffer_interface.h"
+
+namespace android {
+namespace filterfw {
+
+class GLEnv;
+class ShaderProgram;
+
+// A GLFrame stores pixel data on the GPU. While pixel data may be uploaded to
+// a GLFrame and also read out of a GLFrame (access in place is not supported),
+// it is strongly recommended to use ShaderProgram objects for any kind of
+// processing from one GLFrame to another.
+class GLFrame : public GLBufferHandle {
+ public:
+ // Create an empty GL frame in the specified GL environment. Note, that the GLFrame does NOT
+ // take ownership. The caller must make sure the GLEnv stays valid as long as the GLFrame is
+ // alive.
+ GLFrame(GLEnv* gl_env);
+
+ // Deallocate a GL frame.
+ ~GLFrame();
+
+ // Initialize a GL frame to the given width, height, format. Also specify
+ // whether this is a read-only GL frame or not.
+ bool Init(int width, int height);
+
+ // Initialize as using an external texture.
+ bool InitWithExternalTexture();
+
+ // Initialize using an existing texture.
+ bool InitWithTexture(GLint texture_id, int width, int height);
+
+ // Initialize using an existing FBO.
+ bool InitWithFbo(GLint fbo_id, int width, int height);
+
+ // Write the data with the given size in bytes to the frame. The frame size must match the
+ // size of the data.
+ bool WriteData(const uint8_t* data, int size);
+
+ // Copies the frame data to the given buffer.
+ bool CopyDataTo(uint8_t* buffer, int size);
+
+ // Copies the pixels from another GL frame to this frame.
+ bool CopyPixelsFrom(const GLFrame* frame);
+
+ // Returns the size of the buffer in bytes.
+ int Size() const;
+
+ // Clone the current frame by creating a new GL frame and copying all data to it.
+ GLFrame* Clone() const;
+
+ // Returns the held texture id. Only call this if the GLFrame holds a
+ // texture. You can check this by calling HoldsTexture().
+ // Note, that a texture is created only when needed. If you are creating a
+ // new GLFrame, and you need it to be bound to a texture, upload (zeroed)
+ // data to it first, before calling this method.
+ GLuint GetTextureId() const;
+
+ // Returns the held FBO id. Only call this if the GLFrame holds an FBO. You
+ // can check this by calling HoldsFbo().
+ GLuint GetFboId() const;
+
+ // Returns the texture target: GL_TEXTURE_2D or GL_TEXTURE_EXTERNAL_OES.
+ GLuint GetTextureTarget() const {
+ return texture_target_;
+ }
+
+ // Set the viewport that will be used when focusing this frame for rendering. Defaults to
+ // the dimensions of the frame.
+ bool SetViewport(int x, int y, int width, int height);
+
+ // Binds the held texture. This may result in creating the texture if it
+ // is not yet available.
+ bool FocusTexture();
+
+ // Binds the held FBO. This may result in creating the FBO if it
+ // is not yet available.
+ bool FocusFrameBuffer();
+
+ // Generates the mipmap chain of the held texture. Returns true, iff
+ // generating was successful.
+ bool GenerateMipMap();
+
+ // Set a texture parameter (see glTextureParameter documentation). Returns
+ // true iff the parameter was set successfully.
+ bool SetTextureParameter(GLenum pname, GLint value);
+
+ // Reset any modifed texture parameters.
+ bool ResetTexParameters();
+
+ // Detaches the internal texture from the FBO.
+ bool DetachTextureFromFbo();
+
+ // Reattaches the internal texture to the FBO after detachment.
+ bool ReattachTextureToFbo();
+
+ private:
+ // Type to keep track of texture and FBO states
+ enum GLObjectState {
+ kStateUnmanaged, // We do not manage this object (externally managed)
+ kStateUninitialized, // Not yet initialized
+ kStateGenerated, // Tex/FBO id is generated
+ kStateComplete // FBO has valid attachment / Tex has valid pixel data
+ };
+
+ // Sets the frame and viewport dimensions.
+ void InitDimensions(int width, int height);
+
+ // Generates the internal texture name.
+ bool GenerateTextureName();
+
+ // Allocates the internal texture.
+ bool AllocateTexture();
+
+ // Creates the internal FBO.
+ bool GenerateFboName();
+
+ // Copies pixels from texture or FBO to the specified buffer.
+ bool CopyPixelsTo(uint8_t* buffer);
+
+ // Reads the pixels from the internal texture to the given buffer.
+ bool ReadTexturePixels(uint8_t* pixels) const;
+
+ // Reads the pixels from the internal FBO to the given buffer.
+ bool ReadFboPixels(uint8_t* pixels) const;
+
+ // Writes the specified pixels to the internal texture.
+ bool UploadTexturePixels(const uint8_t* pixels);
+
+ // Binds the internal texture.
+ bool BindTexture() const;
+
+ // Binds the internal FBO.
+ bool BindFrameBuffer() const;
+
+ // Attaches the internal texture to the internal FBO.
+ bool AttachTextureToFbo();
+
+ // Update the texture parameters to the user specified parameters
+ bool UpdateTexParameters();
+
+ // Returns true if the current texture parameters are not the GLES2
+ // default parameters.
+ bool TexParametersModifed();
+
+ // Sets the current texture parameters to the GLES2 default
+ // parameters. This still requires a call to UpdateTexParameters()
+ // for the changes to take effect.
+ void SetDefaultTexParameters();
+
+ // Returns true if the texture we assume to be allocated has been
+ // deleted externally. In this case we assume the texture name is
+ // still valid (otherwise we were provided with a bad texture id).
+ bool TextureWasDeleted() const;
+
+ // Get the (cached) identity shader.
+ ShaderProgram* GetIdentity() const;
+
+ // The GL environment this frame belongs to
+ GLEnv* gl_env_;
+
+ // The width, height and format of the frame
+ int width_;
+ int height_;
+
+ // The viewport dimensions
+ int vp_x_;
+ int vp_y_;
+ int vp_width_;
+ int vp_height_;
+
+ // The texture and FBO ids
+ GLuint texture_id_;
+ GLuint fbo_id_;
+
+ // The texture target: GL_TEXTURE_2D or GL_TEXTURE_EXTERNAL_OES
+ GLuint texture_target_;
+
+ // Flags whether or not frame holds a texture and FBO
+ GLObjectState texture_state_;
+ GLObjectState fbo_state_;
+
+ // Set of current texture parameters
+ std::map<GLenum, GLint> tex_params_;
+
+ // Flag whether frame owns the texture and FBO
+ bool owns_texture_;
+ bool owns_fbo_;
+};
+
+} // namespace filterfw
+} // namespace android
+
+#endif // ANDROID_FILTERFW_CORE_GL_FRAME_H
diff --git a/media/mca/filterfw/native/core/native_frame.cpp b/media/mca/filterfw/native/core/native_frame.cpp
new file mode 100644
index 0000000..957ecb1
--- /dev/null
+++ b/media/mca/filterfw/native/core/native_frame.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "core/native_frame.h"
+
+namespace android {
+namespace filterfw {
+
+NativeFrame::NativeFrame(int size) : data_(NULL), size_(size), capacity_(size) {
+ data_ = capacity_ == 0 ? NULL : new uint8_t[capacity_];
+}
+
+NativeFrame::~NativeFrame() {
+ delete[] data_;
+}
+
+bool NativeFrame::WriteData(const uint8_t* data, int offset, int size) {
+ if (size_ >= (offset + size)) {
+ memcpy(data_ + offset, data, size);
+ return true;
+ }
+ return false;
+}
+
+bool NativeFrame::SetData(uint8_t* data, int size) {
+ delete[] data_;
+ size_ = capacity_ = size;
+ data_ = data;
+ return true;
+}
+
+NativeFrame* NativeFrame::Clone() const {
+ NativeFrame* result = new NativeFrame(size_);
+ if (data_)
+ result->WriteData(data_, 0, size_);
+ return result;
+}
+
+bool NativeFrame::Resize(int newSize) {
+ if (newSize <= capacity_ && newSize >= 0) {
+ size_ = newSize;
+ return true;
+ }
+ return false;
+}
+
+} // namespace filterfw
+} // namespace android
diff --git a/media/mca/filterfw/native/core/native_frame.h b/media/mca/filterfw/native/core/native_frame.h
new file mode 100644
index 0000000..0d335b3
--- /dev/null
+++ b/media/mca/filterfw/native/core/native_frame.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FILTERFW_CORE_NATIVE_FRAME_H
+#define ANDROID_FILTERFW_CORE_NATIVE_FRAME_H
+
+#include "base/utilities.h"
+
+namespace android {
+namespace filterfw {
+
+// A NativeFrame stores data in a memory buffer (on the heap). It is used for
+// data processing on the CPU.
+class NativeFrame {
+ public:
+ // Create an empty native frame.
+ NativeFrame(int size);
+
+ ~NativeFrame();
+
+ // Set the frame data and size in bytes. The NativeFrame object takes ownership of the data.
+ // To copy data into an existing frame, use WriteData().
+ bool SetData(uint8_t* data, int size);
+
+ // Write the specified data of the given size to the frame at the specified offset. The
+ // receiver must be large enough to hold the data.
+ bool WriteData(const uint8_t* data, int offset, int size);
+
+ // Returns a pointer to the data, or NULL if no data was set.
+ const uint8_t* Data() const {
+ return data_;
+ }
+
+ // Returns a non-const pointer to the data, or NULL if no data was set.
+ uint8_t* MutableData() {
+ return data_;
+ }
+
+ // Resize the frame. You can only resize to a size that fits within the frame's capacity.
+ // Returns true if the resize was successful.
+ bool Resize(int newSize);
+
+ // Returns the size of the frame in bytes.
+ int Size() {
+ return size_;
+ }
+
+ // Returns the capacity of the frame in bytes.
+ int Capacity() {
+ return capacity_;
+ }
+
+ // Returns a new native frame
+ NativeFrame* Clone() const;
+
+ private:
+ // Pointer to the data. Owned by the frame.
+ uint8_t* data_;
+
+ // Size of data buffer in bytes.
+ int size_;
+
+ // Capacity of data buffer in bytes.
+ int capacity_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeFrame);
+};
+
+} // namespace filterfw
+} // namespace android
+
+#endif // ANDROID_FILTERFW_CORE_NATIVE_FRAME_H
diff --git a/media/mca/filterfw/native/core/native_program.cpp b/media/mca/filterfw/native/core/native_program.cpp
new file mode 100644
index 0000000..c46c46f
--- /dev/null
+++ b/media/mca/filterfw/native/core/native_program.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dlfcn.h>
+
+#include "base/logging.h"
+#include "core/native_frame.h"
+#include "core/native_program.h"
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace filterfw {
+
+NativeProgram::NativeProgram()
+ : lib_handle_(NULL),
+ init_function_(NULL),
+ setvalue_function_(NULL),
+ getvalue_function_(NULL),
+ process_function_(NULL),
+ reset_function_(NULL),
+ teardown_function_(NULL),
+ user_data_(NULL) {
+}
+
+NativeProgram::~NativeProgram() {
+ if (lib_handle_)
+ dlclose(lib_handle_);
+}
+
+bool NativeProgram::OpenLibrary(const std::string& lib_name) {
+ if (!lib_handle_) {
+ lib_handle_ = dlopen(lib_name.c_str(), RTLD_NOW);
+ if (!lib_handle_) {
+ ALOGE("NativeProgram: Error opening library: '%s': %s", lib_name.c_str(), dlerror());
+ return false;
+ }
+ return true;
+ }
+ return false;
+}
+
+bool NativeProgram::BindProcessFunction(const std::string& func_name) {
+ if (!lib_handle_)
+ return false;
+ process_function_ = reinterpret_cast<ProcessFunctionPtr>(dlsym(lib_handle_, func_name.c_str()));
+ if (!process_function_) {
+ ALOGE("NativeProgram: Could not find process function symbol: '%s'!", func_name.c_str());
+ return false;
+ }
+ return true;
+}
+
+bool NativeProgram::BindInitFunction(const std::string& func_name) {
+ if (!lib_handle_)
+ return false;
+ init_function_ = reinterpret_cast<InitFunctionPtr>(dlsym(lib_handle_, func_name.c_str()));
+ return init_function_ != NULL;
+}
+
+bool NativeProgram::BindSetValueFunction(const std::string& func_name) {
+ if (!lib_handle_)
+ return false;
+ setvalue_function_ = reinterpret_cast<SetValueFunctionPtr>(dlsym(lib_handle_, func_name.c_str()));
+ return setvalue_function_ != NULL;
+}
+
+bool NativeProgram::BindGetValueFunction(const std::string& func_name) {
+ if (!lib_handle_)
+ return false;
+ getvalue_function_ = reinterpret_cast<GetValueFunctionPtr>(dlsym(lib_handle_, func_name.c_str()));
+ return getvalue_function_ != NULL;
+}
+
+bool NativeProgram::BindResetFunction(const std::string& func_name) {
+ if (!lib_handle_)
+ return false;
+ reset_function_ = reinterpret_cast<ResetFunctionPtr>(dlsym(lib_handle_, func_name.c_str()));
+ return reset_function_ != NULL;
+}
+
+bool NativeProgram::BindTeardownFunction(const std::string& func_name) {
+ if (!lib_handle_)
+ return false;
+ teardown_function_ = reinterpret_cast<TeardownFunctionPtr>(dlsym(lib_handle_, func_name.c_str()));
+ return teardown_function_ != NULL;
+}
+
+bool NativeProgram::CallProcess(const std::vector<const char*>& inputs,
+ const std::vector<int>& input_sizes,
+ char* output,
+ int output_size) {
+ if (process_function_) {
+ return process_function_(const_cast<const char**>(&inputs[0]),
+ &input_sizes[0],
+ inputs.size(),
+ output,
+ output_size,
+ user_data_) == 1;
+ }
+ return false;
+}
+
+bool NativeProgram::CallInit() {
+ if (init_function_) {
+ init_function_(&user_data_);
+ return true;
+ }
+ return false;
+}
+
+bool NativeProgram::CallSetValue(const std::string& key, const std::string& value) {
+ if (setvalue_function_) {
+ setvalue_function_(key.c_str(), value.c_str(), user_data_);
+ return true;
+ }
+ return false;
+}
+
+std::string NativeProgram::CallGetValue(const std::string& key) {
+ if (getvalue_function_) {
+ static const int buffer_size = 1024;
+ char result[buffer_size];
+ result[buffer_size - 1] = '\0';
+ getvalue_function_(key.c_str(), result, buffer_size, user_data_);
+ return std::string(result);
+ }
+ return std::string();
+}
+
+bool NativeProgram::CallReset() {
+ if (reset_function_) {
+ reset_function_(user_data_);
+ return true;
+ }
+ return false;
+}
+
+bool NativeProgram::CallTeardown() {
+ if (teardown_function_) {
+ teardown_function_(user_data_);
+ return true;
+ }
+ return false;
+}
+
+} // namespace filterfw
+} // namespace android
diff --git a/media/mca/filterfw/native/core/native_program.h b/media/mca/filterfw/native/core/native_program.h
new file mode 100644
index 0000000..ce704af
--- /dev/null
+++ b/media/mca/filterfw/native/core/native_program.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FILTERFW_CORE_NATIVE_PROGRAM_H
+#define ANDROID_FILTERFW_CORE_NATIVE_PROGRAM_H
+
+#include <vector>
+#include <string>
+
+#include "base/utilities.h"
+
+namespace android {
+namespace filterfw {
+
+class NativeFrame;
+
+typedef void (*InitFunctionPtr)(void**);
+typedef void (*SetValueFunctionPtr)(const char*, const char*, void*);
+typedef void (*GetValueFunctionPtr)(const char*, char*, int, void*);
+typedef int (*ProcessFunctionPtr)(const char**, const int*, int, char*, int, void*);
+typedef void (*ResetFunctionPtr)(void*);
+typedef void (*TeardownFunctionPtr)(void*);
+
+class NativeProgram {
+ public:
+ // Create an empty native frame.
+ NativeProgram();
+
+ ~NativeProgram();
+
+ bool OpenLibrary(const std::string& lib_name);
+
+ bool BindInitFunction(const std::string& func_name);
+ bool BindSetValueFunction(const std::string& func_name);
+ bool BindGetValueFunction(const std::string& func_name);
+ bool BindProcessFunction(const std::string& func_name);
+ bool BindResetFunction(const std::string& func_name);
+ bool BindTeardownFunction(const std::string& func_name);
+
+ bool CallInit();
+ bool CallSetValue(const std::string& key, const std::string& value);
+ std::string CallGetValue(const std::string& key);
+ bool CallProcess(const std::vector<const char*>& inputs,
+ const std::vector<int>& input_sizes,
+ char* output,
+ int output_size);
+ bool CallReset();
+ bool CallTeardown();
+
+ private:
+ // Pointer to the data. Owned by the frame.
+ void* lib_handle_;
+
+ // The function pointers to the native function implementations.
+ InitFunctionPtr init_function_;
+ SetValueFunctionPtr setvalue_function_;
+ GetValueFunctionPtr getvalue_function_;
+ ProcessFunctionPtr process_function_;
+ ResetFunctionPtr reset_function_;
+ TeardownFunctionPtr teardown_function_;
+
+ // Pointer to user data
+ void* user_data_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeProgram);
+};
+
+} // namespace filterfw
+} // namespace android
+
+#endif // ANDROID_FILTERFW_CORE_NATIVE_PROGRAM_H
diff --git a/media/mca/filterfw/native/core/shader_program.cpp b/media/mca/filterfw/native/core/shader_program.cpp
new file mode 100644
index 0000000..d92eb31
--- /dev/null
+++ b/media/mca/filterfw/native/core/shader_program.cpp
@@ -0,0 +1,1122 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/logging.h"
+
+#include "core/geometry.h"
+#include "core/gl_buffer_interface.h"
+#include "core/gl_env.h"
+#include "core/gl_frame.h"
+#include "core/shader_program.h"
+#include "core/vertex_frame.h"
+
+#include <string>
+#include <sstream>
+#include <vector>
+
+namespace android {
+namespace filterfw {
+
+// VBO attachment keys
+static const int kDefaultVboKey = 1;
+
+static const char* s_default_vertex_shader_source_ =
+ "attribute vec4 a_position;\n"
+ "attribute vec2 a_texcoord;\n"
+ "varying vec2 v_texcoord;\n"
+ "void main() {\n"
+ " gl_Position = a_position;\n"
+ " v_texcoord = a_texcoord;\n"
+ "}\n";
+
+// Helper Functions ////////////////////////////////////////////////////////////
+// Maps coordinates x,y in the unit rectangle over to the quadrangle specified
+// by the four points in b. The result coordinates are written to xt and yt.
+static void GetTileCoords(const float* b, float x, float y, float* xt, float* yt) {
+ const float w0 = (1.0f - x) * (1.0f - y);
+ const float w1 = x * (1.0f - y);
+ const float w2 = (1.0f - x) * y;
+ const float w3 = x * y;
+
+ *xt = w0 * b[0] + w1 * b[2] + w2 * b[4] + w3 * b[6];
+ *yt = w0 * b[1] + w1 * b[3] + w2 * b[5] + w3 * b[7];
+}
+
+static inline float AdjustRatio(float current, float next) {
+ return (current + next) / 2.0;
+}
+
+// VertexAttrib implementation /////////////////////////////////////////////////
+ShaderProgram::VertexAttrib::VertexAttrib()
+ : is_const(true),
+ index(-1),
+ normalized(false),
+ stride(0),
+ components(0),
+ offset(0),
+ type(GL_FLOAT),
+ vbo(0),
+ values(NULL),
+ owned_data(NULL) {
+}
+
+// ShaderProgram implementation ////////////////////////////////////////////////
+ShaderProgram::ShaderProgram(GLEnv* gl_env, const std::string& fragment_shader)
+ : fragment_shader_source_(fragment_shader),
+ vertex_shader_source_(s_default_vertex_shader_source_),
+ fragment_shader_(0),
+ vertex_shader_(0),
+ program_(0),
+ gl_env_(gl_env),
+ base_texture_unit_(GL_TEXTURE0),
+ source_coords_(NULL),
+ target_coords_(NULL),
+ manage_coordinates_(false),
+ tile_x_count_(1),
+ tile_y_count_(1),
+ vertex_count_(4),
+ draw_mode_(GL_TRIANGLE_STRIP),
+ clears_(false),
+ blending_(false),
+ sfactor_(GL_SRC_ALPHA),
+ dfactor_(GL_ONE_MINUS_SRC_ALPHA) {
+ SetDefaultCoords();
+}
+
+ShaderProgram::ShaderProgram(GLEnv* gl_env,
+ const std::string& vertex_shader,
+ const std::string& fragment_shader)
+ : fragment_shader_source_(fragment_shader),
+ vertex_shader_source_(vertex_shader),
+ fragment_shader_(0),
+ vertex_shader_(0),
+ program_(0),
+ gl_env_(gl_env),
+ base_texture_unit_(GL_TEXTURE0),
+ source_coords_(NULL),
+ target_coords_(NULL),
+ manage_coordinates_(false),
+ tile_x_count_(1),
+ tile_y_count_(1),
+ vertex_count_(4),
+ draw_mode_(GL_TRIANGLE_STRIP),
+ clears_(false),
+ blending_(false),
+ sfactor_(GL_SRC_ALPHA),
+ dfactor_(GL_ONE_MINUS_SRC_ALPHA) {
+ SetDefaultCoords();
+}
+
+ShaderProgram::~ShaderProgram() {
+ // Delete our vertex data
+ delete[] source_coords_;
+ delete[] target_coords_;
+
+ // Delete any owned attribute data
+ VertexAttribMap::const_iterator iter;
+ for (iter = attrib_values_.begin(); iter != attrib_values_.end(); ++iter) {
+ const VertexAttrib& attrib = iter->second;
+ if (attrib.owned_data)
+ delete[] attrib.owned_data;
+ }
+}
+
+void ShaderProgram::SetDefaultCoords() {
+ if (!source_coords_)
+ source_coords_ = new float[8];
+ if (!target_coords_)
+ target_coords_ = new float[8];
+
+ source_coords_[0] = 0.0f;
+ source_coords_[1] = 0.0f;
+ source_coords_[2] = 1.0f;
+ source_coords_[3] = 0.0f;
+ source_coords_[4] = 0.0f;
+ source_coords_[5] = 1.0f;
+ source_coords_[6] = 1.0f;
+ source_coords_[7] = 1.0f;
+
+ target_coords_[0] = -1.0f;
+ target_coords_[1] = -1.0f;
+ target_coords_[2] = 1.0f;
+ target_coords_[3] = -1.0f;
+ target_coords_[4] = -1.0f;
+ target_coords_[5] = 1.0f;
+ target_coords_[6] = 1.0f;
+ target_coords_[7] = 1.0f;
+
+}
+
+ShaderProgram* ShaderProgram::CreateIdentity(GLEnv* gl_env) {
+ const char* s_id_fragment_shader =
+ "precision mediump float;\n"
+ "uniform sampler2D tex_sampler_0;\n"
+ "varying vec2 v_texcoord;\n"
+ "void main() {\n"
+ " gl_FragColor = texture2D(tex_sampler_0, v_texcoord);\n"
+ "}\n";
+ ShaderProgram* result = new ShaderProgram(gl_env, s_id_fragment_shader);
+ result->CompileAndLink();
+ return result;
+}
+
+bool ShaderProgram::IsVarValid(ProgramVar var) {
+ return var != -1;
+}
+
+bool ShaderProgram::Process(const std::vector<const GLTextureHandle*>& input,
+ GLFrameBufferHandle* output) {
+ // TODO: This can be optimized: If the input and output are the same, as in
+ // the last iteration (typical of a multi-pass filter), a lot of this setup
+ // may be skipped.
+
+ // Abort if program did not successfully compile and link
+ if (!IsExecutable()) {
+ ALOGE("ShaderProgram: unexecutable program!");
+ return false;
+ }
+
+ // Focus the FBO of the output
+ if (!output->FocusFrameBuffer()) {
+ ALOGE("Unable to focus frame buffer");
+ return false;
+ }
+
+ // Get all required textures
+ std::vector<GLuint> textures;
+ std::vector<GLenum> targets;
+ for (unsigned i = 0; i < input.size(); ++i) {
+ // Get the current input frame and make sure it is a GL frame
+ if (input[i]) {
+ // Get the texture bound to that frame
+ const GLuint tex_id = input[i]->GetTextureId();
+ const GLenum target = input[i]->GetTextureTarget();
+ if (tex_id == 0) {
+ ALOGE("ShaderProgram: invalid texture id at input: %d!", i);
+ return false;
+ }
+ textures.push_back(tex_id);
+ targets.push_back(target);
+ }
+ }
+
+ // And render!
+ if (!RenderFrame(textures, targets)) {
+ ALOGE("Unable to render frame");
+ return false;
+ }
+ return true;
+}
+
+bool ShaderProgram::Process(const std::vector<const GLFrame*>& input, GLFrame* output) {
+ std::vector<const GLTextureHandle*> textures(input.size());
+ std::copy(input.begin(), input.end(), textures.begin());
+ return Process(textures, output);
+}
+
+void ShaderProgram::SetSourceRect(float x, float y, float width, float height) {
+ Quad quad(Point(x, y),
+ Point(x + width, y),
+ Point(x, y + height),
+ Point(x + width, y + height));
+ SetSourceRegion(quad);
+}
+
+void ShaderProgram::SetSourceRegion(const Quad& quad) {
+ int index = 0;
+ for (int i = 0; i < 4; ++i, index += 2) {
+ source_coords_[index] = quad.point(i).x();
+ source_coords_[index+1] = quad.point(i).y();
+ }
+}
+
+void ShaderProgram::SetTargetRect(float x, float y, float width, float height) {
+ Quad quad(Point(x, y),
+ Point(x + width, y),
+ Point(x, y + height),
+ Point(x + width, y + height));
+ SetTargetRegion(quad);
+}
+
+void ShaderProgram::SetTargetRegion(const Quad& quad) {
+ int index = 0;
+ for (int i = 0; i < 4; ++i, index += 2) {
+ target_coords_[index] = (quad.point(i).x() * 2.0) - 1.0;
+ target_coords_[index+1] = (quad.point(i).y() * 2.0) - 1.0;
+ }
+}
+
+bool ShaderProgram::CompileAndLink() {
+ // Make sure we haven't compiled and linked already
+ if (vertex_shader_ != 0 || fragment_shader_ != 0 || program_ != 0) {
+ ALOGE("Attempting to re-compile shaders!");
+ return false;
+ }
+
+ // Compile vertex shader
+ vertex_shader_ = CompileShader(GL_VERTEX_SHADER,
+ vertex_shader_source_.c_str());
+ if (!vertex_shader_) {
+ ALOGE("Shader compilation failed!");
+ return false;
+ }
+
+ // Compile fragment shader
+ fragment_shader_ = CompileShader(GL_FRAGMENT_SHADER,
+ fragment_shader_source_.c_str());
+ if (!fragment_shader_)
+ return false;
+
+ // Link
+ GLuint shaders[2] = { vertex_shader_, fragment_shader_ };
+ program_ = LinkProgram(shaders, 2);
+
+ // Scan for all uniforms in the program
+ ScanUniforms();
+
+ // Check if we manage all coordinates
+ if (program_ != 0) {
+ ProgramVar tex_coord_attr = glGetAttribLocation(program_, TexCoordAttributeName().c_str());
+ ProgramVar pos_coord_attr = glGetAttribLocation(program_, PositionAttributeName().c_str());
+ manage_coordinates_ = (tex_coord_attr >= 0 && pos_coord_attr >= 0);
+ } else {
+ ALOGE("Could not link shader program!");
+ return false;
+ }
+
+ return true;
+}
+
+GLuint ShaderProgram::CompileShader(GLenum shader_type, const char* source) {
+ LOG_FRAME("Compiling source:\n[%s]", source);
+
+ // Create shader
+ GLuint shader = glCreateShader(shader_type);
+ if (shader) {
+ // Compile source
+ glShaderSource(shader, 1, &source, NULL);
+ glCompileShader(shader);
+
+ // Check for compilation errors
+ GLint compiled = 0;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+ if (!compiled) {
+ // Log the compilation error messages
+ ALOGE("Problem compiling shader! Source:");
+ ALOGE("%s", source);
+ std::string src(source);
+ unsigned int cur_pos = 0;
+ unsigned int next_pos = 0;
+ int line_number = 1;
+ while ( (next_pos = src.find_first_of('\n', cur_pos)) != std::string::npos) {
+ ALOGE("%03d : %s", line_number, src.substr(cur_pos, next_pos-cur_pos).c_str());
+ cur_pos = next_pos + 1;
+ line_number++;
+ }
+ ALOGE("%03d : %s", line_number, src.substr(cur_pos, next_pos-cur_pos).c_str());
+
+ GLint log_length = 0;
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
+ if (log_length) {
+ char* error_log = new char[log_length];
+ if (error_log) {
+ glGetShaderInfoLog(shader, log_length, NULL, error_log);
+ ALOGE("Shader compilation error %d:\n%s\n", shader_type, error_log);
+ delete[] error_log;
+ }
+ }
+ glDeleteShader(shader);
+ shader = 0;
+ }
+ }
+ return shader;
+}
+
+GLuint ShaderProgram::LinkProgram(GLuint* shaders, GLuint count) {
+ GLuint program = glCreateProgram();
+ if (program) {
+ // Attach all compiled shaders
+ for (GLuint i = 0; i < count; ++i) {
+ glAttachShader(program, shaders[i]);
+ if (GLEnv::CheckGLError("glAttachShader")) return 0;
+ }
+
+ // Link
+ glLinkProgram(program);
+
+ // Check for linking errors
+ GLint linked = 0;
+ glGetProgramiv(program, GL_LINK_STATUS, &linked);
+ if (linked != GL_TRUE) {
+ // Log the linker error messages
+ GLint log_length = 0;
+ glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length);
+ if (log_length) {
+ char* error_log = new char[log_length];
+ if (error_log) {
+ glGetProgramInfoLog(program, log_length, NULL, error_log);
+ ALOGE("Program Linker Error:\n%s\n", error_log);
+ delete[] error_log;
+ }
+ }
+ glDeleteProgram(program);
+ program = 0;
+ }
+ }
+ return program;
+}
+
+void ShaderProgram::ScanUniforms() {
+ int uniform_count;
+ int buffer_size;
+ GLenum type;
+ GLint capacity;
+ glGetProgramiv(program_, GL_ACTIVE_UNIFORMS, &uniform_count);
+ glGetProgramiv(program_, GL_ACTIVE_UNIFORM_MAX_LENGTH, &buffer_size);
+ std::vector<GLchar> name(buffer_size);
+ for (int i = 0; i < uniform_count; ++i) {
+ glGetActiveUniform(program_, i, buffer_size, NULL, &capacity, &type, &name[0]);
+ ProgramVar uniform_id = glGetUniformLocation(program_, &name[0]);
+ uniform_indices_[uniform_id] = i;
+ }
+}
+
+bool ShaderProgram::PushCoords(ProgramVar attr, float* coords) {
+ // If the shader does not define these, we simply ignore the coordinates, and assume that the
+ // user is managing coordinates.
+ if (attr >= 0) {
+ const uint8_t* data = reinterpret_cast<const uint8_t*>(coords);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glVertexAttribPointer(attr, 2, GL_FLOAT, false, 2 * sizeof(float), data);
+ glEnableVertexAttribArray(attr);
+ return !GLEnv::CheckGLError("Pushing vertex coordinates");
+ }
+ return true;
+}
+
+bool ShaderProgram::PushSourceCoords(float* coords) {
+ ProgramVar tex_coord_attr = glGetAttribLocation(program_, TexCoordAttributeName().c_str());
+ return PushCoords(tex_coord_attr, coords);
+}
+
+bool ShaderProgram::PushTargetCoords(float* coords) {
+ ProgramVar pos_coord_attr = glGetAttribLocation(program_, PositionAttributeName().c_str());
+ return PushCoords(pos_coord_attr, coords);
+}
+
+std::string ShaderProgram::InputTextureUniformName(int index) {
+ std::stringstream tex_name;
+ tex_name << "tex_sampler_" << index;
+ return tex_name.str();
+}
+
+bool ShaderProgram::BindInputTextures(const std::vector<GLuint>& textures,
+ const std::vector<GLenum>& targets) {
+ for (unsigned i = 0; i < textures.size(); ++i) {
+ // Activate texture unit i
+ glActiveTexture(BaseTextureUnit() + i);
+ if (GLEnv::CheckGLError("Activating Texture Unit"))
+ return false;
+
+ // Bind our texture
+ glBindTexture(targets[i], textures[i]);
+ LOG_FRAME("Binding texture %d", textures[i]);
+ if (GLEnv::CheckGLError("Binding Texture"))
+ return false;
+
+ // Set the texture handle in the shader to unit i
+ ProgramVar tex_var = GetUniform(InputTextureUniformName(i));
+ if (tex_var >= 0) {
+ glUniform1i(tex_var, i);
+ } else {
+ ALOGE("ShaderProgram: Shader does not seem to support %d number of "
+ "inputs! Missing uniform 'tex_sampler_%d'!", textures.size(), i);
+ return false;
+ }
+
+ if (GLEnv::CheckGLError("Texture Variable Binding"))
+ return false;
+ }
+
+ return true;
+}
+
+bool ShaderProgram::UseProgram() {
+ if (GLEnv::GetCurrentProgram() != program_) {
+ LOG_FRAME("Using program %d", program_);
+ glUseProgram(program_);
+ return !GLEnv::CheckGLError("Use Program");
+ }
+ return true;
+}
+
+bool ShaderProgram::RenderFrame(const std::vector<GLuint>& textures,
+ const std::vector<GLenum>& targets) {
+ // Make sure we have enough texture units to accomodate the textures
+ if (textures.size() > static_cast<unsigned>(MaxTextureUnits())) {
+ ALOGE("ShaderProgram: Number of input textures is unsupported on this "
+ "platform!");
+ return false;
+ }
+
+ // Prepare to render
+ if (!BeginDraw()) {
+ ALOGE("ShaderProgram: couldn't initialize gl for drawing!");
+ return false;
+ }
+
+ // Bind input textures
+ if (!BindInputTextures(textures, targets)) {
+ ALOGE("BindInputTextures failed");
+ return false;
+ }
+
+ if (LOG_EVERY_FRAME) {
+ int fbo, program, buffer;
+ glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo);
+ glGetIntegerv(GL_CURRENT_PROGRAM, &program);
+ glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &buffer);
+ ALOGV("RenderFrame: fbo %d prog %d buff %d", fbo, program, buffer);
+ }
+
+ // Render!
+ const bool requestTile = (tile_x_count_ != 1 || tile_y_count_ != 1);
+ const bool success = (!requestTile || !manage_coordinates_ || vertex_count_ != 4)
+ ? Draw()
+ : DrawTiled();
+
+ // Pop vertex attributes
+ PopAttributes();
+
+ return success && !GLEnv::CheckGLError("Rendering");
+}
+
+bool ShaderProgram::Draw() {
+ if (PushSourceCoords(source_coords_) && PushTargetCoords(target_coords_)) {
+ glDrawArrays(draw_mode_, 0, vertex_count_);
+ return true;
+ }
+ return false;
+}
+
+bool ShaderProgram::DrawTiled() {
+ // Target coordinate step size
+ float s[8];
+ float t[8];
+
+ // Step sizes
+ const float xs = 1.0f / static_cast<float>(tile_x_count_);
+ const float ys = 1.0f / static_cast<float>(tile_y_count_);
+
+ // Tile drawing loop
+ for (int i = 0; i < tile_x_count_; ++i) {
+ for (int j = 0; j < tile_y_count_; ++j) {
+ // Current coordinates in unit rectangle
+ const float x = i / static_cast<float>(tile_x_count_);
+ const float y = j / static_cast<float>(tile_y_count_);
+
+ // Source coords
+ GetTileCoords(source_coords_, x, y, &s[0], &s[1]);
+ GetTileCoords(source_coords_, x + xs, y, &s[2], &s[3]);
+ GetTileCoords(source_coords_, x, y + ys, &s[4], &s[5]);
+ GetTileCoords(source_coords_, x + xs, y + ys, &s[6], &s[7]);
+
+ // Target coords
+ GetTileCoords(target_coords_, x, y, &t[0], &t[1]);
+ GetTileCoords(target_coords_, x + xs, y, &t[2], &t[3]);
+ GetTileCoords(target_coords_, x, y + ys, &t[4], &t[5]);
+ GetTileCoords(target_coords_, x + xs, y + ys, &t[6], &t[7]);
+
+ if (PushSourceCoords(s) && PushTargetCoords(t)) {
+ glDrawArrays(draw_mode_, 0, vertex_count_);
+ Yield();
+ } else {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+void ShaderProgram::Yield() {
+ glFinish();
+}
+
+bool ShaderProgram::BeginDraw() {
+ // Activate shader program
+ if (!UseProgram())
+ return false;
+
+ // Push vertex attributes
+ PushAttributes();
+
+ // Clear output, if requested
+ if (clears_) {
+ glClearColor(clear_color_.red,
+ clear_color_.green,
+ clear_color_.blue,
+ clear_color_.alpha);
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
+
+ // Enable/Disable blending
+ if (blending_) {
+ glEnable(GL_BLEND);
+ glBlendFunc(sfactor_, dfactor_);
+ } else glDisable(GL_BLEND);
+
+ return true;
+}
+
+int ShaderProgram::MaxVaryingCount() {
+ GLint result;
+ glGetIntegerv(GL_MAX_VARYING_VECTORS, &result);
+ return result;
+}
+
+int ShaderProgram::MaxTextureUnits() {
+ return GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS - 1;
+}
+
+void ShaderProgram::SetDrawMode(GLenum mode) {
+ draw_mode_ = mode;
+}
+
+void ShaderProgram::SetClearsOutput(bool clears) {
+ clears_ = clears;
+}
+
+void ShaderProgram::SetClearColor(float red, float green, float blue, float alpha) {
+ clear_color_.red = red;
+ clear_color_.green = green;
+ clear_color_.blue = blue;
+ clear_color_.alpha = alpha;
+}
+
+void ShaderProgram::SetTileCounts(int x_count, int y_count) {
+ tile_x_count_ = x_count;
+ tile_y_count_ = y_count;
+}
+
+// Variable Value Setting Helpers //////////////////////////////////////////////
+bool ShaderProgram::CheckValueCount(const std::string& var_type,
+ const std::string& var_name,
+ int expected_count,
+ int components,
+ int value_size) {
+ if (expected_count != (value_size / components)) {
+ ALOGE("Shader Program: %s Value Error (%s): Expected value length %d "
+ "(%d components), but received length of %d (%d components)!",
+ var_type.c_str(), var_name.c_str(),
+ expected_count, components * expected_count,
+ value_size / components, value_size);
+ return false;
+ }
+ return true;
+}
+
+bool ShaderProgram::CheckValueMult(const std::string& var_type,
+ const std::string& var_name,
+ int components,
+ int value_size) {
+ if (value_size % components != 0) {
+ ALOGE("Shader Program: %s Value Error (%s): Value must be multiple of %d, "
+ "but %d elements were passed!", var_type.c_str(), var_name.c_str(),
+ components, value_size);
+ return false;
+ }
+ return true;
+}
+
+bool ShaderProgram::CheckVarValid(ProgramVar var) {
+ if (!IsVarValid(var)) {
+ ALOGE("Shader Program: Attempting to access invalid variable!");
+ return false;
+ }
+ return true;
+}
+
+// Uniforms ////////////////////////////////////////////////////////////////////
+bool ShaderProgram::CheckUniformValid(ProgramVar var) {
+ if (!IsVarValid(var) || uniform_indices_.find(var) == uniform_indices_.end()) {
+ ALOGE("Shader Program: Attempting to access unknown uniform %d!", var);
+ return false;
+ }
+ return true;
+}
+
+int ShaderProgram::MaxUniformCount() {
+ // Here we return the minimum of the max uniform count for fragment and vertex
+ // shaders.
+ GLint count1, count2;
+ glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &count1);
+ glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &count2);
+ return count1 < count2 ? count1 : count2;
+}
+
+ProgramVar ShaderProgram::GetUniform(const std::string& name) const {
+ if (!IsExecutable()) {
+ ALOGE("ShaderProgram: Error: Must link program before querying uniforms!");
+ return -1;
+ }
+ return glGetUniformLocation(program_, name.c_str());
+}
+
+bool ShaderProgram::SetUniformValue(ProgramVar var, int value) {
+ if (!CheckVarValid(var))
+ return false;
+
+ // Uniforms are local to the currently used program.
+ if (UseProgram()) {
+ glUniform1i(var, value);
+ return !GLEnv::CheckGLError("Set Uniform Value (int)");
+ }
+ return false;
+}
+
+bool ShaderProgram::SetUniformValue(ProgramVar var, float value) {
+ if (!CheckVarValid(var))
+ return false;
+
+ // Uniforms are local to the currently used program.
+ if (UseProgram()) {
+ glUniform1f(var, value);
+ return !GLEnv::CheckGLError("Set Uniform Value (float)");
+ }
+ return false;
+}
+
+bool ShaderProgram::SetUniformValue(ProgramVar var,
+ const int* values,
+ int count) {
+ if (!CheckUniformValid(var))
+ return false;
+
+ // Make sure we have values at all
+ if (count == 0)
+ return false;
+
+ // Uniforms are local to the currently used program.
+ if (UseProgram()) {
+ // Get uniform information
+ GLint capacity;
+ GLenum type;
+ char name[128];
+ glGetActiveUniform(program_, IndexOfUniform(var), 128, NULL, &capacity, &type, name);
+
+ // Make sure passed values are compatible
+ const int components = GLEnv::NumberOfComponents(type);
+ if (!CheckValueCount("Uniform (int)", name, capacity, components, count)
+ || !CheckValueMult ("Uniform (int)", name, components, count))
+ return false;
+
+ // Set value based on type
+ const int n = count / components;
+ switch(type) {
+ case GL_INT:
+ glUniform1iv(var, n, values);
+ break;
+
+ case GL_INT_VEC2:
+ glUniform2iv(var, n, values);
+ break;
+
+ case GL_INT_VEC3:
+ glUniform3iv(var, n, values);
+ break;
+
+ case GL_INT_VEC4:
+ glUniform4iv(var, n, values);
+ break;
+
+ default:
+ return false;
+ };
+ return !GLEnv::CheckGLError("Set Uniform Value");
+ }
+ return false;
+}
+
+bool ShaderProgram::SetUniformValue(ProgramVar var,
+ const float* values,
+ int count) {
+ if (!CheckUniformValid(var))
+ return false;
+
+ // Make sure we have values at all
+ if (count == 0)
+ return false;
+
+ // Uniforms are local to the currently used program.
+ if (UseProgram()) {
+ // Get uniform information
+ GLint capacity;
+ GLenum type;
+ char name[128];
+ glGetActiveUniform(program_, IndexOfUniform(var), 128, NULL, &capacity, &type, name);
+
+ // Make sure passed values are compatible
+ const int components = GLEnv::NumberOfComponents(type);
+ if (!CheckValueCount("Uniform (float)", name, capacity, components, count)
+ || !CheckValueMult ("Uniform (float)", name, components, count))
+ return false;
+
+ // Set value based on type
+ const int n = count / components;
+ switch(type) {
+ case GL_FLOAT:
+ glUniform1fv(var, n, values);
+ break;
+
+ case GL_FLOAT_VEC2:
+ glUniform2fv(var, n, values);
+ break;
+
+ case GL_FLOAT_VEC3:
+ glUniform3fv(var, n, values);
+ break;
+
+ case GL_FLOAT_VEC4:
+ glUniform4fv(var, n, values);
+ break;
+
+ case GL_FLOAT_MAT2:
+ glUniformMatrix2fv(var, n, GL_FALSE, values);
+ break;
+
+ case GL_FLOAT_MAT3:
+ glUniformMatrix3fv(var, n, GL_FALSE, values);
+ break;
+
+ case GL_FLOAT_MAT4:
+ glUniformMatrix4fv(var, n, GL_FALSE, values);
+ break;
+
+ default:
+ return false;
+ };
+ return !GLEnv::CheckGLError("Set Uniform Value");
+ }
+ return false;
+}
+
+bool ShaderProgram::SetUniformValue(ProgramVar var, const std::vector<int>& values) {
+ return SetUniformValue(var, &values[0], values.size());
+}
+
+bool ShaderProgram::SetUniformValue(ProgramVar var,
+ const std::vector<float>& values) {
+ return SetUniformValue(var, &values[0], values.size());
+}
+
+bool ShaderProgram::SetUniformValue(const std::string& name, const Value& value) {
+ if (ValueIsFloat(value))
+ return SetUniformValue(GetUniform(name), GetFloatValue(value));
+ else if (ValueIsInt(value))
+ return SetUniformValue(GetUniform(name), GetIntValue(value));
+ else if (ValueIsFloatArray(value))
+ return SetUniformValue(GetUniform(name), GetFloatArrayValue(value), GetValueCount(value));
+ else if (ValueIsIntArray(value))
+ return SetUniformValue(GetUniform(name), GetIntArrayValue(value), GetValueCount(value));
+ else
+ return false;
+}
+
+Value ShaderProgram::GetUniformValue(const std::string& name) {
+ ProgramVar var = GetUniform(name);
+ if (CheckUniformValid(var)) {
+ // Get uniform information
+ GLint capacity;
+ GLenum type;
+ glGetActiveUniform(program_, IndexOfUniform(var), 0, NULL, &capacity, &type, NULL);
+ if (!GLEnv::CheckGLError("Get Active Uniform")) {
+ // Get value based on type, and wrap in value object
+ switch(type) {
+ case GL_INT: {
+ int value;
+ glGetUniformiv(program_, var, &value);
+ return !GLEnv::CheckGLError("GetVariableValue") ? MakeIntValue(value)
+ : MakeNullValue();
+ } break;
+
+ case GL_INT_VEC2: {
+ int value[2];
+ glGetUniformiv(program_, var, &value[0]);
+ return !GLEnv::CheckGLError("GetVariableValue") ? MakeIntArrayValue(value, 2)
+ : MakeNullValue();
+ } break;
+
+ case GL_INT_VEC3: {
+ int value[3];
+ glGetUniformiv(program_, var, &value[0]);
+ return !GLEnv::CheckGLError("GetVariableValue") ? MakeIntArrayValue(value, 3)
+ : MakeNullValue();
+ } break;
+
+ case GL_INT_VEC4: {
+ int value[4];
+ glGetUniformiv(program_, var, &value[0]);
+ return !GLEnv::CheckGLError("GetVariableValue") ? MakeIntArrayValue(value, 4)
+ : MakeNullValue();
+ } break;
+
+ case GL_FLOAT: {
+ float value;
+ glGetUniformfv(program_, var, &value);
+ return !GLEnv::CheckGLError("GetVariableValue") ? MakeFloatValue(value)
+ : MakeNullValue();
+ } break;
+
+ case GL_FLOAT_VEC2: {
+ float value[2];
+ glGetUniformfv(program_, var, &value[0]);
+ return !GLEnv::CheckGLError("GetVariableValue") ? MakeFloatArrayValue(value, 2)
+ : MakeNullValue();
+ } break;
+
+ case GL_FLOAT_VEC3: {
+ float value[3];
+ glGetUniformfv(program_, var, &value[0]);
+ return !GLEnv::CheckGLError("GetVariableValue") ? MakeFloatArrayValue(value, 3)
+ : MakeNullValue();
+ } break;
+
+ case GL_FLOAT_VEC4: {
+ float value[4];
+ glGetUniformfv(program_, var, &value[0]);
+ return !GLEnv::CheckGLError("GetVariableValue") ? MakeFloatArrayValue(value, 4)
+ : MakeNullValue();
+ } break;
+
+ case GL_FLOAT_MAT2: {
+ float value[4];
+ glGetUniformfv(program_, var, &value[0]);
+ return !GLEnv::CheckGLError("GetVariableValue") ? MakeFloatArrayValue(value, 4)
+ : MakeNullValue();
+ } break;
+
+ case GL_FLOAT_MAT3: {
+ float value[9];
+ glGetUniformfv(program_, var, &value[0]);
+ return !GLEnv::CheckGLError("GetVariableValue") ? MakeFloatArrayValue(value, 9)
+ : MakeNullValue();
+ } break;
+
+ case GL_FLOAT_MAT4: {
+ float value[16];
+ glGetUniformfv(program_, var, &value[0]);
+ return !GLEnv::CheckGLError("GetVariableValue") ? MakeFloatArrayValue(value, 16)
+ : MakeNullValue();
+ } break;
+ }
+ }
+ }
+ return MakeNullValue();
+}
+
+GLuint ShaderProgram::IndexOfUniform(ProgramVar var) {
+ return uniform_indices_[var];
+}
+
+// Attributes //////////////////////////////////////////////////////////////////////////////////////
+int ShaderProgram::MaxAttributeCount() {
+ GLint result;
+ glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &result);
+ return result;
+}
+
+ProgramVar ShaderProgram::GetAttribute(const std::string& name) const {
+ if (!IsExecutable()) {
+ ALOGE("ShaderProgram: Error: Must link program before querying attributes!");
+ return -1;
+ } else if (name == PositionAttributeName() || name == TexCoordAttributeName()) {
+ ALOGW("ShaderProgram: Attempting to overwrite internal vertex attribute '%s'!", name.c_str());
+ }
+ return glGetAttribLocation(program_, name.c_str());
+}
+
+bool ShaderProgram::SetAttributeValues(ProgramVar var,
+ const VertexFrame* vbo,
+ GLenum type,
+ int components,
+ int stride,
+ int offset,
+ bool normalize) {
+ if (!CheckVarValid(var))
+ return false;
+
+ if (vbo) {
+ VertexAttrib attrib;
+ attrib.is_const = false;
+ attrib.index = var;
+ attrib.components = components;
+ attrib.normalized = normalize;
+ attrib.stride = stride;
+ attrib.type = type;
+ attrib.vbo = vbo->GetVboId();
+ attrib.offset = offset;
+
+ return StoreAttribute(attrib);
+ }
+ return false;
+}
+
+bool ShaderProgram::SetAttributeValues(ProgramVar var,
+ const uint8_t* data,
+ GLenum type,
+ int components,
+ int stride,
+ int offset,
+ bool normalize) {
+ if (!CheckVarValid(var))
+ return false;
+
+ if (data) {
+ VertexAttrib attrib;
+ attrib.is_const = false;
+ attrib.index = var;
+ attrib.components = components;
+ attrib.normalized = normalize;
+ attrib.stride = stride;
+ attrib.type = type;
+ attrib.values = data + offset;
+
+ return StoreAttribute(attrib);
+ }
+ return false;
+}
+
+bool ShaderProgram::SetAttributeValues(ProgramVar var,
+ const std::vector<float>& data,
+ int components) {
+ return SetAttributeValues(var, &data[0], data.size(), components);
+}
+
+bool ShaderProgram::SetAttributeValues(ProgramVar var,
+ const float* data,
+ int total,
+ int components) {
+ if (!CheckVarValid(var))
+ return false;
+
+ // Make sure the passed data vector has a valid size
+ if (total % components != 0) {
+ ALOGE("ShaderProgram: Invalid attribute vector given! Specified a component "
+ "count of %d, but passed a non-multiple vector of size %d!",
+ components, total);
+ return false;
+ }
+
+ // Copy the data to a buffer we own
+ float* data_cpy = new float[total];
+ memcpy(data_cpy, data, sizeof(float) * total);
+
+ // Store the attribute
+ VertexAttrib attrib;
+ attrib.is_const = false;
+ attrib.index = var;
+ attrib.components = components;
+ attrib.normalized = false;
+ attrib.stride = components * sizeof(float);
+ attrib.type = GL_FLOAT;
+ attrib.values = data_cpy;
+ attrib.owned_data = data_cpy; // Marks this for deletion later on
+
+ return StoreAttribute(attrib);
+}
+
+bool ShaderProgram::StoreAttribute(VertexAttrib attrib) {
+ if (attrib.index >= 0) {
+ attrib_values_[attrib.index] = attrib;
+ return true;
+ }
+ return false;
+}
+
+bool ShaderProgram::PushAttributes() {
+ for (VertexAttribMap::const_iterator iter = attrib_values_.begin();
+ iter != attrib_values_.end();
+ ++iter) {
+ const VertexAttrib& attrib = iter->second;
+
+ if (attrib.is_const) {
+ // Set constant attribute values (must be specified as host values)
+ if (!attrib.values)
+ return false;
+
+ const float* values = reinterpret_cast<const float*>(attrib.values);
+ switch (attrib.components) {
+ case 1: glVertexAttrib1fv(attrib.index, values); break;
+ case 2: glVertexAttrib2fv(attrib.index, values); break;
+ case 3: glVertexAttrib3fv(attrib.index, values); break;
+ case 4: glVertexAttrib4fv(attrib.index, values); break;
+ default: return false;
+ }
+ glDisableVertexAttribArray(attrib.index);
+ } else {
+ // Set per-vertex values
+ if (attrib.values) {
+ // Make sure no buffer is bound and set attribute
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ glVertexAttribPointer(attrib.index,
+ attrib.components,
+ attrib.type,
+ attrib.normalized,
+ attrib.stride,
+ attrib.values);
+ } else if (attrib.vbo) {
+ // Bind VBO and set attribute
+ glBindBuffer(GL_ARRAY_BUFFER, attrib.vbo);
+
+ glVertexAttribPointer(attrib.index,
+ attrib.components,
+ attrib.type,
+ attrib.normalized,
+ attrib.stride,
+ reinterpret_cast<const void*>(attrib.offset));
+ } else {
+ return false;
+ }
+ glEnableVertexAttribArray(attrib.index);
+ }
+
+ // Make sure everything worked
+ if (GLEnv::CheckGLError("Pushing Vertex Attributes"))
+ return false;
+ }
+
+ return true;
+}
+
+bool ShaderProgram::PopAttributes() {
+ // Disable vertex attributes
+ for (VertexAttribMap::const_iterator iter = attrib_values_.begin();
+ iter != attrib_values_.end();
+ ++iter) {
+ glDisableVertexAttribArray(iter->second.index);
+ }
+ // Unbind buffer: Very important as this greatly affects what glVertexAttribPointer does!
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ return !GLEnv::CheckGLError("Popping Vertex Attributes");
+}
+
+void ShaderProgram::SetVertexCount(int count) {
+ vertex_count_ = count;
+}
+
+} // namespace filterfw
+} // namespace android
diff --git a/media/mca/filterfw/native/core/shader_program.h b/media/mca/filterfw/native/core/shader_program.h
new file mode 100644
index 0000000..2063175
--- /dev/null
+++ b/media/mca/filterfw/native/core/shader_program.h
@@ -0,0 +1,553 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FILTERFW_CORE_SHADER_PROGRAM_H
+#define ANDROID_FILTERFW_CORE_SHADER_PROGRAM_H
+
+#include <vector>
+#include <map>
+#include <string>
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <EGL/egl.h>
+
+#include "core/gl_env.h"
+#include "core/value.h"
+
+namespace android {
+namespace filterfw {
+
+class GLFrame;
+class GLFrameBufferHandle;
+class GLTextureHandle;
+class Quad;
+class VertexFrame;
+
+typedef GLint ProgramVar;
+
+// A ShaderProgram is a Program object that holds a GLSL shader implementation.
+// It provides functionality for compiling, linking, and executing the shader.
+// On top of that, it provides access to the shaders source code, uniforms,
+// attributes, and other properties.
+// By default a ShaderProgram provides its own vertex shader. However, a custom
+// vertex shader may be passed and used instead.
+// When implementing a vertex shader, the following attribute names have special
+// meaning:
+//
+// - a_position: The vertex position
+// - a_texcoord: The texture cooridnates
+//
+// The shader program will bind these attributes to the correct values, if they
+// are present in the vertex shader source code.
+//
+// When implementing the fragment shader, the following variable names must be
+// defined:
+//
+// - tex_sampler_<n>: The n'th input texture. For instance, use tex_sampler_0
+// for the first input texture. Must be a uniform sampler2D.
+// - v_texcoord: The current texture coordinate.
+//
+// If more input textures are given than the shader can handle, this will result
+// in an error.
+//
+class ShaderProgram {
+ public:
+ // General Functionality ///////////////////////////////////////////////////
+ // Create a new shader program with the given fragment shader source code.
+ // A default vertex shader is used, which renders the input texture to a
+ // rectangular region of the output texture. You can modify the input and
+ // output regions by using the SetSourceRegion(...) and SetTargetRegion(...)
+ // (and related) functions below.
+ // This program will not be executable until you have compiled and linked
+ // it.
+ // Note, that the ShaderProgram does NOT take ownership of the GLEnv. The
+ // caller must make sure the GLEnv stays valid as long as the GLFrame is
+ // alive.
+ explicit ShaderProgram(GLEnv* gl_env, const std::string& fragment_shader);
+
+ // Create a new shader program with the given fragment and vertex shader
+ // source code. This program will not be executable until you have compiled
+ // and linked it.
+ // Note, that the ShaderProgram does NOT take ownership of the GLEnv. The
+ // caller must make sure the GLEnv stays valid as long as the GLFrame is
+ // alive.
+ ShaderProgram(GLEnv* gl_env,
+ const std::string& vertex_shader,
+ const std::string& fragment_shader);
+
+ // Destructor.
+ ~ShaderProgram();
+
+ // Process the given input frames and write the result to the output frame.
+ // Returns false if there was an error processing.
+ bool Process(const std::vector<const GLFrame*>& inputs, GLFrame* output);
+
+ // Same as above, but pass GL interfaces rather than frame objects. Use this
+ // only if you are not working on Frame objects, but rather directly on GL
+ // textures and FBOs.
+ bool Process(const std::vector<const GLTextureHandle*>& input,
+ GLFrameBufferHandle* output);
+
+ // Compile and link the shader source code. Returns true if compilation
+ // and linkage was successful. Compilation and linking error messages are
+ // written to the error log.
+ bool CompileAndLink();
+
+ // Returns true if this Program has been compiled and linked successfully.
+ bool IsExecutable() const {
+ return program_ != 0;
+ }
+
+ // Returns true if the shader program variable is valid.
+ static bool IsVarValid(ProgramVar var);
+
+ // Special ShaderPrograms //////////////////////////////////////////////////
+ // A (compiled) shader program which assigns the sampled pixels from the
+ // input to the output. Note that transformations may be applied to achieve
+ // effects such as cropping, scaling or rotation.
+ // The caller takes ownership of the result!
+ static ShaderProgram* CreateIdentity(GLEnv* env);
+
+ // Geometry ////////////////////////////////////////////////////////////////
+ // These functions modify the source and target regions used during
+ // rasterization. Note, that these functions will ONLY take effect if
+ // the default vertex shader is used, or your custom vertex shader defines
+ // the a_position and a_texcoord attributes.
+
+ // Set the program to read from a subregion of the input frame, given by
+ // the origin (x, y) and dimensions (width, height). Values are considered
+ // normalized between 0.0 and 1.0. If this region exceeds the input frame
+ // dimensions the results are undefined.
+ void SetSourceRect(float x, float y, float width, float height) ;
+
+ // Set the program to read from a subregion of the input frame, given by
+ // the passed Quad. Values are considered normalized between 0.0 and 1.0.
+ // The Quad points are expected to be in the order top-left, top-right,
+ // bottom-left, bottom-right.
+ // If this region exceeds the input frame dimensions the results are
+ // undefined.
+ void SetSourceRegion(const Quad& quad);
+
+ // Set the program to write to a subregion of the output frame, given by
+ // the origin (x, y) and dimensions (width, height). Values are considered
+ // normalized between 0.0 and 1.0. If this region exceeds the output frame
+ // dimensions the image will be clipped.
+ void SetTargetRect(float x, float y, float width, float height);
+
+ // Set the program to write to a subregion of the output frame, given by
+ // the passed Quad. Values are considered normalized between 0.0 and 1.0.
+ // The Quad points are expected to be in the order top-left, top-right,
+ // bottom-left, bottom-right.
+ // If this region exceeds the output frame dimensions the image will be
+ // clipped.
+ void SetTargetRegion(const Quad& quad);
+
+ // Uniform Variable access /////////////////////////////////////////////////
+ // Note: In order to get and set uniforms, the program must have been
+ // successfully compiled and linked. Otherwise, the getters will return an
+ // invalid ProgramVar variable (check with IsVarValid()).
+ // When setting values, the value type must be match the type of the uniform
+ // in the shader. For instance, a vector of 3 elements cannot be assigned to
+ // a vec2. Similarly, an integer value cannot be assigned to a float value.
+ // Such a type mismatch will result in failure to set the value (which will
+ // remain untouched). Check the return value of the setters to determine
+ // success.
+
+ // Returns the maximum number of uniforms supported by this implementation.
+ static int MaxUniformCount();
+
+ // Returns a handle to the uniform with the given name, or invalid if no
+ // such uniform variable exists in the shader.
+ ProgramVar GetUniform(const std::string& name) const;
+
+ // Set the specified uniform value to the given integer value. Returns true
+ // if the assignment was successful.
+ bool SetUniformValue(ProgramVar var, int value);
+
+ // Set the specified uniform value to the given float value. Returns true
+ // if the assignment was successful.
+ bool SetUniformValue(ProgramVar var, float value);
+
+ // Set the specified uniform value to the given values. Returns true
+ // if the assignment was successful.
+ bool SetUniformValue(ProgramVar var, const int* values, int count);
+
+ // Set the specified uniform value to the given values. Returns true
+ // if the assignment was successful.
+ bool SetUniformValue(ProgramVar var, const float* values, int count);
+
+ // Set the specified uniform value to the given vector value. Returns true
+ // if the assignment was successful.
+ bool SetUniformValue(ProgramVar var, const std::vector<int>& values);
+
+ // Set the specified uniform value to the given vector value. Returns true
+ // if the assignment was successful.
+ bool SetUniformValue(ProgramVar var, const std::vector<float>& values);
+
+ // Generic variable setter, which in the case of GL programs always attempts
+ // to set the value of a uniform variable with the given name. Only values
+ // of type float, float array (or vector), and int are supported.
+ bool SetUniformValue(const std::string& name, const Value& value);
+
+ // Generic variable getter, which in the case of GL programs always attempts
+ // to get the value of a uniform variable with the given name.
+ Value GetUniformValue(const std::string& name);
+
+ // Returns the default name of the input texture uniform variable for the
+ // given input index.
+ static std::string InputTextureUniformName(int index);
+
+ // Attribute access ////////////////////////////////////////////////////////
+ // Note: In order to get and set attributes, the program must have been
+ // successfully compiled and linked. Otherwise, the getters will return an
+ // invalid ProgramVar variable (check with IsVarValid()). Constant attribute
+ // values must be floats. Attribute pointers must be associated with a
+ // specific type, which can be any of the following:
+ // GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_FLOAT,
+ // GL_FIXED, GL_HALF_FLOAT_OES.
+ // When storing vertex data, it is recommended to use VertexFrames when
+ // possible as these will be kept in GPU memory, and no copying of vertex
+ // attributes between system and GPU memory needs to take place.
+
+ // Returns the maximum number of attributes supported by this
+ // implementation.
+ static int MaxAttributeCount();
+
+ // Returns a handle to the attribute with the given name, or invalid if no
+ // such attribute exists in the vertex shader.
+ ProgramVar GetAttribute(const std::string& name) const;
+
+ // Set an attribute value that will be constant for each vertex. Returns
+ // true if the assignment was successful.
+ bool SetConstAttributeValue(ProgramVar var, float value);
+
+ // Set an attribute vector value that will be constant for each vertex.
+ // Returns true if the assignment was successful.
+ bool SetConstAttributeValue(ProgramVar var, const std::vector<float>& value);
+
+ // Set attribute values that differ across vertexes, using a VertexFrame.
+ // This is the recommended method of specifying vertex data, that does not
+ // change from iteration to iteration. The parameters are as follows:
+ // var: The shader variable to bind the values to.
+ // data: The vertex frame which holds the vertex data. This may be a
+ // superset of the data required for this particular vertex. Use the
+ // offset and stride to select the correct data portion.
+ // type: The type of the data values. This may differ from the type of the
+ // shader variables. See the normalize flag on how values are
+ // converted.
+ // components: The number of components per value. Valid values are 1-4.
+ // stride: The delta of one element to the next in bytes.
+ // offset: The offset of the first element.
+ // normalize: True, if not float values should be normalized to the range
+ // 0-1, when converted to a float.
+ // Returns true, if the assignment was successful.
+ bool SetAttributeValues(ProgramVar var,
+ const VertexFrame* data,
+ GLenum type,
+ int components,
+ int stride,
+ int offset,
+ bool normalize);
+
+ // Set attribute values that differ across vertexes, using a data buffer.
+ // This is the recommended method of specifying vertex data, if your data
+ // changes often. Note that this data may need to be copied to GPU memory
+ // for each render pass. Please see above for a description of the
+ // parameters.
+ // Note: The data passed here MUST be valid until all executions of this
+ // Program instance have been completed!
+ bool SetAttributeValues(ProgramVar var,
+ const uint8_t* data,
+ GLenum type,
+ int components,
+ int stride,
+ int offset,
+ bool normalize);
+
+ // Convenience method for setting vertex values using a vector of floats.
+ // The components parameter specifies how many elements per variable should
+ // be assigned (The variable must be able to fit the number of components).
+ // It must be a value between 1 and 4.
+ // While this method is not as flexible as the methods above, this can be
+ // used when more advanced methods are not necessary. Note, that if your
+ // vertex data does not change, it is recommended to use a VertexFrame.
+ bool SetAttributeValues(ProgramVar var,
+ const std::vector<float>& data,
+ int components);
+
+ // Same as above, but using a float pointer instead of vector. Pass the
+ // total number of elements in total.
+ bool SetAttributeValues(ProgramVar var,
+ const float* data,
+ int total,
+ int components);
+
+ // By default, rendering only uses the first 4 vertices. You should only
+ // adjust this value if you are providing your own vertex attributes with
+ // a count unequal to 4. Adjust this value before calling Process().
+ void SetVertexCount(int count);
+
+ // Returns the default name of the attribute used to hold the texture
+ // coordinates. Use this when you need to access the texture coordinate
+ // attribute of the shader's default vertex shader.
+ static const std::string& TexCoordAttributeName() {
+ static std::string s_texcoord("a_texcoord");
+ return s_texcoord;
+ }
+
+ // Returns the default name of the attribute used to hold the output
+ // coordinates. Use this when you need to access the output coordinate
+ // attribute of the shader's default vertex shader.
+ static const std::string& PositionAttributeName() {
+ static std::string s_position("a_position");
+ return s_position;
+ }
+
+ // Rendering ///////////////////////////////////////////////////////////////
+ // Set the draw mode, which can be any of GL_POINTS, GL_LINES,
+ // GL_LINE_STRIP, GL_LINE_LOOP, GL_TRIANGLES, GL_TRIANGLE_STRIP,
+ // GL_TRIANGLE_FAN. The default is GL_TRIANGLE_STRIP.
+ // Warning: Do NOT change this if you are not specifying your own vertex
+ // data with SetAttributeValues(...).
+ void SetDrawMode(GLenum mode);
+
+ // If you are doing your own drawing you should call this before beginning
+ // to draw. This will activate the program, push all used attributes, and
+ // clear the frame if requested. You do not need to call this if you are
+ // not doing your own GL drawing!
+ bool BeginDraw();
+
+ // Render a single frame with the given input textures. You may override
+ // this, if you need custom rendering behavior. However, you must take
+ // care of the following things when overriding:
+ // - Use the correct program (e.g. by calling UseProgram()).
+ // - Bind the given textures
+ // - Bind vertex attributes
+ // - Draw
+ bool RenderFrame(const std::vector<GLuint>& textures,
+ const std::vector<GLenum>& targets);
+
+ // Pass true to clear the output frame before rendering. The color used
+ // to clear is set in SetClearColor().
+ void SetClearsOutput(bool clears);
+
+ // Set the color used to clear the output frame before rendering. You
+ // must activate clearing by calling SetClearsOutput(true).
+ void SetClearColor(float red, float green, float blue, float alpha);
+
+ // Set the number of tiles to split rendering into. Higher tile numbers
+ // will affect performance negatively, but will allow other GPU threads
+ // to render more frequently. Defaults to 1, 1.
+ void SetTileCounts(int x_count, int y_count);
+
+ // Enable or Disable Blending
+ // Set to true to enable, false to disable.
+ void SetBlendEnabled(bool enable) {
+ blending_ = enable;
+ }
+
+ // Specify pixel arithmetic for blending
+ // The values of sfactor and dfactor can be:
+ // GL_ZERO, GL_ONE, GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_SRC_ALPHA,
+ // GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA,
+ // GL_DST_COLOR, GL_ONE_MINUS_DST_COLOR, GL_SRC_ALPHA_SATURATE
+ // Default values for blending are set to:
+ // sfactor = GL_SRC_ALPHA
+ // dfactor = GL_ONE_MINUS_SRC_ALPHA
+ void SetBlendFunc(int sfactor, int dfactor) {
+ sfactor_ = sfactor;
+ dfactor_ = dfactor;
+ }
+
+ // Accessing the Compiled Program //////////////////////////////////////////
+ // Use the compiled and linked program for rendering. You should not need
+ // to call this, unless you are implementing your own rendering method.
+ bool UseProgram();
+
+ // Other Properties ////////////////////////////////////////////////////////
+ // Returns the maximum number of varyings supported by this implementation.
+ static int MaxVaryingCount();
+
+ // Returns the maximum number of texture units supported by this
+ // implementation.
+ static int MaxTextureUnits();
+
+ // Lower level functionality ///////////////////////////////////////////////
+ // Compile the shader with the given source. The shader_type must be either
+ // GL_VERTEX_SHADER or GL_FRAGMENT_SHADER.
+ static GLuint CompileShader(GLenum shader_type, const char* source);
+
+ // Link the compiled shader objects and return the resulting program.
+ static GLuint LinkProgram(GLuint* shaders, GLuint count);
+
+ // Returns the lowest texture unit that will be used to bind textures.
+ GLuint BaseTextureUnit() const {
+ return base_texture_unit_;
+ }
+
+ // Sets the lowest texture unit that will be used to bind textures. The
+ // default value is GL_TEXTURE0.
+ void SetBaseTextureUnit(GLuint texture_unit) {
+ base_texture_unit_ = texture_unit;
+ }
+
+ private:
+ // Structure to store vertex attribute data.
+ struct VertexAttrib {
+ bool is_const;
+ int index;
+ bool normalized;
+ int stride;
+ int components;
+ int offset;
+ GLenum type;
+ GLuint vbo;
+ const void* values;
+ float* owned_data;
+
+ VertexAttrib();
+ };
+ typedef std::map<ProgramVar, VertexAttrib> VertexAttribMap;
+
+ struct RGBAColor {
+ float red;
+ float green;
+ float blue;
+ float alpha;
+
+ RGBAColor() : red(0), green(0), blue(0), alpha(1) {
+ }
+ };
+
+ // Scans for all uniforms in the shader and creates index -> id map.
+ void ScanUniforms();
+
+ // Returns the index of the given uniform. The caller must make sure
+ // that the variable id passed is valid.
+ GLuint IndexOfUniform(ProgramVar var);
+
+ // Binds the given input textures.
+ bool BindInputTextures(const std::vector<GLuint>& textures,
+ const std::vector<GLenum>& targets);
+
+ // Sets the default source and target coordinates.
+ void SetDefaultCoords();
+
+ // Pushes the specified coordinates to the shader attribute.
+ bool PushCoords(ProgramVar attr, float* coords);
+
+ // Pushes the source coordinates.
+ bool PushSourceCoords(float* coords);
+
+ // Pushes the target coordinates.
+ bool PushTargetCoords(float* coords);
+
+ // Performs (simple) GL drawing.
+ bool Draw();
+
+ // Performs tiled GL drawing.
+ bool DrawTiled();
+
+ // Yields to other GPU threads.
+ void Yield();
+
+ // Helper method to assert that the variable value passed has the correct
+ // total size.
+ static bool CheckValueCount(const std::string& var_type,
+ const std::string& var_name,
+ int expected_count,
+ int components,
+ int value_size);
+
+ // Helper method to assert that the variable value passed has a size, that
+ // is compatible with the type size (must be divisible).
+ static bool CheckValueMult(const std::string& var_type,
+ const std::string& var_name,
+ int components,
+ int value_size);
+
+ // Checks that the variable is valid. Logs an error and returns false if
+ // not.
+ static bool CheckVarValid(ProgramVar var);
+
+ // Returns true if the uniform specified by var is an active uniform in the
+ // program.
+ bool CheckUniformValid(ProgramVar var);
+
+ // Store an attribute to use when rendering.
+ bool StoreAttribute(VertexAttrib attrib);
+
+ // Push all assigned attributes before rendering.
+ bool PushAttributes();
+
+ // Pop all assigned attributes after rendering.
+ bool PopAttributes();
+
+ // The shader source code
+ std::string fragment_shader_source_;
+ std::string vertex_shader_source_;
+
+ // The compiled shaders and linked program
+ GLuint fragment_shader_;
+ GLuint vertex_shader_;
+ GLuint program_;
+
+ // The GL environment this shader lives in.
+ GLEnv* gl_env_;
+
+ // The lowest texture unit this program will use
+ GLuint base_texture_unit_;
+
+ // The current source and target coordinates to render from/to.
+ float* source_coords_;
+ float* target_coords_;
+
+ // True, if the program has control over both source and target coordinates.
+ bool manage_coordinates_;
+
+ // The number of tiles to split rendering into.
+ int tile_x_count_;
+ int tile_y_count_;
+
+ // List of attribute data that we need to set before rendering
+ VertexAttribMap attrib_values_;
+
+ // The number of vertices to render
+ int vertex_count_;
+
+ // The draw mode used during rendering
+ GLenum draw_mode_;
+
+ // True, iff the output frame is cleared before rendering
+ bool clears_;
+
+ // The color used to clear the output frame.
+ RGBAColor clear_color_;
+
+ // Set to true to enable blending.
+ bool blending_;
+ int sfactor_;
+ int dfactor_;
+
+ // Map from uniform ids to indices
+ std::map<ProgramVar, GLuint> uniform_indices_;
+};
+
+} // namespace filterfw
+} // namespace android
+
+#endif // ANDROID_FILTERFW_CORE_SHADER_PROGRAM_H
diff --git a/media/mca/filterfw/native/core/statistics.cpp b/media/mca/filterfw/native/core/statistics.cpp
new file mode 100644
index 0000000..6f7fee7
--- /dev/null
+++ b/media/mca/filterfw/native/core/statistics.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "core/statistics.h"
+
+#include <math.h>
+
+namespace android {
+namespace filterfw {
+
+IncrementalGaussian::IncrementalGaussian()
+ : n_(0),
+ sum_x_(0.0f),
+ sum_x2_(0.0f),
+ mean_(0.0f),
+ var_(0.0f),
+ exp_denom_(0.0f),
+ pdf_denom_(0.0f) {
+}
+
+void IncrementalGaussian::Add(float value) {
+ ++n_;
+ sum_x_ += value;
+ sum_x2_ += value * value;
+
+ mean_ = sum_x_ / n_;
+ var_ = sum_x2_ / n_ - mean_ * mean_;
+
+ exp_denom_ = 2.0f * var_;
+ pdf_denom_ = sqrtf(M_PI * exp_denom_);
+}
+
+float IncrementalGaussian::Std() const {
+ return sqrtf(var_);
+}
+
+float IncrementalGaussian::Pdf(float value) const {
+ if (var_ == 0.0f) { return n_ > 0 ? value == mean_ : 0.0f; }
+ const float diff = value - mean_;
+ return expf(-diff * diff / exp_denom_) / pdf_denom_;
+}
+
+} // namespace filterfw
+} // namespace android
diff --git a/media/mca/filterfw/native/core/statistics.h b/media/mca/filterfw/native/core/statistics.h
new file mode 100644
index 0000000..ce73b2b
--- /dev/null
+++ b/media/mca/filterfw/native/core/statistics.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FILTERFW_CORE_STATISTICS_H
+#define ANDROID_FILTERFW_CORE_STATISTICS_H
+
+namespace android {
+namespace filterfw {
+
+// An incrementally-constructed Normal distribution.
+class IncrementalGaussian {
+ public:
+ IncrementalGaussian();
+
+ void Add(float value);
+
+ float NumSamples() const { return n_; }
+ float Mean() const { return mean_; }
+ float Var() const { return var_; }
+ float Std() const;
+ float Pdf(float value) const;
+
+ private:
+ int n_;
+ float sum_x_;
+ float sum_x2_;
+ float mean_;
+ float var_;
+ float exp_denom_;
+ float pdf_denom_;
+};
+
+// Discrete-time implementation of a simple RC low-pass filter:
+// exponentially-weighted moving average.
+class RCFilter {
+ public:
+ explicit RCFilter(float gain)
+ : gain_(gain), n_(0), value_(0.0f) {}
+
+ void Add(float measurement) {
+ value_ = n_++ ? gain_ * measurement + (1.0f - gain_) * value_ : measurement;
+ }
+
+ void Reset() { n_ = 0; }
+
+ int NumMeasurements() const { return n_; }
+ float Output() const { return value_; }
+
+ private:
+ float gain_;
+ int n_;
+ float value_;
+};
+
+} // namespace filterfw
+} // namespace android
+
+#endif // ANDROID_FILTERFW_CORE_STATISTICS_H
diff --git a/media/mca/filterfw/native/core/time_util.cpp b/media/mca/filterfw/native/core/time_util.cpp
new file mode 100644
index 0000000..c86c80d
--- /dev/null
+++ b/media/mca/filterfw/native/core/time_util.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/logging.h"
+#include "base/utilities.h"
+
+#include "core/time_util.h"
+
+#include <map>
+#include <string>
+#include <sys/time.h>
+
+namespace android {
+namespace filterfw {
+
+uint64_t getTimeUs() {
+ static long basesec;
+ struct timeval tv;
+ uint64_t nowtime;
+ gettimeofday(&tv, 0);
+ if (basesec == 0) {
+ basesec = tv.tv_sec;
+ }
+ nowtime = (uint64_t)(tv.tv_sec - basesec) * (uint64_t)1000000 +
+ (uint64_t)tv.tv_usec;
+ return nowtime;
+}
+
+const uint64_t NamedStopWatch::kDefaultLoggingPeriodInFrames = 100;
+
+NamedStopWatch::NamedStopWatch(const std::string& name)
+ : mName(name),
+ mLoggingPeriodInFrames(kDefaultLoggingPeriodInFrames),
+ mStartUSec(0),
+ mNumCalls(0),
+ mTotalUSec(0) {
+}
+
+void NamedStopWatch::Start() {
+ mStartUSec = getTimeUs();
+}
+
+void NamedStopWatch::Stop() {
+ if (!mStartUSec) {
+ return;
+ }
+ uint64_t stopUSec = getTimeUs();
+ if (stopUSec > mStartUSec) {
+ ++mNumCalls;
+ mTotalUSec += stopUSec - mStartUSec;
+ if (mNumCalls % mLoggingPeriodInFrames == 0) {
+ const float mSec = TotalUSec() * 1.0E-3f / NumCalls();
+ ALOGE("%s: %f ms", Name().c_str(), mSec);
+ }
+ }
+ mStartUSec = 0;
+}
+
+namespace {
+static NamedStopWatch* GetWatchForName(const string& watch_name) {
+ // TODO: this leaks the NamedStopWatch objects. Replace it with a
+ // singleton to avoid that and make it thread safe.
+ static map<string, NamedStopWatch*> watches;
+ NamedStopWatch* watch = FindPtrOrNull(watches, watch_name);
+ if (!watch) {
+ watch = new NamedStopWatch(watch_name);
+ watches[watch_name] = watch;
+ }
+ return watch;
+};
+} // namespace
+
+ScopedTimer::ScopedTimer(const string& stop_watch_name) {
+ mWatch = GetWatchForName(stop_watch_name);
+ mWatch->Start();
+}
+
+} // namespace filterfw
+} // namespace android
diff --git a/media/mca/filterfw/native/core/time_util.h b/media/mca/filterfw/native/core/time_util.h
new file mode 100644
index 0000000..3cf2ec9
--- /dev/null
+++ b/media/mca/filterfw/native/core/time_util.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FILTERFW_CORE_TIME_UTIL_H
+#define ANDROID_FILTERFW_CORE_TIME_UTIL_H
+
+#include <string>
+#include <utils/RefBase.h>
+
+#define LOG_MFF_RUNNING_TIMES 0
+
+namespace android {
+namespace filterfw {
+
+uint64_t getTimeUs();
+
+class NamedStopWatch : public RefBase {
+ public:
+ static const uint64_t kDefaultLoggingPeriodInFrames;
+
+ explicit NamedStopWatch(const string& name);
+ void Start();
+ void Stop();
+
+ void SetName(const string& name) { mName = name; }
+ void SetLoggingPeriodInFrames(uint64_t numFrames) {
+ mLoggingPeriodInFrames = numFrames;
+ }
+
+ const string& Name() const { return mName; }
+ uint64_t NumCalls() const { return mNumCalls; }
+ uint64_t TotalUSec() const { return mTotalUSec; }
+
+ private:
+ string mName;
+ uint64_t mLoggingPeriodInFrames;
+ uint64_t mStartUSec;
+ uint64_t mNumCalls;
+ uint64_t mTotalUSec;
+};
+
+class ScopedTimer {
+ public:
+ explicit ScopedTimer(const string& stop_watch_name);
+ explicit ScopedTimer(NamedStopWatch* watch)
+ : mWatch(watch) { mWatch->Start(); }
+ ~ScopedTimer() { mWatch->Stop(); }
+
+ private:
+ NamedStopWatch* mWatch;
+};
+
+} // namespace filterfw
+} // namespace android
+
+#endif // ANDROID_FILTERFW_CORE_TIME_UTIL_H
diff --git a/media/mca/filterfw/native/core/value.cpp b/media/mca/filterfw/native/core/value.cpp
new file mode 100644
index 0000000..04bf0ef
--- /dev/null
+++ b/media/mca/filterfw/native/core/value.cpp
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "value.h"
+
+#define NULL_VALUE_TYPE 0
+#define INT_VALUE_TYPE 1
+#define FLOAT_VALUE_TYPE 2
+#define STRING_VALUE_TYPE 3
+#define BUFFER_VALUE_TYPE 4
+#define MUTABLE_BUFFER_VALUE_TYPE 5
+#define INT_ARRAY_VALUE_TYPE 6
+#define FLOAT_ARRAY_VALUE_TYPE 7
+
+// Templated versions //////////////////////////////////////////////////////////////////////////////
+template<typename POD, int TYPEID>
+POD GetPODValue(Value value) {
+ return value.type == TYPEID ? *reinterpret_cast<POD*>(value.value) : POD();
+}
+
+template<typename PTR, int TYPEID>
+PTR GetPtrValue(Value value) {
+ return value.type == TYPEID ? reinterpret_cast<PTR>(value.value) : NULL;
+}
+
+template<typename POD, int TYPEID>
+Value MakePODValue(POD value) {
+ Value result;
+ result.type = TYPEID;
+ result.value = malloc(sizeof(POD));
+ result.count = 1;
+ *reinterpret_cast<POD*>(result.value) = value;
+ return result;
+}
+
+template<typename BASE, int TYPEID>
+Value MakePtrValue(const BASE* values, int count) {
+ Value result;
+ result.type = TYPEID;
+ result.value = malloc(sizeof(BASE) * count);
+ memcpy(result.value, values, sizeof(BASE) * count);
+ result.count = count;
+ return result;
+}
+
+template<typename POD, int TYPEID>
+int SetPODValue(Value* value, POD new_value) {
+ if (value->type == NULL_VALUE_TYPE) {
+ value->type = TYPEID;
+ value->value = malloc(sizeof(POD));
+ value->count = 1;
+ }
+ if (value->type == TYPEID) {
+ *reinterpret_cast<POD*>(value->value) = new_value;
+ return 1;
+ }
+ return 0;
+}
+
+template<typename BASE, int TYPEID>
+int SetPtrValue(Value* value, const BASE* new_values, int count) {
+ if (value->type == NULL_VALUE_TYPE) {
+ value->type = TYPEID;
+ value->value = malloc(sizeof(BASE) * count);
+ value->count = count;
+ }
+ if (value->type == TYPEID && value->count == count) {
+ memcpy(value->value, new_values, sizeof(BASE) * count);
+ return 1;
+ }
+ return 0;
+}
+
+// C Wrappers //////////////////////////////////////////////////////////////////////////////////////
+int GetIntValue(Value value) {
+ return GetPODValue<int, INT_VALUE_TYPE>(value);
+}
+
+float GetFloatValue(Value value) {
+ return GetPODValue<float, FLOAT_VALUE_TYPE>(value);
+}
+
+const char* GetStringValue(Value value) {
+ return GetPtrValue<const char*, STRING_VALUE_TYPE>(value);
+}
+
+const char* GetBufferValue(Value value) {
+ return (value.type == BUFFER_VALUE_TYPE || value.type == MUTABLE_BUFFER_VALUE_TYPE)
+ ? (const char*)value.value
+ : NULL;
+}
+
+char* GetMutableBufferValue(Value value) {
+ return GetPtrValue<char*, MUTABLE_BUFFER_VALUE_TYPE>(value);
+}
+
+int* GetIntArrayValue(Value value) {
+ return GetPtrValue<int*, INT_ARRAY_VALUE_TYPE>(value);
+}
+
+float* GetFloatArrayValue(Value value) {
+ return GetPtrValue<float*, FLOAT_ARRAY_VALUE_TYPE>(value);
+}
+
+int ValueIsNull(Value value) {
+ return value.type == NULL_VALUE_TYPE;
+}
+
+int ValueIsInt(Value value) {
+ return value.type == INT_VALUE_TYPE;
+}
+
+int ValueIsFloat(Value value) {
+ return value.type == FLOAT_VALUE_TYPE;
+}
+
+int ValueIsString(Value value) {
+ return value.type == STRING_VALUE_TYPE;
+}
+
+int ValueIsBuffer(Value value) {
+ return value.type == BUFFER_VALUE_TYPE || value.type == MUTABLE_BUFFER_VALUE_TYPE;
+}
+
+int ValueIsIntArray(Value value) {
+ return value.type == INT_ARRAY_VALUE_TYPE;
+}
+
+int ValueIsFloatArray(Value value) {
+ return value.type == FLOAT_ARRAY_VALUE_TYPE;
+}
+
+Value MakeNullValue() {
+ Value result;
+ result.type = NULL_VALUE_TYPE;
+ result.value = NULL;
+ result.count = 0;
+ return result;
+}
+
+Value MakeIntValue(int value) {
+ return MakePODValue<int, INT_VALUE_TYPE>(value);
+}
+
+Value MakeFloatValue(float value) {
+ return MakePODValue<float, FLOAT_VALUE_TYPE>(value);
+}
+
+Value MakeStringValue(const char* value) {
+ return MakePtrValue<char, STRING_VALUE_TYPE>(value, strlen(value) + 1);
+}
+
+Value MakeBufferValue(const char* buffer, int size) {
+ return MakePtrValue<char, BUFFER_VALUE_TYPE>(buffer, size);
+}
+
+Value MakeBufferValueNoCopy(const char* buffer, int size) {
+ Value result;
+ result.type = BUFFER_VALUE_TYPE;
+ result.value = (void*)buffer;
+ result.count = size;
+ return result;
+}
+
+Value MakeMutableBufferValue(const char* buffer, int size) {
+ return MakePtrValue<const char, MUTABLE_BUFFER_VALUE_TYPE>(buffer, size);
+}
+
+Value MakeMutableBufferValueNoCopy(char* buffer, int size) {
+ Value result;
+ result.type = MUTABLE_BUFFER_VALUE_TYPE;
+ result.value = (void*)buffer;
+ result.count = size;
+ return result;
+}
+
+Value MakeIntArrayValue(const int* values, int count) {
+ return MakePtrValue<int, INT_ARRAY_VALUE_TYPE>(values, count);
+}
+
+Value MakeFloatArrayValue(const float* values, int count) {
+ return MakePtrValue<float, FLOAT_ARRAY_VALUE_TYPE>(values, count);
+}
+
+int SetIntValue(Value* value, int new_value) {
+ return SetPODValue<int, INT_VALUE_TYPE>(value, new_value);
+}
+
+int SetFloatValue(Value* value, float new_value) {
+ return SetPODValue<float, FLOAT_VALUE_TYPE>(value, new_value);
+}
+
+int SetStringValue(Value* value, const char* new_value) {
+ return SetPtrValue<char, STRING_VALUE_TYPE>(value, new_value, strlen(new_value) + 1);
+}
+
+int SetMutableBufferValue(Value* value, const char* new_data, int size) {
+ return SetPtrValue<char, MUTABLE_BUFFER_VALUE_TYPE>(value, new_data, size);
+}
+
+int SetIntArrayValue(Value* value, const int* new_values, int count) {
+ return SetPtrValue<int, INT_ARRAY_VALUE_TYPE>(value, new_values, count);
+}
+
+int SetFloatArrayValue(Value* value, const float* new_values, int count) {
+ return SetPtrValue<float, FLOAT_ARRAY_VALUE_TYPE>(value, new_values, count);
+}
+
+int GetValueCount(Value value) {
+ return value.count;
+}
+
+void ReleaseValue(Value* value) {
+ if (value && value->value) {
+ free(value->value);
+ value->value = NULL;
+ value->type = NULL_VALUE_TYPE;
+ }
+}
+
diff --git a/media/mca/filterfw/native/core/value.h b/media/mca/filterfw/native/core/value.h
new file mode 100644
index 0000000..37e8800
--- /dev/null
+++ b/media/mca/filterfw/native/core/value.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FILTERFW_CORE_VALUE_H
+#define ANDROID_FILTERFW_CORE_VALUE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// TODO: As this is no longer part of the proposed NDK, should we make this object-oriented (C++)
+// instead? We can also probably clean this up a bit.
+
+// TODO: Change this to an opaque handle?
+typedef struct {
+ void* value;
+ int type;
+ int count;
+} Value;
+
+// TODO: Probably should make these const Value*?
+int GetIntValue(Value value);
+float GetFloatValue(Value value);
+const char* GetStringValue(Value value);
+const char* GetBufferValue(Value value);
+char* GetMutableBufferValue(Value value);
+int* GetIntArrayValue(Value value);
+float* GetFloatArrayValue(Value value);
+
+// TODO: Probably should make these const Value*?
+int ValueIsNull(Value value);
+int ValueIsInt(Value value);
+int ValueIsFloat(Value value);
+int ValueIsString(Value value);
+int ValueIsBuffer(Value value);
+int ValueIsMutableBuffer(Value value);
+int ValueIsIntArray(Value value);
+int ValueIsFloatArray(Value value);
+
+Value MakeNullValue();
+Value MakeIntValue(int value);
+Value MakeFloatValue(float value);
+Value MakeStringValue(const char* value);
+Value MakeBufferValue(const char* data, int size);
+Value MakeBufferValueNoCopy(const char* data, int size);
+Value MakeMutableBufferValue(const char* data, int size);
+Value MakeMutableBufferValueNoCopy(char* data, int size);
+Value MakeIntArrayValue(const int* values, int count);
+Value MakeFloatArrayValue(const float* values, int count);
+
+// Note: These only alloc if value is Null! Otherwise they overwrite, so data must fit!
+int SetIntValue(Value* value, int new_value);
+int SetFloatValue(Value* value, float new_value);
+int SetStringValue(Value* value, const char* new_value);
+int SetMutableBufferValue(Value* value, const char* new_data, int size);
+int SetIntArrayValue(Value* value, const int* new_values, int count);
+int SetFloatArrayValue(Value* value, const float* new_values, int count);
+
+int GetValueCount(Value value);
+
+void ReleaseValue(Value* value);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // ANDROID_FILTERFW_FILTER_VALUE_H
diff --git a/media/mca/filterfw/native/core/vertex_frame.cpp b/media/mca/filterfw/native/core/vertex_frame.cpp
new file mode 100644
index 0000000..822573f
--- /dev/null
+++ b/media/mca/filterfw/native/core/vertex_frame.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/logging.h"
+
+#include "core/gl_env.h"
+#include "core/vertex_frame.h"
+
+#include <GLES2/gl2ext.h>
+#include <EGL/egl.h>
+
+namespace android {
+namespace filterfw {
+
+// GL Extensions that are dynamically looked up at runtime
+static PFNGLMAPBUFFEROESPROC GLMapBufferOES = NULL;
+static PFNGLUNMAPBUFFEROESPROC GLUnmapBufferOES = NULL;
+
+VertexFrame::VertexFrame(int size)
+ : vbo_(0),
+ size_(size) {
+}
+
+VertexFrame::~VertexFrame() {
+ glDeleteBuffers(1, &vbo_);
+}
+
+bool VertexFrame::CreateBuffer() {
+ glGenBuffers(1, &vbo_);
+ return !GLEnv::CheckGLError("Generating VBO");
+}
+
+bool VertexFrame::WriteData(const uint8_t* data, int size) {
+ // Create buffer if not created already
+ const bool first_upload = !HasVBO();
+ if (first_upload && !CreateBuffer()) {
+ ALOGE("VertexFrame: Could not create vertex buffer!");
+ return false;
+ }
+
+ // Upload the data
+ glBindBuffer(GL_ARRAY_BUFFER, vbo_);
+ if (GLEnv::CheckGLError("VBO Bind Buffer"))
+ return false;
+
+ if (first_upload && size == size_)
+ glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
+ else if (!first_upload && size <= size_)
+ glBufferSubData(GL_ARRAY_BUFFER, 0, size, data);
+ else {
+ ALOGE("VertexFrame: Attempting to upload more data (%d bytes) than fits "
+ "inside the vertex frame (%d bytes)!", size, size_);
+ return false;
+ }
+
+ // Make sure it worked
+ if (GLEnv::CheckGLError("VBO Data Upload"))
+ return false;
+
+ // Subsequent uploads are now bound to the size given here
+ size_ = size;
+
+ return true;
+}
+
+int VertexFrame::Size() const {
+ return size_;
+}
+
+} // namespace filterfw
+} // namespace android
diff --git a/media/mca/filterfw/native/core/vertex_frame.h b/media/mca/filterfw/native/core/vertex_frame.h
new file mode 100644
index 0000000..1205096
--- /dev/null
+++ b/media/mca/filterfw/native/core/vertex_frame.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FILTERFW_CORE_VERTEXFRAME_H
+#define ANDROID_FILTERFW_CORE_VERTEXFRAME_H
+
+#include <GLES2/gl2.h>
+
+namespace android {
+namespace filterfw {
+
+// A VertexFrame stores vertex attribute data in a VBO. Unlike other frames,
+// you often create instances of VertexFrame yourself, to pass vertex data to
+// a ShaderProgram. Note, that any kind of reading from VertexFrames is NOT
+// supported. Once data is uploaded to a VertexFrame, it cannot be read from
+// again.
+class VertexFrame {
+ public:
+ // Create a VertexFrame of the specified size (in bytes).
+ explicit VertexFrame(int size);
+
+ ~VertexFrame();
+
+ // Upload the given data to the vertex buffer. The size must match the size
+ // passed in the constructor for the first upload. Subsequent uploads must
+ // be able to fit within the allocated space (i.e. size must not exceed the
+ // frame's size).
+ bool WriteData(const uint8_t* data, int size);
+
+ // The size of the vertex buffer in bytes.
+ int Size() const;
+
+ // Return the id of the internal VBO. Returns 0 if no VBO has been
+ // generated yet. The internal VBO is generated the first time data is
+ // uploaded.
+ GLuint GetVboId() const {
+ return vbo_;
+ }
+
+ // Returns true if the frame contains an allocated VBO.
+ bool HasBuffer() const {
+ return vbo_ != 0;
+ }
+
+ private:
+ // Create the VBO
+ bool CreateBuffer();
+
+ // Returns true if the VBO has been created.
+ bool HasVBO() const {
+ return vbo_ != 0;
+ }
+
+ // The internal VBO handle
+ GLuint vbo_;
+
+ // The size of this frame in bytes
+ int size_;
+};
+
+} // namespace filterfw
+} // namespace android
+
+#endif // ANDROID_FILTERFW_CORE_VERTEXFRAME_H
diff --git a/media/mca/filterfw/native/libfilterfw.mk b/media/mca/filterfw/native/libfilterfw.mk
new file mode 100644
index 0000000..4e88e6f
--- /dev/null
+++ b/media/mca/filterfw/native/libfilterfw.mk
@@ -0,0 +1,33 @@
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Add include paths for native code.
+FFW_PATH := $(call my-dir)
+
+# Uncomment the requirements below, once we need them:
+
+# STLport
+include external/stlport/libstlport.mk
+
+# Neven FaceDetect SDK
+#LOCAL_C_INCLUDES += external/neven/FaceRecEm/common/src/b_FDSDK \
+# external/neven/FaceRecEm/common/src \
+# external/neven/Embedded/common/conf \
+# external/neven/Embedded/common/src \
+# external/neven/unix/src
+
+# Finally, add this directory
+LOCAL_C_INCLUDES += $(FFW_PATH)
+
diff --git a/media/mca/filterpacks/Android.mk b/media/mca/filterpacks/Android.mk
new file mode 100644
index 0000000..6166b1e
--- /dev/null
+++ b/media/mca/filterpacks/Android.mk
@@ -0,0 +1,55 @@
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+##
+# base
+##
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE := libfilterpack_base
+LOCAL_SRC_FILES := native/base/geometry.cpp \
+ native/base/time_util.cpp
+
+LOCAL_CFLAGS := -DANDROID
+
+include external/stlport/libstlport.mk
+
+include $(BUILD_STATIC_LIBRARY)
+
+##
+# filterpack_imageproc
+##
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE := libfilterpack_imageproc
+
+LOCAL_SRC_FILES += native/imageproc/brightness.c \
+ native/imageproc/contrast.c \
+ native/imageproc/invert.c \
+ native/imageproc/to_rgba.c
+
+LOCAL_SHARED_LIBRARIES := libutils libfilterfw
+
+LOCAL_PRELINK_MODULE := false
+
+include $(BUILD_SHARED_LIBRARY)
+
+
diff --git a/media/mca/filterpacks/java/android/filterpacks/base/CallbackFilter.java b/media/mca/filterpacks/java/android/filterpacks/base/CallbackFilter.java
new file mode 100644
index 0000000..4185343
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/base/CallbackFilter.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.base;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GenerateFinalPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.os.Handler;
+import android.os.Looper;
+
+import java.lang.Runnable;
+
+/**
+ * @hide
+ */
+public class CallbackFilter extends Filter {
+
+ @GenerateFieldPort(name = "listener", hasDefault = true)
+ private FilterContext.OnFrameReceivedListener mListener;
+
+ @GenerateFieldPort(name = "userData", hasDefault = true)
+ private Object mUserData;
+
+ @GenerateFinalPort(name = "callUiThread", hasDefault = true)
+ private boolean mCallbacksOnUiThread = true;
+
+ private Handler mUiThreadHandler;
+
+ private class CallbackRunnable implements Runnable {
+ private Filter mFilter;
+ private Frame mFrame;
+ private Object mUserData;
+ private FilterContext.OnFrameReceivedListener mListener;
+
+ public CallbackRunnable(FilterContext.OnFrameReceivedListener listener, Filter filter, Frame frame, Object userData) {
+ mListener = listener;
+ mFilter = filter;
+ mFrame = frame;
+ mUserData = userData;
+ }
+
+ public void run() {
+ mListener.onFrameReceived(mFilter, mFrame, mUserData);
+ mFrame.release();
+ }
+ }
+
+ public CallbackFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addInputPort("frame");
+ }
+
+ public void prepare(FilterContext context) {
+ if (mCallbacksOnUiThread) {
+ mUiThreadHandler = new Handler(Looper.getMainLooper());
+ }
+ }
+
+ public void process(FilterContext context) {
+ // Get frame and forward to listener
+ final Frame input = pullInput("frame");
+ if (mListener != null) {
+ if (mCallbacksOnUiThread) {
+ input.retain();
+ CallbackRunnable uiRunnable = new CallbackRunnable(mListener, this, input, mUserData);
+ if (!mUiThreadHandler.post(uiRunnable)) {
+ throw new RuntimeException("Unable to send callback to UI thread!");
+ }
+ } else {
+ mListener.onFrameReceived(this, input, mUserData);
+ }
+ } else {
+ throw new RuntimeException("CallbackFilter received frame, but no listener set!");
+ }
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/base/FrameBranch.java b/media/mca/filterpacks/java/android/filterpacks/base/FrameBranch.java
new file mode 100644
index 0000000..6b8cbc7
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/base/FrameBranch.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.base;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFinalPort;
+import android.filterfw.core.KeyValueMap;
+
+/**
+ * @hide
+ */
+public class FrameBranch extends Filter {
+
+ @GenerateFinalPort(name = "outputs", hasDefault = true)
+ private int mNumberOfOutputs = 2;
+
+ public FrameBranch(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addInputPort("in");
+ for (int i = 0; i < mNumberOfOutputs; ++i) {
+ addOutputBasedOnInput("out" + i, "in");
+ }
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("in");
+
+ // Push output
+ for (int i = 0; i < mNumberOfOutputs; ++i) {
+ pushOutput("out" + i, input);
+ }
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/base/FrameFetch.java b/media/mca/filterpacks/java/android/filterpacks/base/FrameFetch.java
new file mode 100644
index 0000000..518b837
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/base/FrameFetch.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.base;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GenerateFinalPort;
+
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class FrameFetch extends Filter {
+
+ @GenerateFinalPort(name = "format", hasDefault = true)
+ private FrameFormat mFormat;
+
+ @GenerateFieldPort(name = "key")
+ private String mKey;
+
+ @GenerateFieldPort(name = "repeatFrame", hasDefault = true)
+ private boolean mRepeatFrame = false;
+
+ public FrameFetch(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addOutputPort("frame", mFormat == null ? FrameFormat.unspecified() : mFormat);
+ }
+
+ public void process(FilterContext context) {
+ Frame output = context.fetchFrame(mKey);
+ if (output != null) {
+ pushOutput("frame", output);
+ if (!mRepeatFrame) {
+ closeOutputPort("frame");
+ }
+ } else {
+ delayNextProcess(250);
+ }
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/base/FrameSource.java b/media/mca/filterpacks/java/android/filterpacks/base/FrameSource.java
new file mode 100644
index 0000000..1218d1a
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/base/FrameSource.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.base;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GenerateFinalPort;
+
+/**
+ * @hide
+ */
+public class FrameSource extends Filter {
+
+ @GenerateFinalPort(name = "format")
+ private FrameFormat mFormat;
+
+ @GenerateFieldPort(name = "frame", hasDefault = true)
+ private Frame mFrame = null;
+
+ @GenerateFieldPort(name = "repeatFrame", hasDefault = true)
+ private boolean mRepeatFrame = false;
+
+ public FrameSource(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addOutputPort("frame", mFormat);
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ if (mFrame != null) {
+ // Push output
+ pushOutput("frame", mFrame);
+ }
+
+ if (!mRepeatFrame) {
+ // Close output port as we are done here
+ closeOutputPort("frame");
+ }
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/base/FrameStore.java b/media/mca/filterpacks/java/android/filterpacks/base/FrameStore.java
new file mode 100644
index 0000000..3aadaac
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/base/FrameStore.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.base;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+
+/**
+ * @hide
+ */
+public class FrameStore extends Filter {
+
+ @GenerateFieldPort(name = "key")
+ private String mKey;
+
+ public FrameStore(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addInputPort("frame");
+ }
+
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("frame");
+
+ // Store frame
+ context.storeFrame(mKey, input);
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/base/GLTextureSource.java b/media/mca/filterpacks/java/android/filterpacks/base/GLTextureSource.java
new file mode 100644
index 0000000..1776820
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/base/GLTextureSource.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.base;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GLFrame;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.format.ImageFormat;
+
+import java.util.Set;
+
+/**
+ * @hide
+ */
+public class GLTextureSource extends Filter {
+
+ @GenerateFieldPort(name = "texId")
+ private int mTexId;
+
+ @GenerateFieldPort(name = "width")
+ private int mWidth;
+
+ @GenerateFieldPort(name = "height")
+ private int mHeight;
+
+ @GenerateFieldPort(name = "repeatFrame", hasDefault = true)
+ private boolean mRepeatFrame = false;
+
+ /* This timestamp will be used for all output frames from this source. They
+ * represent nanoseconds, and should be positive and monotonically
+ * increasing. Set to Frame.TIMESTAMP_UNKNOWN if timestamps are not
+ * meaningful for these textures.
+ */
+ @GenerateFieldPort(name = "timestamp", hasDefault = true)
+ private long mTimestamp = Frame.TIMESTAMP_UNKNOWN;
+
+ private Frame mFrame;
+
+ public GLTextureSource(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addOutputPort("frame", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU));
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ // Release frame, so that it is recreated during the next process call
+ if (mFrame != null) {
+ mFrame.release();
+ mFrame = null;
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Generate frame if not generated already
+ if (mFrame == null) {
+ FrameFormat outputFormat = ImageFormat.create(mWidth, mHeight,
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ mFrame = context.getFrameManager().newBoundFrame(outputFormat,
+ GLFrame.EXISTING_TEXTURE_BINDING,
+ mTexId);
+ mFrame.setTimestamp(mTimestamp);
+ }
+
+ // Push output
+ pushOutput("frame", mFrame);
+
+ if (!mRepeatFrame) {
+ // Close output port as we are done here
+ closeOutputPort("frame");
+ }
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ if (mFrame != null) {
+ mFrame.release();
+ }
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/base/GLTextureTarget.java b/media/mca/filterpacks/java/android/filterpacks/base/GLTextureTarget.java
new file mode 100644
index 0000000..b2285cd
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/base/GLTextureTarget.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.base;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GLFrame;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.format.ImageFormat;
+
+import java.util.Set;
+
+/**
+ * @hide
+ */
+public class GLTextureTarget extends Filter {
+
+ @GenerateFieldPort(name = "texId")
+ private int mTexId;
+
+ public GLTextureTarget(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("frame", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("frame");
+
+ FrameFormat format = ImageFormat.create(input.getFormat().getWidth(),
+ input.getFormat().getHeight(),
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+
+ Frame frame = context.getFrameManager().newBoundFrame(format, GLFrame.EXISTING_TEXTURE_BINDING, mTexId);
+
+ // Copy to our texture frame
+ frame.setDataFromFrame(input);
+ frame.release();
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/base/InputStreamSource.java b/media/mca/filterpacks/java/android/filterpacks/base/InputStreamSource.java
new file mode 100644
index 0000000..6c22ee7
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/base/InputStreamSource.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.base;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GenerateFinalPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.format.PrimitiveFormat;
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * @hide
+ */
+public class InputStreamSource extends Filter {
+
+ @GenerateFinalPort(name = "target")
+ private String mTarget;
+
+ @GenerateFieldPort(name = "stream")
+ private InputStream mInputStream;
+
+ @GenerateFinalPort(name = "format", hasDefault = true)
+ private MutableFrameFormat mOutputFormat = null;
+
+ public InputStreamSource(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ int target = FrameFormat.readTargetString(mTarget);
+ if (mOutputFormat == null) {
+ mOutputFormat = PrimitiveFormat.createByteFormat(target);
+ }
+ addOutputPort("data", mOutputFormat);
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ int fileSize = 0;
+ ByteBuffer byteBuffer = null;
+
+ // Read the file
+ try {
+ ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+ byte[] buffer = new byte[1024];
+ int bytesRead;
+ while ((bytesRead = mInputStream.read(buffer)) > 0) {
+ byteStream.write(buffer, 0, bytesRead);
+ fileSize += bytesRead;
+ }
+ byteBuffer = ByteBuffer.wrap(byteStream.toByteArray());
+ } catch (IOException exception) {
+ throw new RuntimeException(
+ "InputStreamSource: Could not read stream: " + exception.getMessage() + "!");
+ }
+
+ // Put it into a frame
+ mOutputFormat.setDimensions(fileSize);
+ Frame output = context.getFrameManager().newFrame(mOutputFormat);
+ output.setData(byteBuffer);
+
+ // Push output
+ pushOutput("data", output);
+
+ // Release pushed frame
+ output.release();
+
+ // Close output port as we are done here
+ closeOutputPort("data");
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/base/NullFilter.java b/media/mca/filterpacks/java/android/filterpacks/base/NullFilter.java
new file mode 100644
index 0000000..f3e08e4
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/base/NullFilter.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.base;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+
+/**
+ * @hide
+ */
+public class NullFilter extends Filter {
+
+ public NullFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addInputPort("frame");
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ pullInput("frame");
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/base/ObjectSource.java b/media/mca/filterpacks/java/android/filterpacks/base/ObjectSource.java
new file mode 100644
index 0000000..d511e44
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/base/ObjectSource.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.base;
+
+import java.util.Set;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GenerateFinalPort;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.format.ObjectFormat;
+
+/**
+ * @hide
+ */
+public class ObjectSource extends Filter {
+
+ @GenerateFieldPort(name = "object")
+ private Object mObject;
+
+ @GenerateFinalPort(name = "format", hasDefault = true)
+ private FrameFormat mOutputFormat = FrameFormat.unspecified();
+
+ @GenerateFieldPort(name = "repeatFrame", hasDefault = true)
+ boolean mRepeatFrame = false;
+
+ private Frame mFrame;
+
+ public ObjectSource(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addOutputPort("frame", mOutputFormat);
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // If no frame has been created, create one now.
+ if (mFrame == null) {
+ if (mObject == null) {
+ throw new NullPointerException("ObjectSource producing frame with no object set!");
+ }
+ FrameFormat outputFormat = ObjectFormat.fromObject(mObject, FrameFormat.TARGET_SIMPLE);
+ mFrame = context.getFrameManager().newFrame(outputFormat);
+ mFrame.setObjectValue(mObject);
+ mFrame.setTimestamp(Frame.TIMESTAMP_UNKNOWN);
+ }
+
+ // Push output
+ pushOutput("frame", mFrame);
+
+ // Wait for free output
+ if (!mRepeatFrame) {
+ closeOutputPort("frame");
+ }
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ mFrame.release();
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ // Release our internal frame, so that it is regenerated on the next call to process().
+ if (name.equals("object")) {
+ if (mFrame != null) {
+ mFrame.release();
+ mFrame = null;
+ }
+ }
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/base/OutputStreamTarget.java b/media/mca/filterpacks/java/android/filterpacks/base/OutputStreamTarget.java
new file mode 100644
index 0000000..3d3d0f1
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/base/OutputStreamTarget.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.base;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+
+import java.io.OutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * @hide
+ */
+public class OutputStreamTarget extends Filter {
+
+ @GenerateFieldPort(name = "stream")
+ private OutputStream mOutputStream;
+
+ public OutputStreamTarget(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addInputPort("data");
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ Frame input = pullInput("data");
+ ByteBuffer data;
+
+ if (input.getFormat().getObjectClass() == String.class) {
+ String stringVal = (String)input.getObjectValue();
+ data = ByteBuffer.wrap(stringVal.getBytes());
+ } else {
+ data = input.getData();
+ }
+ try {
+ mOutputStream.write(data.array(), 0, data.limit());
+ mOutputStream.flush();
+ } catch (IOException exception) {
+ throw new RuntimeException(
+ "OutputStreamTarget: Could not write to stream: " + exception.getMessage() + "!");
+ }
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/base/RetargetFilter.java b/media/mca/filterpacks/java/android/filterpacks/base/RetargetFilter.java
new file mode 100644
index 0000000..254167a
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/base/RetargetFilter.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.base;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GenerateFinalPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+
+/**
+ * @hide
+ */
+public class RetargetFilter extends Filter {
+
+ @GenerateFinalPort(name = "target", hasDefault = false)
+ private String mTargetString;
+
+ private MutableFrameFormat mOutputFormat;
+ private int mTarget = -1;
+
+ public RetargetFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ // Setup target
+ mTarget = FrameFormat.readTargetString(mTargetString);
+
+ // Add ports
+ addInputPort("frame");
+ addOutputBasedOnInput("frame", "frame");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ MutableFrameFormat retargeted = inputFormat.mutableCopy();
+ retargeted.setTarget(mTarget);
+ return retargeted;
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("frame");
+
+ // Create output frame
+ Frame output = context.getFrameManager().duplicateFrameToTarget(input, mTarget);
+
+ // Push output
+ pushOutput("frame", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/AlphaBlendFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/AlphaBlendFilter.java
new file mode 100644
index 0000000..473369c
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/AlphaBlendFilter.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import java.util.Set;
+
+/**
+ * @hide
+ */
+public class AlphaBlendFilter extends ImageCombineFilter {
+
+ private final String mAlphaBlendShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform sampler2D tex_sampler_1;\n" +
+ "uniform sampler2D tex_sampler_2;\n" +
+ "uniform float weight;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 colorL = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " vec4 colorR = texture2D(tex_sampler_1, v_texcoord);\n" +
+ " float blend = texture2D(tex_sampler_2, v_texcoord).r * weight;\n" +
+ " gl_FragColor = colorL * (1.0 - blend) + colorR * blend;\n" +
+ "}\n";
+
+ public AlphaBlendFilter(String name) {
+ super(name, new String[] { "source", "overlay", "mask" }, "blended", "weight");
+ }
+
+ @Override
+ protected Program getNativeProgram(FilterContext context) {
+ throw new RuntimeException("TODO: Write native implementation for AlphaBlend!");
+ }
+
+ @Override
+ protected Program getShaderProgram(FilterContext context) {
+ return new ShaderProgram(context, mAlphaBlendShader);
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/AutoFixFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/AutoFixFilter.java
new file mode 100644
index 0000000..c71c1c9
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/AutoFixFilter.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import android.util.Log;
+
+public class AutoFixFilter extends Filter {
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ @GenerateFieldPort(name = "scale")
+ private float mScale;
+
+ private static final int normal_cdf[] = {
+ 9, 33, 50, 64, 75, 84, 92, 99, 106, 112, 117, 122, 126, 130, 134, 138, 142,
+ 145, 148, 150, 154, 157, 159, 162, 164, 166, 169, 170, 173, 175, 177, 179,
+ 180, 182, 184, 186, 188, 189, 190, 192, 194, 195, 197, 198, 199, 200, 202,
+ 203, 205, 206, 207, 208, 209, 210, 212, 213, 214, 215, 216, 217, 218, 219,
+ 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 229, 230, 231, 232, 233,
+ 234, 235, 236, 236, 237, 238, 239, 239, 240, 240, 242, 242, 243, 244, 245,
+ 245, 246, 247, 247, 248, 249, 249, 250, 250, 251, 252, 253, 253, 254, 255,
+ 255, 256, 256, 257, 258, 258, 259, 259, 259, 260, 261, 262, 262, 263, 263,
+ 264, 264, 265, 265, 266, 267, 267, 268, 268, 269, 269, 269, 270, 270, 271,
+ 272, 272, 273, 273, 274, 274, 275, 275, 276, 276, 277, 277, 277, 278, 278,
+ 279, 279, 279, 280, 280, 281, 282, 282, 282, 283, 283, 284, 284, 285, 285,
+ 285, 286, 286, 287, 287, 288, 288, 288, 289, 289, 289, 290, 290, 290, 291,
+ 292, 292, 292, 293, 293, 294, 294, 294, 295, 295, 296, 296, 296, 297, 297,
+ 297, 298, 298, 298, 299, 299, 299, 299, 300, 300, 301, 301, 302, 302, 302,
+ 303, 303, 304, 304, 304, 305, 305, 305, 306, 306, 306, 307, 307, 307, 308,
+ 308, 308, 309, 309, 309, 309, 310, 310, 310, 310, 311, 312, 312, 312, 313,
+ 313, 313, 314, 314, 314, 315, 315, 315, 315, 316, 316, 316, 317, 317, 317,
+ 318, 318, 318, 319, 319, 319, 319, 319, 320, 320, 320, 321, 321, 322, 322,
+ 322, 323, 323, 323, 323, 324, 324, 324, 325, 325, 325, 325, 326, 326, 326,
+ 327, 327, 327, 327, 328, 328, 328, 329, 329, 329, 329, 329, 330, 330, 330,
+ 330, 331, 331, 332, 332, 332, 333, 333, 333, 333, 334, 334, 334, 334, 335,
+ 335, 335, 336, 336, 336, 336, 337, 337, 337, 337, 338, 338, 338, 339, 339,
+ 339, 339, 339, 339, 340, 340, 340, 340, 341, 341, 342, 342, 342, 342, 343,
+ 343, 343, 344, 344, 344, 344, 345, 345, 345, 345, 346, 346, 346, 346, 347,
+ 347, 347, 347, 348, 348, 348, 348, 349, 349, 349, 349, 349, 349, 350, 350,
+ 350, 350, 351, 351, 352, 352, 352, 352, 353, 353, 353, 353, 354, 354, 354,
+ 354, 355, 355, 355, 355, 356, 356, 356, 356, 357, 357, 357, 357, 358, 358,
+ 358, 358, 359, 359, 359, 359, 359, 359, 359, 360, 360, 360, 360, 361, 361,
+ 362, 362, 362, 362, 363, 363, 363, 363, 364, 364, 364, 364, 365, 365, 365,
+ 365, 366, 366, 366, 366, 366, 367, 367, 367, 367, 368, 368, 368, 368, 369,
+ 369, 369, 369, 369, 369, 370, 370, 370, 370, 370, 371, 371, 372, 372, 372,
+ 372, 373, 373, 373, 373, 374, 374, 374, 374, 374, 375, 375, 375, 375, 376,
+ 376, 376, 376, 377, 377, 377, 377, 378, 378, 378, 378, 378, 379, 379, 379,
+ 379, 379, 379, 380, 380, 380, 380, 381, 381, 381, 382, 382, 382, 382, 383,
+ 383, 383, 383, 384, 384, 384, 384, 385, 385, 385, 385, 385, 386, 386, 386,
+ 386, 387, 387, 387, 387, 388, 388, 388, 388, 388, 389, 389, 389, 389, 389,
+ 389, 390, 390, 390, 390, 391, 391, 392, 392, 392, 392, 392, 393, 393, 393,
+ 393, 394, 394, 394, 394, 395, 395, 395, 395, 396, 396, 396, 396, 396, 397,
+ 397, 397, 397, 398, 398, 398, 398, 399, 399, 399, 399, 399, 399, 400, 400,
+ 400, 400, 400, 401, 401, 402, 402, 402, 402, 403, 403, 403, 403, 404, 404,
+ 404, 404, 405, 405, 405, 405, 406, 406, 406, 406, 406, 407, 407, 407, 407,
+ 408, 408, 408, 408, 409, 409, 409, 409, 409, 409, 410, 410, 410, 410, 411,
+ 411, 412, 412, 412, 412, 413, 413, 413, 413, 414, 414, 414, 414, 415, 415,
+ 415, 415, 416, 416, 416, 416, 417, 417, 417, 417, 418, 418, 418, 418, 419,
+ 419, 419, 419, 419, 419, 420, 420, 420, 420, 421, 421, 422, 422, 422, 422,
+ 423, 423, 423, 423, 424, 424, 424, 425, 425, 425, 425, 426, 426, 426, 426,
+ 427, 427, 427, 427, 428, 428, 428, 429, 429, 429, 429, 429, 429, 430, 430,
+ 430, 430, 431, 431, 432, 432, 432, 433, 433, 433, 433, 434, 434, 434, 435,
+ 435, 435, 435, 436, 436, 436, 436, 437, 437, 437, 438, 438, 438, 438, 439,
+ 439, 439, 439, 439, 440, 440, 440, 441, 441, 442, 442, 442, 443, 443, 443,
+ 443, 444, 444, 444, 445, 445, 445, 446, 446, 446, 446, 447, 447, 447, 448,
+ 448, 448, 449, 449, 449, 449, 449, 450, 450, 450, 451, 451, 452, 452, 452,
+ 453, 453, 453, 454, 454, 454, 455, 455, 455, 456, 456, 456, 457, 457, 457,
+ 458, 458, 458, 459, 459, 459, 459, 460, 460, 460, 461, 461, 462, 462, 462,
+ 463, 463, 463, 464, 464, 465, 465, 465, 466, 466, 466, 467, 467, 467, 468,
+ 468, 469, 469, 469, 469, 470, 470, 470, 471, 472, 472, 472, 473, 473, 474,
+ 474, 474, 475, 475, 476, 476, 476, 477, 477, 478, 478, 478, 479, 479, 479,
+ 480, 480, 480, 481, 482, 482, 483, 483, 484, 484, 484, 485, 485, 486, 486,
+ 487, 487, 488, 488, 488, 489, 489, 489, 490, 490, 491, 492, 492, 493, 493,
+ 494, 494, 495, 495, 496, 496, 497, 497, 498, 498, 499, 499, 499, 500, 501,
+ 502, 502, 503, 503, 504, 504, 505, 505, 506, 507, 507, 508, 508, 509, 509,
+ 510, 510, 511, 512, 513, 513, 514, 515, 515, 516, 517, 517, 518, 519, 519,
+ 519, 520, 521, 522, 523, 524, 524, 525, 526, 526, 527, 528, 529, 529, 530,
+ 531, 532, 533, 534, 535, 535, 536, 537, 538, 539, 539, 540, 542, 543, 544,
+ 545, 546, 547, 548, 549, 549, 550, 552, 553, 554, 555, 556, 558, 559, 559,
+ 561, 562, 564, 565, 566, 568, 569, 570, 572, 574, 575, 577, 578, 579, 582,
+ 583, 585, 587, 589, 590, 593, 595, 597, 599, 602, 604, 607, 609, 612, 615,
+ 618, 620, 624, 628, 631, 635, 639, 644, 649, 654, 659, 666, 673, 680, 690,
+ 700, 714 };
+
+ private final String mAutoFixShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform sampler2D tex_sampler_1;\n" +
+ "uniform sampler2D tex_sampler_2;\n" +
+ "uniform float scale;\n" +
+ "uniform float shift_scale;\n" +
+ "uniform float hist_offset;\n" +
+ "uniform float hist_scale;\n" +
+ "uniform float density_offset;\n" +
+ "uniform float density_scale;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " const vec3 weights = vec3(0.33333, 0.33333, 0.33333);\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " float energy = dot(color.rgb, weights);\n" +
+ " float mask_value = energy - 0.5;\n" +
+ " float alpha;\n" +
+ " if (mask_value > 0.0) {\n" +
+ " alpha = (pow(2.0 * mask_value, 1.5) - 1.0) * scale + 1.0;\n" +
+ " } else { \n" +
+ " alpha = (pow(2.0 * mask_value, 2.0) - 1.0) * scale + 1.0;\n" +
+ " }\n" +
+ " float index = energy * hist_scale + hist_offset;\n" +
+ " vec4 temp = texture2D(tex_sampler_1, vec2(index, 0.5));\n" +
+ " float value = temp.g + temp.r * shift_scale;\n" +
+ " index = value * density_scale + density_offset;\n" +
+ " temp = texture2D(tex_sampler_2, vec2(index, 0.5));\n" +
+ " value = temp.g + temp.r * shift_scale;\n" +
+ " float dst_energy = energy * alpha + value * (1.0 - alpha);\n" +
+ " float max_energy = energy / max(color.r, max(color.g, color.b));\n" +
+ " if (dst_energy > max_energy) {\n" +
+ " dst_energy = max_energy;\n" +
+ " }\n" +
+ " if (energy == 0.0) {\n" +
+ " gl_FragColor = color;\n" +
+ " } else {\n" +
+ " gl_FragColor = vec4(color.rgb * dst_energy / energy, color.a);\n" +
+ " }\n" +
+ "}\n";
+
+ private Program mShaderProgram;
+ private Program mNativeProgram;
+
+ private int mWidth = 0;
+ private int mHeight = 0;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private Frame mHistFrame;
+ private Frame mDensityFrame;
+
+ public AutoFixFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mAutoFixShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mShaderProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ private void initParameters() {
+ mShaderProgram.setHostValue("shift_scale", 1.0f / 256f);
+ mShaderProgram.setHostValue("hist_offset", 0.5f / 766f);
+ mShaderProgram.setHostValue("hist_scale", 765f / 766f);
+ mShaderProgram.setHostValue("density_offset", 0.5f / 1024f);
+ mShaderProgram.setHostValue("density_scale", 1023f / 1024f);
+ mShaderProgram.setHostValue("scale", mScale);
+ }
+
+ @Override
+ protected void prepare(FilterContext context) {
+ int densityDim = 1024;
+ int histDim = 255 * 3 + 1;
+ long precision = (256l * 256l - 1l);
+
+ int[] densityTable = new int[densityDim];
+ for (int i = 0; i < densityDim; ++i) {
+ long temp = normal_cdf[i] * precision / histDim;
+ densityTable[i] = (int) temp;
+ }
+
+ FrameFormat densityFormat = ImageFormat.create(densityDim, 1,
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ mDensityFrame = context.getFrameManager().newFrame(densityFormat);
+ mDensityFrame.setInts(densityTable);
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ if (mDensityFrame != null) {
+ mDensityFrame.release();
+ mDensityFrame = null;
+ }
+
+ if (mHistFrame != null) {
+ mHistFrame.release();
+ mHistFrame = null;
+ }
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mShaderProgram != null) {
+ mShaderProgram.setHostValue("scale", mScale);
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create program if not created already
+ if (mShaderProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ initParameters();
+ }
+
+ // Check if the frame size has changed
+ if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) {
+ mWidth = inputFormat.getWidth();
+ mHeight = inputFormat.getHeight();
+ createHistogramFrame(context, mWidth, mHeight, input.getInts());
+ }
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Process
+ Frame[] inputs = {input, mHistFrame, mDensityFrame};
+ mShaderProgram.process(inputs, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ private void createHistogramFrame(FilterContext context, int width, int height, int[] data) {
+ int histDims = 255 * 3 + 1;
+ int[] histArray = new int[histDims];
+
+ float border_thickness_ratio = 0.05f;
+ int y_border_thickness = (int) (height * border_thickness_ratio);
+ int x_border_thickness = (int) (width * border_thickness_ratio);
+ int pixels = (width - 2 * x_border_thickness) * (height - 2 * y_border_thickness);
+
+ float count = 0f;
+ for (int y = y_border_thickness; y < height - y_border_thickness; ++y) {
+ for (int x = x_border_thickness; x < width - x_border_thickness; ++x) {
+ int index = y * width + x;
+ int energy = (data[index] & 0xFF) + ((data[index] >> 8) & 0xFF) +
+ ((data[index] >> 16) & 0xFF);
+ histArray[energy] ++;
+ }
+ }
+
+ for (int i = 1; i < histDims; i++) {
+ histArray[i] += histArray[i-1];
+ }
+
+ for (int i = 0; i < histDims; i++) {
+ long temp = (256 * 256 - 1l) * histArray[i] / pixels;
+ histArray[i] = (int) temp;
+ }
+
+ FrameFormat shaderHistFormat = ImageFormat.create(histDims, 1,
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ if (mHistFrame != null)
+ mHistFrame.release();
+
+ mHistFrame = context.getFrameManager().newFrame(shaderHistFormat);
+ mHistFrame.setInts(histArray);
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/BitmapOverlayFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/BitmapOverlayFilter.java
new file mode 100644
index 0000000..d4c901f
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/BitmapOverlayFilter.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+import android.filterpacks.imageproc.ImageCombineFilter;
+import android.graphics.Bitmap;
+
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class BitmapOverlayFilter extends Filter {
+
+ @GenerateFieldPort(name = "bitmap")
+ private Bitmap mBitmap;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+ private Frame mFrame;
+
+ private int mWidth = 0;
+ private int mHeight = 0;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private final String mOverlayShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform sampler2D tex_sampler_1;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 original = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " vec4 mask = texture2D(tex_sampler_1, v_texcoord);\n" +
+ " gl_FragColor = vec4(original.rgb * (1.0 - mask.a) + mask.rgb, 1.0);\n" +
+ "}\n";
+
+ public BitmapOverlayFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mOverlayShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter FisheyeFilter does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ if (mFrame != null) {
+ mFrame.release();
+ mFrame = null;
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+
+ // Check if the frame size has changed
+ if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) {
+ mWidth = inputFormat.getWidth();
+ mHeight = inputFormat.getHeight();
+
+ createBitmapFrame(context);
+ }
+
+ // Process
+ Frame[] inputs = {input, mFrame};
+ mProgram.process(inputs, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ private void createBitmapFrame(FilterContext context) {
+ if (mBitmap != null) {
+ FrameFormat format = ImageFormat.create(mBitmap.getWidth(),
+ mBitmap.getHeight(),
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+
+ if (mFrame != null) {
+ mFrame.release();
+ }
+
+ mFrame = context.getFrameManager().newFrame(format);
+ mFrame.setBitmap(mBitmap);
+
+ mBitmap.recycle();
+ mBitmap = null;
+ }
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/BitmapSource.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/BitmapSource.java
new file mode 100644
index 0000000..978fc94
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/BitmapSource.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.imageproc;
+
+import android.content.Context;
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.FrameManager;
+import android.filterfw.core.GenerateFinalPort;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.format.ImageFormat;
+import android.graphics.Bitmap;
+
+/**
+ * @hide
+ */
+public class BitmapSource extends Filter {
+
+ @GenerateFieldPort(name = "target")
+ String mTargetString;
+
+ @GenerateFieldPort(name = "bitmap")
+ private Bitmap mBitmap;
+
+ @GenerateFieldPort(name = "recycleBitmap", hasDefault = true)
+ private boolean mRecycleBitmap = true;
+
+ @GenerateFieldPort(name = "repeatFrame", hasDefault = true)
+ boolean mRepeatFrame = false;
+
+ private int mTarget;
+ private Frame mImageFrame;
+
+ public BitmapSource(String name) {
+ super(name);
+ }
+
+
+ @Override
+ public void setupPorts() {
+ // Setup output format
+ FrameFormat outputFormat = ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_UNSPECIFIED);
+
+ // Add output port
+ addOutputPort("image", outputFormat);
+ }
+
+ public void loadImage(FilterContext filterContext) {
+ // Create frame with bitmap
+ mTarget = FrameFormat.readTargetString(mTargetString);
+ FrameFormat outputFormat = ImageFormat.create(mBitmap.getWidth(),
+ mBitmap.getHeight(),
+ ImageFormat.COLORSPACE_RGBA,
+ mTarget);
+ mImageFrame = filterContext.getFrameManager().newFrame(outputFormat);
+ mImageFrame.setBitmap(mBitmap);
+ mImageFrame.setTimestamp(Frame.TIMESTAMP_UNKNOWN);
+
+ // Free up space used by bitmap
+ if (mRecycleBitmap) {
+ mBitmap.recycle();
+ }
+ mBitmap = null;
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ // Clear image (to trigger reload) in case parameters have been changed
+ if (name.equals("bitmap") || name.equals("target")) {
+ if (mImageFrame != null) {
+ mImageFrame.release();
+ mImageFrame = null;
+ }
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ if (mImageFrame == null) {
+ loadImage(context);
+ }
+
+ pushOutput("image", mImageFrame);
+
+ if (!mRepeatFrame) {
+ closeOutputPort("image");
+ }
+ }
+
+ @Override
+ public void tearDown(FilterContext env) {
+ if (mImageFrame != null) {
+ mImageFrame.release();
+ mImageFrame = null;
+ }
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/BlackWhiteFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/BlackWhiteFilter.java
new file mode 100644
index 0000000..a1cec01
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/BlackWhiteFilter.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import java.util.Random;
+
+public class BlackWhiteFilter extends Filter {
+
+ @GenerateFieldPort(name = "black", hasDefault = true)
+ private float mBlack = 0f;
+
+ @GenerateFieldPort(name = "white", hasDefault = true)
+ private float mWhite = 1f;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+
+ private int mWidth = 0;
+ private int mHeight = 0;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private Frame mNoiseFrame = null;
+ private Random mRandom;
+
+ private final String mBlackWhiteShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform sampler2D tex_sampler_1;\n" +
+ "uniform float black;\n" +
+ "uniform float scale;\n" +
+ "uniform float stepsize;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " float dither = texture2D(tex_sampler_1, v_texcoord).r;\n" +
+ " vec3 xform = clamp((color.rgb - black) * scale, 0.0, 1.0);\n" +
+ " vec3 temp = clamp((color.rgb + stepsize - black) * scale, 0.0, 1.0);\n" +
+ " vec3 new_color = clamp(xform + (temp - xform) * (dither - 0.5), 0.0, 1.0);\n" +
+ " gl_FragColor = vec4(new_color, color.a);\n" +
+ "}\n";
+
+ public BlackWhiteFilter(String name) {
+ super(name);
+
+ mRandom = new Random();
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ if (mNoiseFrame != null) {
+ mNoiseFrame.release();
+ mNoiseFrame = null;
+ }
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mBlackWhiteShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ updateParameters();
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ private void updateParameters() {
+ float scale = (mBlack != mWhite) ? 1.0f / (mWhite - mBlack) : 2000f;
+ float stepsize = 1.0f / 255.0f;
+
+ mProgram.setHostValue("black", mBlack);
+ mProgram.setHostValue("scale", scale);
+ mProgram.setHostValue("stepsize", stepsize);
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mProgram != null) {
+ updateParameters();
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+
+ // Check if the frame size has changed
+ if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) {
+ mWidth = inputFormat.getWidth();
+ mHeight = inputFormat.getHeight();
+
+ if (mNoiseFrame != null) {
+ mNoiseFrame.release();
+ }
+
+ int[] buffer = new int[mWidth * mHeight];
+ for (int i = 0; i < mWidth * mHeight; ++i) {
+ buffer[i] = mRandom.nextInt(255);
+ }
+ FrameFormat format = ImageFormat.create(mWidth, mHeight,
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ mNoiseFrame = context.getFrameManager().newFrame(format);
+ mNoiseFrame.setInts(buffer);
+ }
+
+ if (mNoiseFrame != null && (mNoiseFrame.getFormat().getWidth() != mWidth ||
+ mNoiseFrame.getFormat().getHeight() != mHeight)) {
+ throw new RuntimeException("Random map and imput image size mismatch!");
+ }
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Process
+ Frame[] inputs = {input, mNoiseFrame};
+ mProgram.process(inputs, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/BlendFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/BlendFilter.java
new file mode 100644
index 0000000..29bc8a3
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/BlendFilter.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import java.util.Set;
+
+/**
+ * The filter linearly blends "left" and "right" frames. The blending weight is
+ * the multiplication of parameter "blend" and the alpha value in "right" frame.
+ * @hide
+ */
+public class BlendFilter extends ImageCombineFilter {
+
+ private final String mBlendShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform sampler2D tex_sampler_1;\n" +
+ "uniform float blend;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 colorL = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " vec4 colorR = texture2D(tex_sampler_1, v_texcoord);\n" +
+ " float weight = colorR.a * blend;\n" +
+ " gl_FragColor = mix(colorL, colorR, weight);\n" +
+ "}\n";
+
+ public BlendFilter(String name) {
+ super(name, new String[] { "left", "right" }, "blended", "blend");
+ }
+
+ @Override
+ protected Program getNativeProgram(FilterContext context) {
+ throw new RuntimeException("TODO: Write native implementation for Blend!");
+ }
+
+ @Override
+ protected Program getShaderProgram(FilterContext context) {
+ return new ShaderProgram(context, mBlendShader);
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/BrightnessFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/BrightnessFilter.java
new file mode 100644
index 0000000..046e69d
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/BrightnessFilter.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+
+/**
+ * @hide
+ */
+public class BrightnessFilter extends SimpleImageFilter {
+
+ private static final String mBrightnessShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform float brightness;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " gl_FragColor = brightness * color;\n" +
+ "}\n";
+
+ public BrightnessFilter(String name) {
+ super(name, "brightness");
+ }
+
+ @Override
+ protected Program getNativeProgram(FilterContext context) {
+ return new NativeProgram("filterpack_imageproc", "brightness");
+ }
+
+ @Override
+ protected Program getShaderProgram(FilterContext context) {
+ return new ShaderProgram(context, mBrightnessShader);
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ColorTemperatureFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ColorTemperatureFilter.java
new file mode 100644
index 0000000..19da006
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ColorTemperatureFilter.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+import android.util.Log;
+
+public class ColorTemperatureFilter extends Filter {
+
+ @GenerateFieldPort(name = "scale", hasDefault = true)
+ private float mScale = 0.5f;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private final String mColorTemperatureShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform float scale;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " vec3 new_color = color.rgb;\n" +
+ " new_color.r = color.r + color.r * ( 1.0 - color.r) * scale;\n" +
+ " new_color.b = color.b - color.b * ( 1.0 - color.b) * scale;\n" +
+ " if (scale > 0.0) { \n" +
+ " color.g = color.g + color.g * ( 1.0 - color.g) * scale * 0.25;\n" +
+ " }\n" +
+ " float max_value = max(new_color.r, max(new_color.g, new_color.b));\n" +
+ " if (max_value > 1.0) { \n" +
+ " new_color /= max_value;\n" +
+ " } \n" +
+ " gl_FragColor = vec4(new_color, color.a);\n" +
+ "}\n";
+
+ public ColorTemperatureFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mColorTemperatureShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ updateParameters();
+ }
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ private void updateParameters() {
+ mProgram.setHostValue("scale", 2.0f * mScale - 1.0f);
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mProgram != null) {
+ updateParameters();
+ }
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ContrastFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ContrastFilter.java
new file mode 100644
index 0000000..70e987f
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ContrastFilter.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+
+import java.util.Set;
+
+/**
+ * @hide
+ */
+public class ContrastFilter extends SimpleImageFilter {
+
+ private static final String mContrastShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform float contrast;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " color -= 0.5;\n" +
+ " color *= contrast;\n" +
+ " color += 0.5;\n" +
+ " gl_FragColor = color;\n" + // this will clamp
+ "}\n";
+
+ public ContrastFilter(String name) {
+ super(name, "contrast");
+ }
+
+ @Override
+ protected Program getNativeProgram(FilterContext context) {
+ return new NativeProgram("filterpack_imageproc", "contrast");
+ }
+
+ @Override
+ protected Program getShaderProgram(FilterContext context) {
+ return new ShaderProgram(context, mContrastShader);
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/CropFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/CropFilter.java
new file mode 100644
index 0000000..5222d9c
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/CropFilter.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.geometry.Point;
+import android.filterfw.geometry.Quad;
+import android.filterfw.format.ImageFormat;
+import android.filterfw.format.ObjectFormat;
+
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class CropFilter extends Filter {
+
+ private Program mProgram;
+ private FrameFormat mLastFormat = null;
+
+ @GenerateFieldPort(name = "owidth")
+ private int mOutputWidth = -1;
+
+ @GenerateFieldPort(name = "oheight")
+ private int mOutputHeight = -1;
+
+ @GenerateFieldPort(name = "fillblack")
+ private boolean mFillBlack = false;
+
+ public CropFilter(String name) {
+ super(name);
+ }
+
+ private final String mFragShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " const vec2 lo = vec2(0.0, 0.0);\n" +
+ " const vec2 hi = vec2(1.0, 1.0);\n" +
+ " const vec4 black = vec4(0.0, 0.0, 0.0, 1.0);\n" +
+ " bool out_of_bounds =\n" +
+ " any(lessThan(v_texcoord, lo)) ||\n" +
+ " any(greaterThan(v_texcoord, hi));\n" +
+ " if (out_of_bounds) {\n" +
+ " gl_FragColor = black;\n" +
+ " } else {\n" +
+ " gl_FragColor = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " }\n" +
+ "}\n";
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addMaskedInputPort("box", ObjectFormat.fromClass(Quad.class, FrameFormat.TARGET_SIMPLE));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ // Make sure output size is set to unspecified, as we do not know what we will be resizing
+ // to.
+ MutableFrameFormat outputFormat = inputFormat.mutableCopy();
+ outputFormat.setDimensions(FrameFormat.SIZE_UNSPECIFIED, FrameFormat.SIZE_UNSPECIFIED);
+ return outputFormat;
+ }
+
+ protected void createProgram(FilterContext context, FrameFormat format) {
+ // TODO: Add CPU version
+ if (mLastFormat != null && mLastFormat.getTarget() == format.getTarget()) return;
+ mLastFormat = format;
+ mProgram = null;
+ switch (format.getTarget()) {
+ case FrameFormat.TARGET_GPU:
+ if(mFillBlack)
+ mProgram = new ShaderProgram(context, mFragShader);
+ else
+ mProgram = ShaderProgram.createIdentity(context);
+
+ break;
+ }
+ if (mProgram == null) {
+ throw new RuntimeException("Could not create a program for crop filter " + this + "!");
+ }
+ }
+
+ @Override
+ public void process(FilterContext env) {
+ // Get input frame
+ Frame imageFrame = pullInput("image");
+ Frame boxFrame = pullInput("box");
+
+ createProgram(env, imageFrame.getFormat());
+
+ // Get the box
+ Quad box = (Quad)boxFrame.getObjectValue();
+
+ // Create output format
+ MutableFrameFormat outputFormat = imageFrame.getFormat().mutableCopy();
+ outputFormat.setDimensions(mOutputWidth == -1 ? outputFormat.getWidth() : mOutputWidth,
+ mOutputHeight == -1 ? outputFormat.getHeight() : mOutputHeight);
+
+ // Create output frame
+ Frame output = env.getFrameManager().newFrame(outputFormat);
+
+ // Set the program parameters
+ if (mProgram instanceof ShaderProgram) {
+ ShaderProgram shaderProgram = (ShaderProgram)mProgram;
+ shaderProgram.setSourceRegion(box);
+ }
+
+ mProgram.process(imageFrame, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/CropRectFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/CropRectFilter.java
new file mode 100644
index 0000000..d423d06
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/CropRectFilter.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class CropRectFilter extends Filter {
+
+ @GenerateFieldPort(name = "xorigin")
+ private int mXorigin;
+
+ @GenerateFieldPort(name = "yorigin")
+ private int mYorigin;
+
+ @GenerateFieldPort(name = "width")
+ private int mOutputWidth;
+
+ @GenerateFieldPort(name = "height")
+ private int mOutputHeight;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+
+ private int mWidth = 0;
+ private int mHeight = 0;
+
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ public CropRectFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = ShaderProgram.createIdentity(context);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mProgram != null) {
+ updateSourceRect(mWidth, mHeight);
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create output frame
+ FrameFormat outputFormat = ImageFormat.create(mOutputWidth, mOutputHeight,
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ Frame output = context.getFrameManager().newFrame(outputFormat);
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+
+ // Check if the frame size has changed
+ if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) {
+ updateSourceRect(inputFormat.getWidth(), inputFormat.getHeight());
+ }
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ void updateSourceRect(int width, int height) {
+ mWidth = width;
+ mHeight = height;
+
+ /*
+ Log.e("CropFilter", mWidth + ", " + mHeight + ", " +
+ (float) mXorigin / mWidth + ", " +
+ (float) mYorigin / mHeight + ", " +
+ (float) mOutputWidth / mWidth + ", " +
+ (float) mOutputHeight / mHeight);
+ */
+
+ ((ShaderProgram) mProgram).setSourceRect((float) mXorigin / mWidth,
+ (float) mYorigin / mHeight,
+ (float) mOutputWidth / mWidth,
+ (float) mOutputHeight / mHeight);
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/CrossProcessFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/CrossProcessFilter.java
new file mode 100644
index 0000000..e0514f8
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/CrossProcessFilter.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import android.util.Log;
+
+public class CrossProcessFilter extends Filter {
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private final String mCrossProcessShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " vec3 ncolor = vec3(0.0, 0.0, 0.0);\n" +
+ " float value;\n" +
+ " if (color.r < 0.5) {\n" +
+ " value = color.r;\n" +
+ " } else {\n" +
+ " value = 1.0 - color.r;\n" +
+ " }\n" +
+ " float red = 4.0 * value * value * value;\n" +
+ " if (color.r < 0.5) {\n" +
+ " ncolor.r = red;\n" +
+ " } else {\n" +
+ " ncolor.r = 1.0 - red;\n" +
+ " }\n" +
+ " if (color.g < 0.5) {\n" +
+ " value = color.g;\n" +
+ " } else {\n" +
+ " value = 1.0 - color.g;\n" +
+ " }\n" +
+ " float green = 2.0 * value * value;\n" +
+ " if (color.g < 0.5) {\n" +
+ " ncolor.g = green;\n" +
+ " } else {\n" +
+ " ncolor.g = 1.0 - green;\n" +
+ " }\n" +
+ " ncolor.b = color.b * 0.5 + 0.25;\n" +
+ " gl_FragColor = vec4(ncolor.rgb, color.a);\n" +
+ "}\n";
+
+ public CrossProcessFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mCrossProcessShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter CrossProcess does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/DocumentaryFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/DocumentaryFilter.java
new file mode 100644
index 0000000..3c7b846
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/DocumentaryFilter.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import java.util.Random;
+
+public class DocumentaryFilter extends Filter {
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+
+ private int mWidth = 0;
+ private int mHeight = 0;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private Frame mNoiseFrame;
+ private Random mRandom;
+
+ private final String mDocumentaryShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform sampler2D tex_sampler_1;\n" +
+ "uniform float stepsize;\n" +
+ "uniform float inv_max_dist;\n" +
+ "uniform vec2 center;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ // black white
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " float dither = texture2D(tex_sampler_1, v_texcoord).r;\n" +
+ " vec3 xform = clamp(2.0 * color.rgb, 0.0, 1.0);\n" +
+ " vec3 temp = clamp(2.0 * (color.rgb + stepsize), 0.0, 1.0);\n" +
+ " vec3 new_color = clamp(xform + (temp - xform) * (dither - 0.5), 0.0, 1.0);\n" +
+ // grayscale
+ " float gray = dot(new_color, vec3(0.299, 0.587, 0.114));\n" +
+ " new_color = vec3(gray, gray, gray);\n" +
+ // vignette
+ " float dist = distance(gl_FragCoord.xy, center);\n" +
+ " float lumen = 0.85 / (1.0 + exp((dist * inv_max_dist - 0.83) * 20.0)) + 0.15;\n" +
+ " gl_FragColor = vec4(new_color * lumen, color.a);\n" +
+ "}\n";
+
+ public DocumentaryFilter(String name) {
+ super(name);
+
+ mRandom = new Random();
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ if (mNoiseFrame != null) {
+ mNoiseFrame.release();
+ mNoiseFrame = null;
+ }
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mDocumentaryShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+
+ // Check if the frame size has changed
+ if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) {
+ mWidth = inputFormat.getWidth();
+ mHeight = inputFormat.getHeight();
+
+ int[] buffer = new int[mWidth * mHeight];
+ for (int i = 0; i < mWidth * mHeight; ++i) {
+ buffer[i] = mRandom.nextInt(255);
+ }
+ FrameFormat format = ImageFormat.create(mWidth, mHeight,
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ if (mNoiseFrame != null) {
+ mNoiseFrame.release();
+ }
+ mNoiseFrame = context.getFrameManager().newFrame(format);
+ mNoiseFrame.setInts(buffer);
+
+ initParameters();
+ }
+
+ if (mNoiseFrame != null && (mNoiseFrame.getFormat().getWidth() != mWidth ||
+ mNoiseFrame.getFormat().getHeight() != mHeight)) {
+ throw new RuntimeException("Random map and imput image size mismatch!");
+ }
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Process
+ Frame[] inputs = {input, mNoiseFrame};
+ mProgram.process(inputs, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ private void initParameters() {
+ if (mProgram != null) {
+ float centerX = (float) (0.5 * mWidth);
+ float centerY = (float) (0.5 * mHeight);
+ float center[] = {centerX, centerY};
+ float max_dist = (float) Math.sqrt(centerX * centerX + centerY * centerY);
+
+ mProgram.setHostValue("center", center);
+ mProgram.setHostValue("inv_max_dist", 1.0f / max_dist);
+ mProgram.setHostValue("stepsize", 1.0f / 255.0f);
+ }
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/DrawOverlayFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/DrawOverlayFilter.java
new file mode 100644
index 0000000..3f1711e
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/DrawOverlayFilter.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GLFrame;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.geometry.Quad;
+import android.filterfw.format.ImageFormat;
+import android.filterfw.format.ObjectFormat;
+
+import android.opengl.GLES20;
+
+/**
+ * @hide
+ */
+public class DrawOverlayFilter extends Filter {
+
+ private ShaderProgram mProgram;
+
+ public DrawOverlayFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ FrameFormat imageFormatMask = ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ addMaskedInputPort("source", imageFormatMask);
+ addMaskedInputPort("overlay", imageFormatMask);
+ addMaskedInputPort("box", ObjectFormat.fromClass(Quad.class, FrameFormat.TARGET_SIMPLE));
+ addOutputBasedOnInput("image", "source");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ @Override
+ public void prepare(FilterContext context) {
+ mProgram = ShaderProgram.createIdentity(context);
+ }
+
+ @Override
+ public void process(FilterContext env) {
+ // Get input frame
+ Frame sourceFrame = pullInput("source");
+ Frame overlayFrame = pullInput("overlay");
+ Frame boxFrame = pullInput("box");
+
+ // Get the box
+ Quad box = (Quad)boxFrame.getObjectValue();
+ box = box.translated(1.0f, 1.0f).scaled(2.0f);
+
+ mProgram.setTargetRegion(box);
+
+ // Create output frame with copy of input
+ Frame output = env.getFrameManager().newFrame(sourceFrame.getFormat());
+ output.setDataFromFrame(sourceFrame);
+
+ // Draw onto output
+ mProgram.process(overlayFrame, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/DrawRectFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/DrawRectFilter.java
new file mode 100644
index 0000000..83c9348
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/DrawRectFilter.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GLFrame;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.geometry.Quad;
+import android.filterfw.format.ImageFormat;
+import android.filterfw.format.ObjectFormat;
+
+import android.opengl.GLES20;
+
+/**
+ * @hide
+ */
+public class DrawRectFilter extends Filter {
+
+ @GenerateFieldPort(name = "colorRed", hasDefault = true)
+ private float mColorRed = 0.8f;
+
+ @GenerateFieldPort(name = "colorGreen", hasDefault = true)
+ private float mColorGreen = 0.8f;
+
+ @GenerateFieldPort(name = "colorBlue", hasDefault = true)
+ private float mColorBlue = 0.0f;
+
+ private final String mVertexShader =
+ "attribute vec4 aPosition;\n" +
+ "void main() {\n" +
+ " gl_Position = aPosition;\n" +
+ "}\n";
+
+ private final String mFixedColorFragmentShader =
+ "precision mediump float;\n" +
+ "uniform vec4 color;\n" +
+ "void main() {\n" +
+ " gl_FragColor = color;\n" +
+ "}\n";
+
+ private ShaderProgram mProgram;
+
+
+ public DrawRectFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU));
+ addMaskedInputPort("box", ObjectFormat.fromClass(Quad.class, FrameFormat.TARGET_SIMPLE));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ @Override
+ public void prepare(FilterContext context) {
+ mProgram = new ShaderProgram(context, mVertexShader, mFixedColorFragmentShader);
+ }
+
+ @Override
+ public void process(FilterContext env) {
+ // Get input frame
+ Frame imageFrame = pullInput("image");
+ Frame boxFrame = pullInput("box");
+
+ // Get the box
+ Quad box = (Quad)boxFrame.getObjectValue();
+ box = box.scaled(2.0f).translated(-1.0f, -1.0f);
+
+ // Create output frame with copy of input
+ GLFrame output = (GLFrame)env.getFrameManager().duplicateFrame(imageFrame);
+
+ // Draw onto output
+ output.focus();
+ renderBox(box);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ private void renderBox(Quad box) {
+ final int FLOAT_SIZE = 4;
+
+ // Get current values
+ float[] color = {mColorRed, mColorGreen, mColorBlue, 1f};
+ float[] vertexValues = { box.p0.x, box.p0.y,
+ box.p1.x, box.p1.y,
+ box.p3.x, box.p3.y,
+ box.p2.x, box.p2.y };
+
+ // Set the program variables
+ mProgram.setHostValue("color", color);
+ mProgram.setAttributeValues("aPosition", vertexValues, 2);
+ mProgram.setVertexCount(4);
+
+ // Draw
+ mProgram.beginDrawing();
+ GLES20.glLineWidth(1.0f);
+ GLES20.glDrawArrays(GLES20.GL_LINE_LOOP, 0, 4);
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/DuotoneFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/DuotoneFilter.java
new file mode 100644
index 0000000..d8c88ee
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/DuotoneFilter.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+import android.graphics.Color;
+
+public class DuotoneFilter extends Filter {
+
+ @GenerateFieldPort(name = "first_color", hasDefault = true)
+ private int mFirstColor = 0xFFFF0000;
+
+ @GenerateFieldPort(name = "second_color", hasDefault = true)
+ private int mSecondColor = 0xFFFFFF00;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private final String mDuotoneShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform vec3 first;\n" +
+ "uniform vec3 second;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " float energy = (color.r + color.g + color.b) * 0.3333;\n" +
+ " vec3 new_color = (1.0 - energy) * first + energy * second;\n" +
+ " gl_FragColor = vec4(new_color.rgb, color.a);\n" +
+ "}\n";
+
+ public DuotoneFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mDuotoneShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Duotone does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+ updateParameters();
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ private void updateParameters() {
+ float first[] = { Color.red(mFirstColor)/255f,
+ Color.green(mFirstColor)/255f,
+ Color.blue(mFirstColor)/255f };
+ float second[] = { Color.red(mSecondColor)/255f,
+ Color.green(mSecondColor)/255f,
+ Color.blue(mSecondColor)/255f };
+
+ mProgram.setHostValue("first", first);
+ mProgram.setHostValue("second", second);
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/FillLightFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/FillLightFilter.java
new file mode 100644
index 0000000..fc917a1
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/FillLightFilter.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GenerateFinalPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import android.util.Log;
+
+public class FillLightFilter extends Filter {
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ @GenerateFieldPort(name = "strength", hasDefault = true)
+ private float mBacklight = 0f;
+
+ private Program mProgram;
+
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private final String mFillLightShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform float mult;\n" +
+ "uniform float igamma;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main()\n" +
+ "{\n" +
+ " const vec3 color_weights = vec3(0.25, 0.5, 0.25);\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " float lightmask = dot(color.rgb, color_weights);\n" +
+ " float backmask = (1.0 - lightmask);\n" +
+ " vec3 ones = vec3(1.0, 1.0, 1.0);\n" +
+ " vec3 diff = pow(mult * color.rgb, igamma * ones) - color.rgb;\n" +
+ " diff = min(diff, 1.0);\n" +
+ " vec3 new_color = min(color.rgb + diff * backmask, 1.0);\n" +
+ " gl_FragColor = vec4(new_color, color.a);\n" +
+ "}\n";
+
+ public FillLightFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mFillLightShader);
+ Log.e("FillLight", "tile size: " + mTileSize);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter FillLight does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ updateParameters();
+ }
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mProgram != null) {
+ updateParameters();
+ }
+ }
+
+ private void updateParameters() {
+ float fade_gamma = 0.3f;
+ float amt = 1.0f - mBacklight;
+ float mult = 1.0f / (amt * 0.7f + 0.3f);
+ float faded = fade_gamma + (1.0f -fade_gamma) *mult;
+ float igamma = 1.0f / faded;
+
+ mProgram.setHostValue("mult", mult);
+ mProgram.setHostValue("igamma", igamma);
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/FisheyeFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/FisheyeFilter.java
new file mode 100644
index 0000000..8d38f98
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/FisheyeFilter.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import android.util.Log;
+
+import java.lang.Math;
+import java.util.Set;
+
+/**
+ * @hide
+ */
+public class FisheyeFilter extends Filter {
+ private static final String TAG = "FisheyeFilter";
+
+ // This parameter has range between 0 and 1. It controls the effect of radial distortion.
+ // The larger the value, the more prominent the distortion effect becomes (a straight line
+ // becomes a curve).
+ @GenerateFieldPort(name = "scale", hasDefault = true)
+ private float mScale = 0f;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+
+ private int mWidth = 0;
+ private int mHeight = 0;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private static final String mFisheyeShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform vec2 center;\n" +
+ "uniform float alpha;\n" +
+ "uniform float bound;\n" +
+ "uniform float radius2;\n" +
+ "uniform float factor;\n" +
+ "uniform float inv_height;\n" +
+ "uniform float inv_width;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " const float m_pi_2 = 1.570963;\n" +
+ " float dist = distance(gl_FragCoord.xy, center);\n" +
+ " float radian = m_pi_2 - atan(alpha * sqrt(radius2 - dist * dist), dist);\n" +
+ " float scale = radian * factor / dist;\n" +
+ " vec2 new_coord = gl_FragCoord.xy * scale + (1.0 - scale) * center;\n" +
+ " new_coord.x *= inv_width;\n" +
+ " new_coord.y *= inv_height;\n" +
+ " vec4 color = texture2D(tex_sampler_0, new_coord);\n" +
+ " gl_FragColor = color;\n" +
+ "}\n";
+
+ public FisheyeFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mFisheyeShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter FisheyeFilter does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+
+ // Check if the frame size has changed
+ if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) {
+ updateFrameSize(inputFormat.getWidth(), inputFormat.getHeight());
+ }
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mProgram != null) {
+ updateProgramParams();
+ }
+ }
+
+ private void updateFrameSize(int width, int height) {
+ float center[] = {0.5f * width, 0.5f * height};
+
+ mProgram.setHostValue("center", center);
+ mProgram.setHostValue("inv_width", 1.0f / width);
+ mProgram.setHostValue("inv_height", 1.0f / height);
+
+ mWidth = width;
+ mHeight = height;
+
+ updateProgramParams();
+ }
+
+ private void updateProgramParams() {
+ final float pi = 3.14159265f;
+
+ float alpha = mScale * 2.0f + 0.75f;
+ float bound2 = 0.25f * (mWidth * mWidth + mHeight * mHeight);
+ float bound = (float) Math.sqrt(bound2);
+ float radius = 1.15f * bound;
+ float radius2 = radius * radius;
+ float max_radian = 0.5f * pi -
+ (float) Math.atan(alpha / bound * (float) Math.sqrt(radius2 - bound2));
+ float factor = bound / max_radian;
+
+ mProgram.setHostValue("radius2",radius2);
+ mProgram.setHostValue("factor", factor);
+ mProgram.setHostValue("alpha", (float) (mScale * 2.0 + 0.75));
+ }
+
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/FixedRotationFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/FixedRotationFilter.java
new file mode 100644
index 0000000..3d319ea
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/FixedRotationFilter.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+import android.filterfw.geometry.Point;
+import android.filterfw.geometry.Quad;
+
+/**
+ * The FixedRotationFilter rotates the input image clockwise, it only accepts
+ * 4 rotation angles: 0, 90, 180, 270
+ * @hide
+ */
+public class FixedRotationFilter extends Filter {
+
+ @GenerateFieldPort(name = "rotation", hasDefault = true)
+ private int mRotation = 0;
+
+ private ShaderProgram mProgram = null;
+
+ public FixedRotationFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ Frame input = pullInput("image");
+ if (mRotation == 0) {
+ pushOutput("image", input);
+ return;
+ }
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create program if not created already
+ if (mProgram == null) {
+ mProgram = ShaderProgram.createIdentity(context);
+ }
+ MutableFrameFormat outputFormat = inputFormat.mutableCopy();
+ int width = inputFormat.getWidth();
+ int height = inputFormat.getHeight();
+ Point p1 = new Point(0.0f, 0.0f);
+ Point p2 = new Point(1.0f, 0.0f);
+ Point p3 = new Point(0.0f, 1.0f);
+ Point p4 = new Point(1.0f, 1.0f);
+ Quad sourceRegion;
+ switch (((int)Math.round(mRotation / 90f)) % 4) {
+ case 1:
+ sourceRegion = new Quad(p3,p1,p4,p2);
+ outputFormat.setDimensions(height, width);
+ break;
+ case 2:
+ sourceRegion = new Quad(p4,p3,p2,p1);
+ break;
+ case 3:
+ sourceRegion = new Quad(p2,p4,p1,p3);
+ outputFormat.setDimensions(height, width);
+ break;
+ case 0:
+ default:
+ sourceRegion = new Quad(p1,p2,p3,p4);
+ break;
+ }
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(outputFormat);
+
+ // Set the source region
+ mProgram.setSourceRegion(sourceRegion);
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/FlipFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/FlipFilter.java
new file mode 100644
index 0000000..f8b857b
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/FlipFilter.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+/**
+ * @hide
+ */
+public class FlipFilter extends Filter {
+
+ @GenerateFieldPort(name = "vertical", hasDefault = true)
+ private boolean mVertical = false;
+
+ @GenerateFieldPort(name = "horizontal", hasDefault = true)
+ private boolean mHorizontal = false;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ public FlipFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = ShaderProgram.createIdentity(context);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ updateParameters();
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mProgram != null) {
+ updateParameters();
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ private void updateParameters() {
+ float x_origin = (mHorizontal) ? 1.0f : 0.0f;
+ float y_origin = (mVertical) ? 1.0f : 0.0f;
+
+ float width = (mHorizontal) ? -1.0f : 1.0f;
+ float height = (mVertical) ? -1.0f : 1.0f;
+
+ ((ShaderProgram) mProgram).setSourceRect(x_origin, y_origin, width, height);
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/GrainFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/GrainFilter.java
new file mode 100644
index 0000000..168a9c6
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/GrainFilter.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+import android.filterfw.geometry.Quad;
+import android.filterfw.geometry.Point;
+
+import java.util.Random;
+
+public class GrainFilter extends Filter {
+
+ private static final int RAND_THRESHOLD = 128;
+
+ @GenerateFieldPort(name = "strength", hasDefault = true)
+ private float mScale = 0f;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+
+ private int mWidth = 0;
+ private int mHeight = 0;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private Frame mNoiseFrame = null;
+ private Random mRandom;
+
+ private final String mGrainShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform sampler2D tex_sampler_1;\n" +
+ "uniform float scale;\n" +
+ "uniform float stepX;\n" +
+ "uniform float stepY;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " float noise = texture2D(tex_sampler_1, v_texcoord + vec2(-stepX, -stepY)).r * 0.224;\n" +
+ " noise += texture2D(tex_sampler_1, v_texcoord + vec2(-stepX, stepY)).r * 0.224;\n" +
+ " noise += texture2D(tex_sampler_1, v_texcoord + vec2(stepX, -stepY)).r * 0.224;\n" +
+ " noise += texture2D(tex_sampler_1, v_texcoord + vec2(stepX, stepY)).r * 0.224;\n" +
+ " noise += 0.4448;\n" +
+ " noise *= scale;\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " float energy = 0.33333 * color.r + 0.33333 * color.g + 0.33333 * color.b;\n" +
+ " float mask = (1.0 - sqrt(energy));\n" +
+ " float weight = 1.0 - 1.333 * mask * noise;\n" +
+ " gl_FragColor = vec4(color.rgb * weight, color.a);\n" +
+ "}\n";
+
+ public GrainFilter(String name) {
+ super(name);
+
+ mRandom = new Random();
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mGrainShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ private void updateParameters() {
+ mProgram.setHostValue("scale", mScale);
+ }
+
+ private void updateFrameSize(int width, int height) {
+ mWidth = width;
+ mHeight = height;
+
+ if (mProgram != null) {
+ mProgram.setHostValue("stepX", 0.5f / mWidth);
+ mProgram.setHostValue("stepY", 0.5f / mHeight);
+ updateParameters();
+ }
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mProgram != null) {
+ updateParameters();
+ }
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ if (mNoiseFrame != null) {
+ mNoiseFrame.release();
+ mNoiseFrame = null;
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ updateParameters();
+ }
+
+ // Check if the frame size has changed
+ if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) {
+ updateFrameSize(inputFormat.getWidth(), inputFormat.getHeight());
+
+ int[] buffer = new int[mWidth * mHeight];
+ for (int i = 0; i < mWidth * mHeight; ++i) {
+ buffer[i] = (mRandom.nextInt(256) < RAND_THRESHOLD) ?
+ mRandom.nextInt(256) : 0;
+ }
+ FrameFormat format = ImageFormat.create(mWidth, mHeight,
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ if (mNoiseFrame != null) {
+ mNoiseFrame.release();
+ }
+ mNoiseFrame = context.getFrameManager().newFrame(format);
+ mNoiseFrame.setInts(buffer);
+ }
+
+ if (mNoiseFrame.getFormat().getWidth() != mWidth ||
+ mNoiseFrame.getFormat().getHeight() != mHeight) {
+ throw new RuntimeException("Random map and imput image size mismatch!");
+ }
+
+ // Process
+ Frame[] inputs = {input, mNoiseFrame};
+ mProgram.process(inputs, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageCombineFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageCombineFilter.java
new file mode 100644
index 0000000..858489b
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageCombineFilter.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import java.lang.reflect.Field;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @hide
+ */
+public abstract class ImageCombineFilter extends Filter {
+
+ protected Program mProgram;
+ protected String[] mInputNames;
+ protected String mOutputName;
+ protected String mParameterName;
+ protected int mCurrentTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ public ImageCombineFilter(String name,
+ String[] inputNames,
+ String outputName,
+ String parameterName) {
+ super(name);
+ mInputNames = inputNames;
+ mOutputName = outputName;
+ mParameterName = parameterName;
+ }
+
+ @Override
+ public void setupPorts() {
+ if (mParameterName != null) {
+ try {
+ Field programField = ImageCombineFilter.class.getDeclaredField("mProgram");
+ addProgramPort(mParameterName, mParameterName, programField, float.class, false);
+ } catch (NoSuchFieldException e) {
+ throw new RuntimeException("Internal Error: mProgram field not found!");
+ }
+ }
+ for (String inputName : mInputNames) {
+ addMaskedInputPort(inputName, ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ }
+ addOutputBasedOnInput(mOutputName, mInputNames[0]);
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ private void assertAllInputTargetsMatch() {
+ int target = getInputFormat(mInputNames[0]).getTarget();
+ for (String inputName : mInputNames) {
+ if (target != getInputFormat(inputName).getTarget()) {
+ throw new RuntimeException("Type mismatch of input formats in filter " + this
+ + ". All input frames must have the same target!");
+ }
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Pull input frames
+ int i = 0;
+ Frame[] inputs = new Frame[mInputNames.length];
+ for (String inputName : mInputNames) {
+ inputs[i++] = pullInput(inputName);
+ }
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputs[0].getFormat());
+
+ // Make sure we have a program
+ updateProgramWithTarget(inputs[0].getFormat().getTarget(), context);
+
+ // Process
+ mProgram.process(inputs, output);
+
+ // Push output
+ pushOutput(mOutputName, output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ protected void updateProgramWithTarget(int target, FilterContext context) {
+ if (target != mCurrentTarget) {
+ switch (target) {
+ case FrameFormat.TARGET_NATIVE:
+ mProgram = getNativeProgram(context);
+ break;
+
+ case FrameFormat.TARGET_GPU:
+ mProgram = getShaderProgram(context);
+ break;
+
+ default:
+ mProgram = null;
+ break;
+ }
+ if (mProgram == null) {
+ throw new RuntimeException("Could not create a program for image filter "
+ + this + "!");
+ }
+ initProgramInputs(mProgram, context);
+ mCurrentTarget = target;
+ }
+ }
+
+ protected abstract Program getNativeProgram(FilterContext context);
+
+ protected abstract Program getShaderProgram(FilterContext context);
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageEncoder.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageEncoder.java
new file mode 100644
index 0000000..a5405cb
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageEncoder.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.imageproc;
+
+import android.content.Context;
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.format.ImageFormat;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
+
+import android.util.Log;
+
+import java.io.OutputStream;
+import java.io.IOException;
+
+/**
+ * @hide
+ */
+public class ImageEncoder extends Filter {
+
+ @GenerateFieldPort(name = "stream")
+ private OutputStream mOutputStream;
+
+ @GenerateFieldPort(name = "quality", hasDefault = true)
+ private int mQuality = 80;
+
+ public ImageEncoder(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_UNSPECIFIED));
+ }
+
+ @Override
+ public void process(FilterContext env) {
+ Frame input = pullInput("image");
+ Bitmap bitmap = input.getBitmap();
+ bitmap.compress(CompressFormat.JPEG, mQuality, mOutputStream);
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageSlicer.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageSlicer.java
new file mode 100644
index 0000000..b996eb8
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageSlicer.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.FrameManager;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+public class ImageSlicer extends Filter {
+
+ @GenerateFieldPort(name = "xSlices")
+ private int mXSlices;
+
+ @GenerateFieldPort(name = "ySlices")
+ private int mYSlices;
+
+ @GenerateFieldPort(name = "padSize")
+ private int mPadSize;
+
+ // The current slice index from 0 to xSlices * ySlices
+ private int mSliceIndex;
+
+ private Frame mOriginalFrame;
+
+ private Program mProgram;
+
+ private int mInputWidth;
+ private int mInputHeight;
+
+ private int mSliceWidth;
+ private int mSliceHeight;
+
+ private int mOutputWidth;
+ private int mOutputHeight;
+
+ public ImageSlicer(String name) {
+ super(name);
+ mSliceIndex = 0;
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ private void calcOutputFormatForInput(Frame frame) {
+
+ // calculate the output size based on input size, xSlices, and ySlices
+ mInputWidth = frame.getFormat().getWidth();
+ mInputHeight = frame.getFormat().getHeight();
+
+ mSliceWidth = (mInputWidth + mXSlices - 1) / mXSlices;
+ mSliceHeight = (mInputHeight + mYSlices - 1)/ mYSlices;
+
+ mOutputWidth = mSliceWidth + mPadSize * 2;
+ mOutputHeight = mSliceHeight + mPadSize * 2;
+ }
+
+
+ @Override
+ public void process(FilterContext context) {
+
+ // Get input frame
+ if (mSliceIndex == 0) {
+ mOriginalFrame = pullInput("image");
+ calcOutputFormatForInput(mOriginalFrame);
+ }
+
+ FrameFormat inputFormat = mOriginalFrame.getFormat();
+ MutableFrameFormat outputFormat = inputFormat.mutableCopy();
+ outputFormat.setDimensions(mOutputWidth, mOutputHeight);
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(outputFormat);
+
+ // Create the program if not created already
+ if (mProgram == null) {
+ mProgram = ShaderProgram.createIdentity(context);
+ }
+
+ // Calculate the four corner of the source region
+ int xSliceIndex = mSliceIndex % mXSlices;
+ int ySliceIndex = mSliceIndex / mXSlices;
+
+ // TODO(rslin) : not sure shifting by 0.5 is needed.
+ float x0 = (xSliceIndex * mSliceWidth - mPadSize) / ((float) mInputWidth);
+ float y0 = (ySliceIndex * mSliceHeight - mPadSize) / ((float) mInputHeight);
+
+ ((ShaderProgram) mProgram).setSourceRect(x0, y0,
+ ((float) mOutputWidth) / mInputWidth,
+ ((float) mOutputHeight) / mInputHeight);
+
+ // Process
+ mProgram.process(mOriginalFrame, output);
+ mSliceIndex++;
+
+ if (mSliceIndex == mXSlices * mYSlices) {
+ mSliceIndex = 0;
+ mOriginalFrame.release();
+ setWaitsOnInputPort("image", true);
+ } else {
+ // Retain the original frame so it can be used next time.
+ mOriginalFrame.retain();
+ setWaitsOnInputPort("image", false);
+ }
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageStitcher.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageStitcher.java
new file mode 100644
index 0000000..20aba91
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ImageStitcher.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.FrameManager;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import android.util.Log;
+
+public class ImageStitcher extends Filter {
+
+ @GenerateFieldPort(name = "xSlices")
+ private int mXSlices;
+
+ @GenerateFieldPort(name = "ySlices")
+ private int mYSlices;
+
+ @GenerateFieldPort(name = "padSize")
+ private int mPadSize;
+
+ private Program mProgram;
+ private Frame mOutputFrame;
+
+ private int mInputWidth;
+ private int mInputHeight;
+
+ private int mImageWidth;
+ private int mImageHeight;
+
+ private int mSliceWidth;
+ private int mSliceHeight;
+
+ private int mSliceIndex;
+
+ public ImageStitcher(String name) {
+ super(name);
+ mSliceIndex = 0;
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ private FrameFormat calcOutputFormatForInput(FrameFormat format) {
+ MutableFrameFormat outputFormat = format.mutableCopy();
+
+ mInputWidth = format.getWidth();
+ mInputHeight = format.getHeight();
+
+ mSliceWidth = mInputWidth - 2 * mPadSize;
+ mSliceHeight = mInputHeight - 2 * mPadSize;
+
+ mImageWidth = mSliceWidth * mXSlices;
+ mImageHeight = mSliceHeight * mYSlices;
+
+ outputFormat.setDimensions(mImageWidth, mImageHeight);
+ return outputFormat;
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat format = input.getFormat();
+
+ // Create output frame
+ if (mSliceIndex == 0) {
+ mOutputFrame = context.getFrameManager().newFrame(calcOutputFormatForInput(format));
+ } else {
+ if ((format.getWidth() != mInputWidth) ||
+ (format.getHeight() != mInputHeight)) {
+ // CHECK input format here
+ throw new RuntimeException("Image size should not change.");
+ }
+ }
+
+ // Create the program if not created already
+ if (mProgram == null) {
+ mProgram = ShaderProgram.createIdentity(context);
+ }
+
+ // TODO(rslin) : not sure shifting by 0.5 is needed.
+ float x0 = ((float) mPadSize) / mInputWidth;
+ float y0 = ((float) mPadSize) / mInputHeight;
+
+ int outputOffsetX = (mSliceIndex % mXSlices) * mSliceWidth;
+ int outputOffsetY = (mSliceIndex / mXSlices) * mSliceHeight;
+
+ float outputWidth = (float) Math.min(mSliceWidth, mImageWidth - outputOffsetX);
+ float outputHeight = (float) Math.min(mSliceHeight, mImageHeight - outputOffsetY);
+
+ // We need to set the source rect as well because the input are padded images.
+ ((ShaderProgram) mProgram).setSourceRect(x0, y0,
+ outputWidth / mInputWidth,
+ outputHeight / mInputHeight);
+
+ ((ShaderProgram) mProgram).setTargetRect(((float) outputOffsetX)/ mImageWidth,
+ ((float) outputOffsetY) / mImageHeight,
+ outputWidth / mImageWidth,
+ outputHeight / mImageHeight);
+
+ // Process this tile
+ mProgram.process(input, mOutputFrame);
+ mSliceIndex++;
+
+ // Push output
+ if (mSliceIndex == mXSlices * mYSlices) {
+ pushOutput("image", mOutputFrame);
+ mOutputFrame.release();
+ mSliceIndex = 0;
+ }
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/Invert.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/Invert.java
new file mode 100644
index 0000000..400fd5d
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/Invert.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+
+/**
+ * @hide
+ */
+public class Invert extends SimpleImageFilter {
+
+ private static final String mInvertShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " gl_FragColor.r = 1.0 - color.r;\n" +
+ " gl_FragColor.g = 1.0 - color.g;\n" +
+ " gl_FragColor.b = 1.0 - color.b;\n" +
+ " gl_FragColor.a = color.a;\n" +
+ "}\n";
+
+ public Invert(String name) {
+ super(name, null);
+ }
+
+ @Override
+ protected Program getNativeProgram(FilterContext context) {
+ return new NativeProgram("filterpack_imageproc", "invert");
+ }
+
+ @Override
+ protected Program getShaderProgram(FilterContext context) {
+ return new ShaderProgram(context, mInvertShader);
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/LomoishFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/LomoishFilter.java
new file mode 100644
index 0000000..452a833
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/LomoishFilter.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import java.util.Random;
+
+public class LomoishFilter extends Filter {
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+
+ private int mWidth = 0;
+ private int mHeight = 0;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private Frame mNoiseFrame;
+ private Random mRandom;
+
+ private final String mLomoishShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform sampler2D tex_sampler_1;\n" +
+ "uniform float stepsizeX;\n" +
+ "uniform float stepsizeY;\n" +
+ "uniform float stepsize;\n" +
+ "uniform vec2 center;\n" +
+ "uniform float inv_max_dist;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ // sharpen
+ " vec3 nbr_color = vec3(0.0, 0.0, 0.0);\n" +
+ " vec2 coord;\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " coord.x = v_texcoord.x - 0.5 * stepsizeX;\n" +
+ " coord.y = v_texcoord.y - stepsizeY;\n" +
+ " nbr_color += texture2D(tex_sampler_0, coord).rgb - color.rgb;\n" +
+ " coord.x = v_texcoord.x - stepsizeX;\n" +
+ " coord.y = v_texcoord.y + 0.5 * stepsizeY;\n" +
+ " nbr_color += texture2D(tex_sampler_0, coord).rgb - color.rgb;\n" +
+ " coord.x = v_texcoord.x + stepsizeX;\n" +
+ " coord.y = v_texcoord.y - 0.5 * stepsizeY;\n" +
+ " nbr_color += texture2D(tex_sampler_0, coord).rgb - color.rgb;\n" +
+ " coord.x = v_texcoord.x + stepsizeX;\n" +
+ " coord.y = v_texcoord.y + 0.5 * stepsizeY;\n" +
+ " nbr_color += texture2D(tex_sampler_0, coord).rgb - color.rgb;\n" +
+ " vec3 s_color = vec3(color.rgb + 0.3 * nbr_color);\n" +
+ // cross process
+ " vec3 c_color = vec3(0.0, 0.0, 0.0);\n" +
+ " float value;\n" +
+ " if (s_color.r < 0.5) {\n" +
+ " value = s_color.r;\n" +
+ " } else {\n" +
+ " value = 1.0 - s_color.r;\n" +
+ " }\n" +
+ " float red = 4.0 * value * value * value;\n" +
+ " if (s_color.r < 0.5) {\n" +
+ " c_color.r = red;\n" +
+ " } else {\n" +
+ " c_color.r = 1.0 - red;\n" +
+ " }\n" +
+ " if (s_color.g < 0.5) {\n" +
+ " value = s_color.g;\n" +
+ " } else {\n" +
+ " value = 1.0 - s_color.g;\n" +
+ " }\n" +
+ " float green = 2.0 * value * value;\n" +
+ " if (s_color.g < 0.5) {\n" +
+ " c_color.g = green;\n" +
+ " } else {\n" +
+ " c_color.g = 1.0 - green;\n" +
+ " }\n" +
+ " c_color.b = s_color.b * 0.5 + 0.25;\n" +
+ // blackwhite
+ " float dither = texture2D(tex_sampler_1, v_texcoord).r;\n" +
+ " vec3 xform = clamp((c_color.rgb - 0.15) * 1.53846, 0.0, 1.0);\n" +
+ " vec3 temp = clamp((color.rgb + stepsize - 0.15) * 1.53846, 0.0, 1.0);\n" +
+ " vec3 bw_color = clamp(xform + (temp - xform) * (dither - 0.5), 0.0, 1.0);\n" +
+ // vignette
+ " float dist = distance(gl_FragCoord.xy, center);\n" +
+ " float lumen = 0.85 / (1.0 + exp((dist * inv_max_dist - 0.73) * 20.0)) + 0.15;\n" +
+ " gl_FragColor = vec4(bw_color * lumen, color.a);\n" +
+ "}\n";
+
+ public LomoishFilter(String name) {
+ super(name);
+
+ mRandom = new Random();
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ if (mNoiseFrame != null) {
+ mNoiseFrame.release();
+ mNoiseFrame = null;
+ }
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mLomoishShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ private void initParameters() {
+ if (mProgram !=null) {
+ float centerX = (float) (0.5 * mWidth);
+ float centerY = (float) (0.5 * mHeight);
+ float center[] = {centerX, centerY};
+ float max_dist = (float) Math.sqrt(centerX * centerX + centerY * centerY);
+
+ mProgram.setHostValue("center", center);
+ mProgram.setHostValue("inv_max_dist", 1.0f / max_dist);
+
+ mProgram.setHostValue("stepsize", 1.0f / 255.0f);
+ mProgram.setHostValue("stepsizeX", 1.0f / mWidth);
+ mProgram.setHostValue("stepsizeY", 1.0f / mHeight);
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+
+ // Check if the frame size has changed
+ if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) {
+ mWidth = inputFormat.getWidth();
+ mHeight = inputFormat.getHeight();
+
+ int[] buffer = new int[mWidth * mHeight];
+ for (int i = 0; i < mWidth * mHeight; ++i) {
+ buffer[i] = mRandom.nextInt(255);
+ }
+ FrameFormat format = ImageFormat.create(mWidth, mHeight,
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ if (mNoiseFrame != null) {
+ mNoiseFrame.release();
+ }
+ mNoiseFrame = context.getFrameManager().newFrame(format);
+ mNoiseFrame.setInts(buffer);
+
+ initParameters();
+ }
+
+ if (mNoiseFrame != null && (mNoiseFrame.getFormat().getWidth() != mWidth ||
+ mNoiseFrame.getFormat().getHeight() != mHeight)) {
+ throw new RuntimeException("Random map and imput image size mismatch!");
+ }
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Process
+ Frame[] inputs = {input, mNoiseFrame};
+ mProgram.process(inputs, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/NegativeFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/NegativeFilter.java
new file mode 100644
index 0000000..440d6a6
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/NegativeFilter.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+
+public class NegativeFilter extends Filter {
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private final String mNegativeShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " gl_FragColor = vec4(1.0 - color.rgb, color.a);\n" +
+ "}\n";
+
+ public NegativeFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mNegativeShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/PosterizeFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/PosterizeFilter.java
new file mode 100644
index 0000000..bc2e553
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/PosterizeFilter.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+public class PosterizeFilter extends Filter {
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private final String mPosterizeShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " vec3 pcolor;\n" +
+ " pcolor.r = (color.r >= 0.5) ? 0.75 : 0.25;\n" +
+ " pcolor.g = (color.g >= 0.5) ? 0.75 : 0.25;\n" +
+ " pcolor.b = (color.b >= 0.5) ? 0.75 : 0.25;\n" +
+ " gl_FragColor = vec4(pcolor, color.a);\n" +
+ "}\n";
+
+ public PosterizeFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mPosterizeShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/RedEyeFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/RedEyeFilter.java
new file mode 100644
index 0000000..5632a5e
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/RedEyeFilter.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PointF;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class RedEyeFilter extends Filter {
+
+ private static final float RADIUS_RATIO = 0.06f;
+ private static final float MIN_RADIUS = 10.0f;
+ private static final float DEFAULT_RED_INTENSITY = 1.30f;
+
+ @GenerateFieldPort(name = "centers")
+ private float[] mCenters;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Frame mRedEyeFrame;
+ private Bitmap mRedEyeBitmap;
+
+ private final Canvas mCanvas = new Canvas();
+ private final Paint mPaint = new Paint();
+
+ private float mRadius;
+
+ private int mWidth = 0;
+ private int mHeight = 0;
+
+ private Program mProgram;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private final String mRedEyeShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform sampler2D tex_sampler_1;\n" +
+ "uniform float intensity;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " vec4 mask = texture2D(tex_sampler_1, v_texcoord);\n" +
+ " gl_FragColor = vec4(mask.a, mask.a, mask.a, 1.0) * intensity + color * (1.0 - intensity);\n" +
+ " if (mask.a > 0.0) {\n" +
+ " gl_FragColor.r = 0.0;\n" +
+ " float green_blue = color.g + color.b;\n" +
+ " float red_intensity = color.r / green_blue;\n" +
+ " if (red_intensity > intensity) {\n" +
+ " color.r = 0.5 * green_blue;\n" +
+ " }\n" +
+ " }\n" +
+ " gl_FragColor = color;\n" +
+ "}\n";
+
+ public RedEyeFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mRedEyeShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter RedEye does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ if (mRedEyeBitmap != null) {
+ mRedEyeBitmap.recycle();
+ mRedEyeBitmap = null;
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+
+ // Check if the frame size has changed
+ if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) {
+ mWidth = inputFormat.getWidth();
+ mHeight = inputFormat.getHeight();
+
+ createRedEyeBitmap();
+ }
+
+ createRedEyeFrame(context);
+
+ // Process
+ Frame[] inputs = {input, mRedEyeFrame};
+ mProgram.process(inputs, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+
+ // Release unused frame
+ mRedEyeFrame.release();
+ mRedEyeFrame = null;
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mProgram != null) {
+ updateProgramParams();
+ }
+ }
+
+ private void createRedEyeBitmap() {
+ if (mRedEyeBitmap != null) {
+ mRedEyeBitmap.recycle();
+ }
+
+ int bitmapWidth = mWidth / 2;
+ int bitmapHeight = mHeight / 2;
+
+ mRedEyeBitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
+ mCanvas.setBitmap(mRedEyeBitmap);
+ mPaint.setColor(Color.WHITE);
+ mRadius = Math.max(MIN_RADIUS, RADIUS_RATIO * Math.min(bitmapWidth, bitmapHeight));
+
+ updateProgramParams();
+ }
+
+ private void createRedEyeFrame(FilterContext context) {
+ FrameFormat format = ImageFormat.create(mRedEyeBitmap.getWidth() ,
+ mRedEyeBitmap.getHeight(),
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ mRedEyeFrame = context.getFrameManager().newFrame(format);
+ mRedEyeFrame.setBitmap(mRedEyeBitmap);
+ }
+
+ private void updateProgramParams() {
+ mProgram.setHostValue("intensity", DEFAULT_RED_INTENSITY);
+
+ if ( mCenters.length % 2 == 1) {
+ throw new RuntimeException("The size of center array must be even.");
+ }
+
+ if (mRedEyeBitmap != null) {
+ for (int i = 0; i < mCenters.length; i += 2) {
+ mCanvas.drawCircle(mCenters[i] * mRedEyeBitmap.getWidth(),
+ mCenters[i + 1] * mRedEyeBitmap.getHeight(),
+ mRadius, mPaint);
+ }
+ }
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ResizeFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ResizeFilter.java
new file mode 100644
index 0000000..411e061
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ResizeFilter.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GLFrame;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import android.opengl.GLES20;
+
+/**
+ * @hide
+ */
+public class ResizeFilter extends Filter {
+
+ @GenerateFieldPort(name = "owidth")
+ private int mOWidth;
+ @GenerateFieldPort(name = "oheight")
+ private int mOHeight;
+ @GenerateFieldPort(name = "keepAspectRatio", hasDefault = true)
+ private boolean mKeepAspectRatio = false;
+ @GenerateFieldPort(name = "generateMipMap", hasDefault = true)
+ private boolean mGenerateMipMap = false;
+
+ private Program mProgram;
+ private FrameFormat mLastFormat = null;
+
+ private MutableFrameFormat mOutputFormat;
+ private int mInputChannels;
+
+ public ResizeFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ protected void createProgram(FilterContext context, FrameFormat format) {
+ if (mLastFormat != null && mLastFormat.getTarget() == format.getTarget()) return;
+ mLastFormat = format;
+ switch (format.getTarget()) {
+ case FrameFormat.TARGET_NATIVE:
+ throw new RuntimeException("Native ResizeFilter not implemented yet!");
+
+
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram prog = ShaderProgram.createIdentity(context);
+ mProgram = prog;
+ break;
+
+ default:
+ throw new RuntimeException("ResizeFilter could not create suitable program!");
+ }
+ }
+ @Override
+ public void process(FilterContext env) {
+ // Get input frame
+ Frame input = pullInput("image");
+ createProgram(env, input.getFormat());
+
+ // Create output frame
+ MutableFrameFormat outputFormat = input.getFormat().mutableCopy();
+ if (mKeepAspectRatio) {
+ FrameFormat inputFormat = input.getFormat();
+ mOHeight = mOWidth * inputFormat.getHeight() / inputFormat.getWidth();
+ }
+ outputFormat.setDimensions(mOWidth, mOHeight);
+ Frame output = env.getFrameManager().newFrame(outputFormat);
+
+ // Process
+ if (mGenerateMipMap) {
+ GLFrame mipmapped = (GLFrame)env.getFrameManager().newFrame(input.getFormat());
+ mipmapped.setTextureParameter(GLES20.GL_TEXTURE_MIN_FILTER,
+ GLES20.GL_LINEAR_MIPMAP_NEAREST);
+ mipmapped.setDataFromFrame(input);
+ mipmapped.generateMipMap();
+ mProgram.process(mipmapped, output);
+ mipmapped.release();
+ } else {
+ mProgram.process(input, output);
+ }
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/RotateFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/RotateFilter.java
new file mode 100644
index 0000000..3da7939
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/RotateFilter.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+import android.filterfw.geometry.Quad;
+import android.filterfw.geometry.Point;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class RotateFilter extends Filter {
+
+ @GenerateFieldPort(name = "angle")
+ private int mAngle;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+
+ private int mWidth = 0;
+ private int mHeight = 0;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private int mOutputWidth;
+ private int mOutputHeight;
+
+ public RotateFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = ShaderProgram.createIdentity(context);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ shaderProgram.setClearsOutput(true);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mProgram != null) {
+ updateParameters();
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+
+ if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) {
+ mWidth = inputFormat.getWidth();
+ mHeight = inputFormat.getHeight();
+ mOutputWidth = mWidth;
+ mOutputHeight = mHeight;
+
+ updateParameters();
+ }
+
+ // Create output frame
+ FrameFormat outputFormat = ImageFormat.create(mOutputWidth, mOutputHeight,
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+
+ Frame output = context.getFrameManager().newFrame(outputFormat);
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ private void updateParameters() {
+ float sinTheta;
+ float cosTheta;
+
+ if (mAngle % 90 == 0) {
+ if (mAngle % 180 == 0) {
+ sinTheta = 0f;
+ cosTheta = (mAngle % 360 == 0) ? 1f:-1f;
+ } else {
+ cosTheta = 0f;
+ sinTheta = ((mAngle + 90) % 360 == 0) ? -1f:1f;
+
+ mOutputWidth = mHeight;
+ mOutputHeight = mWidth;
+ }
+ } else {
+ throw new RuntimeException("degree has to be multiply of 90.");
+ }
+
+ Point x0 = new Point(0.5f * (-cosTheta + sinTheta + 1f),
+ 0.5f * (-sinTheta - cosTheta + 1f));
+ Point x1 = new Point(0.5f * (cosTheta + sinTheta + 1f),
+ 0.5f * (sinTheta - cosTheta + 1f));
+ Point x2 = new Point(0.5f * (-cosTheta - sinTheta + 1f),
+ 0.5f * (-sinTheta + cosTheta + 1f));
+ Point x3 = new Point(0.5f * (cosTheta - sinTheta + 1f),
+ 0.5f * (sinTheta + cosTheta + 1f));
+ Quad quad = new Quad(x0, x1, x2, x3);
+ ((ShaderProgram) mProgram).setTargetRegion(quad);
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/SaturateFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/SaturateFilter.java
new file mode 100644
index 0000000..b83af39
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/SaturateFilter.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+public class SaturateFilter extends Filter {
+
+ @GenerateFieldPort(name = "scale", hasDefault = true)
+ private float mScale = 0f;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mBenProgram;
+ private Program mHerfProgram;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private final String mBenSaturateShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform float scale;\n" +
+ "uniform float shift;\n" +
+ "uniform vec3 weights;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " float kv = dot(color.rgb, weights) + shift;\n" +
+ " vec3 new_color = scale * color.rgb + (1.0 - scale) * kv;\n" +
+ " gl_FragColor = vec4(new_color, color.a);\n" +
+ "}\n";
+
+ private final String mHerfSaturateShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform vec3 weights;\n" +
+ "uniform vec3 exponents;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " float de = dot(color.rgb, weights);\n" +
+ " float inv_de = 1.0 / de;\n" +
+ " vec3 new_color = de * pow(color.rgb * inv_de, exponents);\n" +
+ " float max_color = max(max(max(new_color.r, new_color.g), new_color.b), 1.0);\n" +
+ " gl_FragColor = vec4(new_color / max_color, color.a);\n" +
+ "}\n";
+
+
+ public SaturateFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mBenSaturateShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mBenProgram = shaderProgram;
+
+ shaderProgram = new ShaderProgram(context, mHerfSaturateShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mHerfProgram = shaderProgram;
+
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mBenProgram != null && mHerfProgram != null) {
+ updateParameters();
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create program if not created already
+ if (mBenProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ initParameters();
+ }
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Process
+ if (mScale > 0.0f) {
+ mHerfProgram.process(input, output);
+ } else {
+ mBenProgram.process(input, output);
+ }
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ private void initParameters() {
+ float shift = 1.0f / 255.0f;
+ float weights[] = { 2f/8f, 5f/8f, 1f/8f};
+
+ mBenProgram.setHostValue("weights", weights);
+ mBenProgram.setHostValue("shift", shift);
+
+ mHerfProgram.setHostValue("weights", weights);
+
+ updateParameters();
+ }
+
+ private void updateParameters() {
+
+ if (mScale > 0.0f) {
+ float exponents[] = new float[3];
+
+ exponents[0] = (0.9f * mScale) + 1.0f;
+ exponents[1] = (2.1f * mScale) + 1.0f;
+ exponents[2] = (2.7f * mScale) + 1.0f;
+
+ mHerfProgram.setHostValue("exponents", exponents);
+ } else {
+ mBenProgram.setHostValue("scale", 1.0f + mScale);
+ }
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/SepiaFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/SepiaFilter.java
new file mode 100644
index 0000000..7a83fdf
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/SepiaFilter.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+public class SepiaFilter extends Filter {
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private final String mSepiaShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform mat3 matrix;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " vec3 new_color = min(matrix * color.rgb, 1.0);\n" +
+ " gl_FragColor = vec4(new_color.rgb, color.a);\n" +
+ "}\n";
+
+ public SepiaFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mSepiaShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ initParameters();
+ }
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ private void initParameters() {
+ float weights[] = { 805.0f / 2048.0f, 715.0f / 2048.0f, 557.0f / 2048.0f,
+ 1575.0f / 2048.0f, 1405.0f / 2048.0f, 1097.0f / 2048.0f,
+ 387.0f / 2048.0f, 344.0f / 2048.0f, 268.0f / 2048.0f };
+ mProgram.setHostValue("matrix", weights);
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/SharpenFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/SharpenFilter.java
new file mode 100644
index 0000000..256b769
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/SharpenFilter.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import java.util.Set;
+
+public class SharpenFilter extends Filter {
+
+ @GenerateFieldPort(name = "scale", hasDefault = true)
+ private float mScale = 0f;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+
+ private int mWidth = 0;
+ private int mHeight = 0;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private final String mSharpenShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform float scale;\n" +
+ "uniform float stepsizeX;\n" +
+ "uniform float stepsizeY;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec3 nbr_color = vec3(0.0, 0.0, 0.0);\n" +
+ " vec2 coord;\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " coord.x = v_texcoord.x - 0.5 * stepsizeX;\n" +
+ " coord.y = v_texcoord.y - stepsizeY;\n" +
+ " nbr_color += texture2D(tex_sampler_0, coord).rgb - color.rgb;\n" +
+ " coord.x = v_texcoord.x - stepsizeX;\n" +
+ " coord.y = v_texcoord.y + 0.5 * stepsizeY;\n" +
+ " nbr_color += texture2D(tex_sampler_0, coord).rgb - color.rgb;\n" +
+ " coord.x = v_texcoord.x + stepsizeX;\n" +
+ " coord.y = v_texcoord.y - 0.5 * stepsizeY;\n" +
+ " nbr_color += texture2D(tex_sampler_0, coord).rgb - color.rgb;\n" +
+ " coord.x = v_texcoord.x + stepsizeX;\n" +
+ " coord.y = v_texcoord.y + 0.5 * stepsizeY;\n" +
+ " nbr_color += texture2D(tex_sampler_0, coord).rgb - color.rgb;\n" +
+ " gl_FragColor = vec4(color.rgb - 2.0 * scale * nbr_color, color.a);\n" +
+ "}\n";
+
+ public SharpenFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mSharpenShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+
+ // Check if the frame size has changed
+ if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) {
+ updateFrameSize(inputFormat.getWidth(), inputFormat.getHeight());
+ }
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ private void updateFrameSize(int width, int height) {
+ mWidth = width;
+ mHeight = height;
+
+ if (mProgram != null) {
+ mProgram.setHostValue("stepsizeX", 1.0f / mWidth);
+ mProgram.setHostValue("stepsizeY", 1.0f / mHeight);
+ updateParameters();
+ }
+ }
+
+ private void updateParameters() {
+ mProgram.setHostValue("scale", mScale);
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mProgram != null) {
+ updateParameters();
+ }
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/SimpleImageFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/SimpleImageFilter.java
new file mode 100644
index 0000000..f4fc271
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/SimpleImageFilter.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import java.lang.reflect.Field;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @hide
+ */
+public abstract class SimpleImageFilter extends Filter {
+
+ protected int mCurrentTarget = FrameFormat.TARGET_UNSPECIFIED;
+ protected Program mProgram;
+ protected String mParameterName;
+
+ public SimpleImageFilter(String name, String parameterName) {
+ super(name);
+ mParameterName = parameterName;
+ }
+
+ @Override
+ public void setupPorts() {
+ if (mParameterName != null) {
+ try {
+ Field programField = SimpleImageFilter.class.getDeclaredField("mProgram");
+ addProgramPort(mParameterName, mParameterName, programField, float.class, false);
+ } catch (NoSuchFieldException e) {
+ throw new RuntimeException("Internal Error: mProgram field not found!");
+ }
+ }
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Create program if not created already
+ updateProgramWithTarget(inputFormat.getTarget(), context);
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ protected void updateProgramWithTarget(int target, FilterContext context) {
+ if (target != mCurrentTarget) {
+ switch (target) {
+ case FrameFormat.TARGET_NATIVE:
+ mProgram = getNativeProgram(context);
+ break;
+
+ case FrameFormat.TARGET_GPU:
+ mProgram = getShaderProgram(context);
+ break;
+
+ default:
+ mProgram = null;
+ break;
+ }
+ if (mProgram == null) {
+ throw new RuntimeException("Could not create a program for image filter " + this + "!");
+ }
+ initProgramInputs(mProgram, context);
+ mCurrentTarget = target;
+ }
+ }
+
+ protected abstract Program getNativeProgram(FilterContext context);
+
+ protected abstract Program getShaderProgram(FilterContext context);
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/StraightenFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/StraightenFilter.java
new file mode 100644
index 0000000..c9f097d
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/StraightenFilter.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+import android.filterfw.geometry.Quad;
+import android.filterfw.geometry.Point;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class StraightenFilter extends Filter {
+
+ @GenerateFieldPort(name = "angle", hasDefault = true)
+ private float mAngle = 0f;
+
+ @GenerateFieldPort(name = "maxAngle", hasDefault = true)
+ private float mMaxAngle = 45f;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+
+ private int mWidth = 0;
+ private int mHeight = 0;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private static final float DEGREE_TO_RADIAN = (float) Math.PI / 180.0f;
+
+ public StraightenFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = ShaderProgram.createIdentity(context);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mProgram != null) {
+ updateParameters();
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+
+ // Create output frame
+ if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) {
+ mWidth = inputFormat.getWidth();
+ mHeight = inputFormat.getHeight();
+ updateParameters();
+ }
+
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ private void updateParameters() {
+ float cosTheta = (float) Math.cos(mAngle * DEGREE_TO_RADIAN);
+ float sinTheta = (float) Math.sin(mAngle * DEGREE_TO_RADIAN);
+
+ if (mMaxAngle <= 0)
+ throw new RuntimeException("Max angle is out of range (0-180).");
+ mMaxAngle = (mMaxAngle > 90) ? 90 : mMaxAngle;
+
+ Point p0 = new Point(-cosTheta * mWidth + sinTheta * mHeight,
+ -sinTheta * mWidth - cosTheta * mHeight);
+
+ Point p1 = new Point(cosTheta * mWidth + sinTheta * mHeight,
+ sinTheta * mWidth - cosTheta * mHeight);
+
+ Point p2 = new Point(-cosTheta * mWidth - sinTheta * mHeight,
+ -sinTheta * mWidth + cosTheta * mHeight);
+
+ Point p3 = new Point(cosTheta * mWidth - sinTheta * mHeight,
+ sinTheta * mWidth + cosTheta * mHeight);
+
+ float maxWidth = (float) Math.max(Math.abs(p0.x), Math.abs(p1.x));
+ float maxHeight = (float) Math.max(Math.abs(p0.y), Math.abs(p1.y));
+
+ float scale = 0.5f * Math.min( mWidth / maxWidth,
+ mHeight / maxHeight);
+
+ p0.set(scale * p0.x / mWidth + 0.5f, scale * p0.y / mHeight + 0.5f);
+ p1.set(scale * p1.x / mWidth + 0.5f, scale * p1.y / mHeight + 0.5f);
+ p2.set(scale * p2.x / mWidth + 0.5f, scale * p2.y / mHeight + 0.5f);
+ p3.set(scale * p3.x / mWidth + 0.5f, scale * p3.y / mHeight + 0.5f);
+
+ Quad quad = new Quad(p0, p1, p2, p3);
+ ((ShaderProgram) mProgram).setSourceRegion(quad);
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/TintFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/TintFilter.java
new file mode 100644
index 0000000..0da54a5
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/TintFilter.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+import android.graphics.Color;
+
+public class TintFilter extends Filter {
+
+ @GenerateFieldPort(name = "tint", hasDefault = true)
+ private int mTint = 0xFF0000FF;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private final String mTintShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform vec3 tint;\n" +
+ "uniform vec3 color_ratio;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " float avg_color = dot(color_ratio, color.rgb);\n" +
+ " vec3 new_color = min(0.8 * avg_color + 0.2 * tint, 1.0);\n" +
+ " gl_FragColor = vec4(new_color.rgb, color.a);\n" +
+ "}\n";
+
+ public TintFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mTintShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mProgram != null) {
+ updateParameters();
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ initParameters();
+ }
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+ private void initParameters() {
+ float color_ratio[] = {0.21f, 0.71f, 0.07f};
+ mProgram.setHostValue("color_ratio", color_ratio);
+
+ updateParameters();
+ }
+
+ private void updateParameters() {
+ float tint_color[] = {Color.red(mTint) / 255f,
+ Color.green(mTint) / 255f,
+ Color.blue(mTint) / 255f };
+
+ mProgram.setHostValue("tint", tint_color);
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ToGrayFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ToGrayFilter.java
new file mode 100644
index 0000000..00e7bf4
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ToGrayFilter.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import android.util.Log;
+
+import java.lang.reflect.Field;
+
+/**
+ * @hide
+ */
+public class ToGrayFilter extends SimpleImageFilter {
+
+ @GenerateFieldPort(name = "invertSource", hasDefault = true)
+ private boolean mInvertSource = false;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private MutableFrameFormat mOutputFormat;
+
+ private static final String mColorToGray4Shader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " float y = dot(color, vec4(0.299, 0.587, 0.114, 0));\n" +
+ " gl_FragColor = vec4(y, y, y, color.a);\n" +
+ "}\n";
+
+ public ToGrayFilter(String name) {
+ super(name, null);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ protected Program getNativeProgram(FilterContext context) {
+ throw new RuntimeException("Native toGray not implemented yet!");
+ }
+
+ @Override
+ protected Program getShaderProgram(FilterContext context) {
+ int inputChannels = getInputFormat("image").getBytesPerSample();
+ if (inputChannels != 4) {
+ throw new RuntimeException("Unsupported GL input channels: " +
+ inputChannels + "! Channels must be 4!");
+ }
+ ShaderProgram program = new ShaderProgram(context, mColorToGray4Shader);
+ program.setMaximumTileSize(mTileSize);
+ if (mInvertSource)
+ program.setSourceRect(0.0f, 1.0f, 1.0f, -1.0f);
+ return program;
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ToPackedGrayFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ToPackedGrayFilter.java
new file mode 100644
index 0000000..bc4a65e
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ToPackedGrayFilter.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import android.util.Log;
+
+import java.lang.Math;
+/**
+ * @hide
+ */
+public class ToPackedGrayFilter extends Filter {
+
+ @GenerateFieldPort(name = "owidth", hasDefault = true)
+ private int mOWidth = FrameFormat.SIZE_UNSPECIFIED;
+ @GenerateFieldPort(name = "oheight", hasDefault = true)
+ private int mOHeight = FrameFormat.SIZE_UNSPECIFIED;
+ @GenerateFieldPort(name = "keepAspectRatio", hasDefault = true)
+ private boolean mKeepAspectRatio = false;
+
+ private Program mProgram;
+
+ private final String mColorToPackedGrayShader =
+ "precision mediump float;\n" +
+ "const vec4 coeff_y = vec4(0.299, 0.587, 0.114, 0);\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform float pix_stride;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " for (int i = 0; i < 4; ++i) {\n" +
+ " vec4 p = texture2D(tex_sampler_0,\n" +
+ " v_texcoord + vec2(pix_stride * float(i), 0.0));\n" +
+ " gl_FragColor[i] = dot(p, coeff_y);\n" +
+ " }\n" +
+ "}\n";
+
+ public ToPackedGrayFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return convertInputFormat(inputFormat);
+ }
+
+ private void checkOutputDimensions(int outputWidth, int outputHeight) {
+ if (outputWidth <= 0 || outputHeight <= 0) {
+ throw new RuntimeException("Invalid output dimensions: " +
+ outputWidth + " " + outputHeight);
+ }
+ }
+
+ private FrameFormat convertInputFormat(FrameFormat inputFormat) {
+ int ow = mOWidth;
+ int oh = mOHeight;
+ int w = inputFormat.getWidth();
+ int h = inputFormat.getHeight();
+ if (mOWidth == FrameFormat.SIZE_UNSPECIFIED) {
+ ow = w;
+ }
+ if (mOHeight == FrameFormat.SIZE_UNSPECIFIED) {
+ oh = h;
+ }
+ if (mKeepAspectRatio) {
+ // if keep aspect ratio, use the bigger dimension to determine the
+ // final output size
+ if (w > h) {
+ ow = Math.max(ow, oh);
+ oh = ow * h / w;
+ } else {
+ oh = Math.max(ow, oh);
+ ow = oh * w / h;
+ }
+ }
+ ow = (ow > 0 && ow < 4) ? 4 : (ow / 4) * 4; // ensure width is multiple of 4
+ return ImageFormat.create(ow, oh,
+ ImageFormat.COLORSPACE_GRAY,
+ FrameFormat.TARGET_NATIVE);
+ }
+
+ @Override
+ public void prepare(FilterContext context) {
+ mProgram = new ShaderProgram(context, mColorToPackedGrayShader);
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+ FrameFormat outputFormat = convertInputFormat(inputFormat);
+ int ow = outputFormat.getWidth();
+ int oh = outputFormat.getHeight();
+ checkOutputDimensions(ow, oh);
+ mProgram.setHostValue("pix_stride", 1.0f / ow);
+
+ // Do the RGBA to luminance conversion.
+ MutableFrameFormat tempFrameFormat = inputFormat.mutableCopy();
+ tempFrameFormat.setDimensions(ow / 4, oh);
+ Frame temp = context.getFrameManager().newFrame(tempFrameFormat);
+ mProgram.process(input, temp);
+
+ // Read frame from GPU to CPU.
+ Frame output = context.getFrameManager().newFrame(outputFormat);
+ output.setDataFromFrame(temp);
+ temp.release();
+
+ // Push output and yield ownership.
+ pushOutput("image", output);
+ output.release();
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ToRGBAFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ToRGBAFilter.java
new file mode 100644
index 0000000..ab4814f
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ToRGBAFilter.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class ToRGBAFilter extends Filter {
+
+ private int mInputBPP;
+ private Program mProgram;
+ private FrameFormat mLastFormat = null;
+
+ public ToRGBAFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ MutableFrameFormat mask = new MutableFrameFormat(FrameFormat.TYPE_BYTE,
+ FrameFormat.TARGET_NATIVE);
+ mask.setDimensionCount(2);
+ addMaskedInputPort("image", mask);
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return getConvertedFormat(inputFormat);
+ }
+
+ public FrameFormat getConvertedFormat(FrameFormat format) {
+ MutableFrameFormat result = format.mutableCopy();
+ result.setMetaValue(ImageFormat.COLORSPACE_KEY, ImageFormat.COLORSPACE_RGBA);
+ result.setBytesPerSample(4);
+ return result;
+ }
+
+ public void createProgram(FilterContext context, FrameFormat format) {
+ mInputBPP = format.getBytesPerSample();
+ if (mLastFormat != null && mLastFormat.getBytesPerSample() == mInputBPP) return;
+ mLastFormat = format;
+ switch (mInputBPP) {
+ case 1:
+ mProgram = new NativeProgram("filterpack_imageproc", "gray_to_rgba");
+ break;
+ case 3:
+ mProgram = new NativeProgram("filterpack_imageproc", "rgb_to_rgba");
+ break;
+ default:
+ throw new RuntimeException("Unsupported BytesPerPixel: " + mInputBPP + "!");
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ createProgram(context, input.getFormat());
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(getConvertedFormat(input.getFormat()));
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/ToRGBFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/ToRGBFilter.java
new file mode 100644
index 0000000..9258502
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/ToRGBFilter.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class ToRGBFilter extends Filter {
+
+ private int mInputBPP;
+ private Program mProgram;
+ private FrameFormat mLastFormat = null;
+
+ public ToRGBFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ MutableFrameFormat mask = new MutableFrameFormat(FrameFormat.TYPE_BYTE,
+ FrameFormat.TARGET_NATIVE);
+ mask.setDimensionCount(2);
+ addMaskedInputPort("image", mask);
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return getConvertedFormat(inputFormat);
+ }
+
+ public FrameFormat getConvertedFormat(FrameFormat format) {
+ MutableFrameFormat result = format.mutableCopy();
+ result.setMetaValue(ImageFormat.COLORSPACE_KEY, ImageFormat.COLORSPACE_RGB);
+ result.setBytesPerSample(3);
+ return result;
+ }
+
+ public void createProgram(FilterContext context, FrameFormat format) {
+ mInputBPP = format.getBytesPerSample();
+ if (mLastFormat != null && mLastFormat.getBytesPerSample() == mInputBPP) return;
+ mLastFormat = format;
+ switch (mInputBPP) {
+ case 1:
+ mProgram = new NativeProgram("filterpack_imageproc", "gray_to_rgb");
+ break;
+ case 4:
+ mProgram = new NativeProgram("filterpack_imageproc", "rgba_to_rgb");
+ break;
+ default:
+ throw new RuntimeException("Unsupported BytesPerPixel: " + mInputBPP + "!");
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ createProgram(context, input.getFormat());
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(getConvertedFormat(input.getFormat()));
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/VignetteFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/VignetteFilter.java
new file mode 100644
index 0000000..2d78fff
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/VignetteFilter.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.filterpacks.imageproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+public class VignetteFilter extends Filter {
+
+ @GenerateFieldPort(name = "scale", hasDefault = true)
+ private float mScale = 0f;
+
+ @GenerateFieldPort(name = "tile_size", hasDefault = true)
+ private int mTileSize = 640;
+
+ private Program mProgram;
+
+ private int mWidth = 0;
+ private int mHeight = 0;
+ private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
+
+ private final float mSlope = 20.0f;
+ private final float mShade = 0.85f;
+
+ private final String mVignetteShader =
+ "precision mediump float;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform float range;\n" +
+ "uniform float inv_max_dist;\n" +
+ "uniform float shade;\n" +
+ "uniform vec2 center;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " const float slope = 20.0;\n" +
+ " float dist = distance(gl_FragCoord.xy, center);\n" +
+ " float lumen = shade / (1.0 + exp((dist * inv_max_dist - range) * slope)) + (1.0 - shade);\n" +
+ " vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " gl_FragColor = vec4(color.rgb * lumen, color.a);\n" +
+ "}\n";
+
+ public VignetteFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ addOutputBasedOnInput("image", "image");
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ public void initProgram(FilterContext context, int target) {
+ switch (target) {
+ case FrameFormat.TARGET_GPU:
+ ShaderProgram shaderProgram = new ShaderProgram(context, mVignetteShader);
+ shaderProgram.setMaximumTileSize(mTileSize);
+ mProgram = shaderProgram;
+ break;
+
+ default:
+ throw new RuntimeException("Filter Sharpen does not support frames of " +
+ "target " + target + "!");
+ }
+ mTarget = target;
+ }
+
+ private void initParameters() {
+ if (mProgram != null) {
+ float centerX = (float) (0.5 * mWidth);
+ float centerY = (float) (0.5 * mHeight);
+ float center[] = {centerX, centerY};
+ float max_dist = (float) Math.sqrt(centerX * centerX + centerY * centerY);
+
+ mProgram.setHostValue("center", center);
+ mProgram.setHostValue("inv_max_dist", 1.0f / max_dist);
+ mProgram.setHostValue("shade", mShade);
+
+ updateParameters();
+ }
+ }
+
+ private void updateParameters() {
+ // The 'range' is between 1.3 to 0.6. When scale is zero then range is 1.3
+ // which means no vignette at all because the luminousity difference is
+ // less than 1/256 and will cause nothing.
+ mProgram.setHostValue("range", 1.30f - (float) Math.sqrt(mScale) * 0.7f);
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mProgram != null) {
+ updateParameters();
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Get input frame
+ Frame input = pullInput("image");
+ FrameFormat inputFormat = input.getFormat();
+
+ // Create program if not created already
+ if (mProgram == null || inputFormat.getTarget() != mTarget) {
+ initProgram(context, inputFormat.getTarget());
+ }
+
+ // Check if the frame size has changed
+ if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) {
+ mWidth = inputFormat.getWidth();
+ mHeight = inputFormat.getHeight();
+ initParameters();
+ }
+
+ // Create output frame
+ Frame output = context.getFrameManager().newFrame(inputFormat);
+
+ // Process
+ mProgram.process(input, output);
+
+ // Push output
+ pushOutput("image", output);
+
+ // Release pushed frame
+ output.release();
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/package-info.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/package-info.java
new file mode 100644
index 0000000..1cf48b0
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * @hide
+ */
+package android.filterpacks.imageproc;
diff --git a/media/mca/filterpacks/java/android/filterpacks/numeric/SinWaveFilter.java b/media/mca/filterpacks/java/android/filterpacks/numeric/SinWaveFilter.java
new file mode 100644
index 0000000..7e2b4cf
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/numeric/SinWaveFilter.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.numeric;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.format.ObjectFormat;
+
+import java.lang.Math;
+
+/**
+ * @hide
+ */
+public class SinWaveFilter extends Filter {
+
+ @GenerateFieldPort(name = "stepSize", hasDefault = true)
+ private float mStepSize = 0.05f;
+
+ private float mValue = 0.0f;
+
+ private FrameFormat mOutputFormat;
+
+ public SinWaveFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ mOutputFormat = ObjectFormat.fromClass(Float.class, FrameFormat.TARGET_SIMPLE);
+ addOutputPort("value", mOutputFormat);
+ }
+
+ @Override
+ public void open(FilterContext env) {
+ mValue = 0.0f;
+ }
+
+ @Override
+ public void process(FilterContext env) {
+ Frame output = env.getFrameManager().newFrame(mOutputFormat);
+ output.setObjectValue(((float)Math.sin(mValue) + 1.0f) / 2.0f);
+ pushOutput("value", output);
+ mValue += mStepSize;
+ output.release();
+ }
+
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/numeric/package-info.java b/media/mca/filterpacks/java/android/filterpacks/numeric/package-info.java
new file mode 100644
index 0000000..55088eb
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/numeric/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * @hide
+ */
+package android.filterpacks.numeric;
diff --git a/media/mca/filterpacks/java/android/filterpacks/performance/Throughput.java b/media/mca/filterpacks/java/android/filterpacks/performance/Throughput.java
new file mode 100644
index 0000000..51f29f3
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/performance/Throughput.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.performance;
+
+/**
+ * @hide
+ */
+public class Throughput {
+
+ private final int mTotalFrames;
+ private final int mPeriodFrames;
+ private final int mPeriodTime;
+ private final int mPixels;
+
+ public Throughput(int totalFrames, int periodFrames, int periodTime, int pixels) {
+ mTotalFrames = totalFrames;
+ mPeriodFrames = periodFrames;
+ mPeriodTime = periodTime;
+ mPixels = pixels;
+ }
+
+ public int getTotalFrameCount() {
+ return mTotalFrames;
+ }
+
+ public int getPeriodFrameCount() {
+ return mPeriodFrames;
+ }
+
+ public int getPeriodTime() {
+ return mPeriodTime;
+ }
+
+ public float getFramesPerSecond() {
+ return mPeriodFrames / (float)mPeriodTime;
+ }
+
+ public float getNanosPerPixel() {
+ double frameTimeInNanos = (mPeriodTime / (double)mPeriodFrames) * 1000000.0;
+ return (float)(frameTimeInNanos / mPixels);
+ }
+
+ public String toString() {
+ return getFramesPerSecond() + " FPS";
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/performance/ThroughputFilter.java b/media/mca/filterpacks/java/android/filterpacks/performance/ThroughputFilter.java
new file mode 100644
index 0000000..ac837ed
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/performance/ThroughputFilter.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.performance;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.format.ObjectFormat;
+import android.os.SystemClock;
+
+/**
+ * @hide
+ */
+public class ThroughputFilter extends Filter {
+
+ @GenerateFieldPort(name = "period", hasDefault = true)
+ private int mPeriod = 5;
+
+ private long mLastTime = 0;
+
+ private int mTotalFrameCount = 0;
+ private int mPeriodFrameCount = 0;
+
+ private FrameFormat mOutputFormat;
+
+ public ThroughputFilter(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ // Add input ports
+ addInputPort("frame");
+
+ // Add output ports
+ mOutputFormat = ObjectFormat.fromClass(Throughput.class, FrameFormat.TARGET_SIMPLE);
+ addOutputBasedOnInput("frame", "frame");
+ addOutputPort("throughput", mOutputFormat);
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ return inputFormat;
+ }
+
+ @Override
+ public void open(FilterContext env) {
+ mTotalFrameCount = 0;
+ mPeriodFrameCount = 0;
+ mLastTime = 0;
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Pass through input frame
+ Frame input = pullInput("frame");
+ pushOutput("frame", input);
+
+ // Update stats
+ ++mTotalFrameCount;
+ ++mPeriodFrameCount;
+
+ // Check clock
+ if (mLastTime == 0) {
+ mLastTime = SystemClock.elapsedRealtime();
+ }
+ long curTime = SystemClock.elapsedRealtime();
+
+ // Output throughput info if time period is up
+ if ((curTime - mLastTime) >= (mPeriod * 1000)) {
+ FrameFormat inputFormat = input.getFormat();
+ int pixelCount = inputFormat.getWidth() * inputFormat.getHeight();
+ Throughput throughput = new Throughput(mTotalFrameCount,
+ mPeriodFrameCount,
+ mPeriod,
+ pixelCount);
+ Frame throughputFrame = context.getFrameManager().newFrame(mOutputFormat);
+ throughputFrame.setObjectValue(throughput);
+ pushOutput("throughput", throughputFrame);
+ mLastTime = curTime;
+ mPeriodFrameCount = 0;
+ }
+ }
+
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/performance/package-info.java b/media/mca/filterpacks/java/android/filterpacks/performance/package-info.java
new file mode 100644
index 0000000..8b77bbb
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/performance/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * @hide
+ */
+package android.filterpacks.performance;
diff --git a/media/mca/filterpacks/java/android/filterpacks/text/StringLogger.java b/media/mca/filterpacks/java/android/filterpacks/text/StringLogger.java
new file mode 100644
index 0000000..8c7cd69
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/text/StringLogger.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.text;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.format.ObjectFormat;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class StringLogger extends Filter {
+
+ public StringLogger(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ addMaskedInputPort("string", ObjectFormat.fromClass(Object.class,
+ FrameFormat.TARGET_SIMPLE));
+ }
+
+ @Override
+ public void process(FilterContext env) {
+ Frame input = pullInput("string");
+ String inputString = input.getObjectValue().toString();
+ Log.i("StringLogger", inputString);
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/text/StringSource.java b/media/mca/filterpacks/java/android/filterpacks/text/StringSource.java
new file mode 100644
index 0000000..cc33b89
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/text/StringSource.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.text;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.format.ObjectFormat;
+
+/**
+ * @hide
+ */
+public class StringSource extends Filter {
+
+ @GenerateFieldPort(name = "stringValue")
+ private String mString;
+
+ private FrameFormat mOutputFormat;
+
+ public StringSource(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ mOutputFormat = ObjectFormat.fromClass(String.class, FrameFormat.TARGET_SIMPLE);
+ addOutputPort("string", mOutputFormat);
+ }
+
+ @Override
+ public void process(FilterContext env) {
+ Frame output = env.getFrameManager().newFrame(mOutputFormat);
+ output.setObjectValue(mString);
+ output.setTimestamp(Frame.TIMESTAMP_UNKNOWN);
+ pushOutput("string", output);
+ closeOutputPort("string");
+ }
+
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/text/ToUpperCase.java b/media/mca/filterpacks/java/android/filterpacks/text/ToUpperCase.java
new file mode 100644
index 0000000..0cf3477
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/text/ToUpperCase.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.text;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.format.ObjectFormat;
+
+/**
+ * @hide
+ */
+public class ToUpperCase extends Filter {
+
+ private FrameFormat mOutputFormat;
+
+ public ToUpperCase(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setupPorts() {
+ mOutputFormat = ObjectFormat.fromClass(String.class, FrameFormat.TARGET_SIMPLE);
+ addMaskedInputPort("mixedcase", mOutputFormat);
+ addOutputPort("uppercase", mOutputFormat);
+ }
+
+ @Override
+ public void process(FilterContext env) {
+ Frame input = pullInput("mixedcase");
+ String inputString = (String)input.getObjectValue();
+
+ Frame output = env.getFrameManager().newFrame(mOutputFormat);
+ output.setObjectValue(inputString.toUpperCase());
+
+ pushOutput("uppercase", output);
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/text/package-info.java b/media/mca/filterpacks/java/android/filterpacks/text/package-info.java
new file mode 100644
index 0000000..371d3c1
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/text/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * @hide
+ */
+package android.filterpacks.text;
diff --git a/media/mca/filterpacks/java/android/filterpacks/ui/SurfaceRenderFilter.java b/media/mca/filterpacks/java/android/filterpacks/ui/SurfaceRenderFilter.java
new file mode 100644
index 0000000..a5c1ccb
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/ui/SurfaceRenderFilter.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.ui;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.FilterSurfaceView;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GenerateFinalPort;
+import android.filterfw.core.GLEnvironment;
+import android.filterfw.core.GLFrame;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+import android.graphics.Rect;
+
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class SurfaceRenderFilter extends Filter implements SurfaceHolder.Callback {
+
+ private final int RENDERMODE_STRETCH = 0;
+ private final int RENDERMODE_FIT = 1;
+ private final int RENDERMODE_FILL_CROP = 2;
+
+ /** Required. Sets the destination filter surface view for this
+ * node.
+ */
+ @GenerateFinalPort(name = "surfaceView")
+ private FilterSurfaceView mSurfaceView;
+
+ /** Optional. Control how the incoming frames are rendered onto the
+ * output. Default is FIT.
+ * RENDERMODE_STRETCH: Just fill the output surfaceView.
+ * RENDERMODE_FIT: Keep aspect ratio and fit without cropping. May
+ * have black bars.
+ * RENDERMODE_FILL_CROP: Keep aspect ratio and fit without black
+ * bars. May crop.
+ */
+ @GenerateFieldPort(name = "renderMode", hasDefault = true)
+ private String mRenderModeString;
+
+ private boolean mIsBound = false;
+
+ private ShaderProgram mProgram;
+ private GLFrame mScreen;
+ private int mRenderMode = RENDERMODE_FIT;
+ private float mAspectRatio = 1.f;
+
+ private int mScreenWidth;
+ private int mScreenHeight;
+
+ private boolean mLogVerbose;
+ private static final String TAG = "SurfaceRenderFilter";
+
+ public SurfaceRenderFilter(String name) {
+ super(name);
+
+ mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+ }
+
+ @Override
+ public void setupPorts() {
+ // Make sure we have a SurfaceView
+ if (mSurfaceView == null) {
+ throw new RuntimeException("NULL SurfaceView passed to SurfaceRenderFilter");
+ }
+
+ // Add input port
+ addMaskedInputPort("frame", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ }
+
+ public void updateRenderMode() {
+ if (mRenderModeString != null) {
+ if (mRenderModeString.equals("stretch")) {
+ mRenderMode = RENDERMODE_STRETCH;
+ } else if (mRenderModeString.equals("fit")) {
+ mRenderMode = RENDERMODE_FIT;
+ } else if (mRenderModeString.equals("fill_crop")) {
+ mRenderMode = RENDERMODE_FILL_CROP;
+ } else {
+ throw new RuntimeException("Unknown render mode '" + mRenderModeString + "'!");
+ }
+ }
+ updateTargetRect();
+ }
+
+ @Override
+ public void prepare(FilterContext context) {
+ // Create identity shader to render, and make sure to render upside-down, as textures
+ // are stored internally bottom-to-top.
+ mProgram = ShaderProgram.createIdentity(context);
+ mProgram.setSourceRect(0, 1, 1, -1);
+ mProgram.setClearsOutput(true);
+ mProgram.setClearColor(0.0f, 0.0f, 0.0f);
+
+ updateRenderMode();
+
+ // Create a frame representing the screen
+ MutableFrameFormat screenFormat = ImageFormat.create(mSurfaceView.getWidth(),
+ mSurfaceView.getHeight(),
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ mScreen = (GLFrame)context.getFrameManager().newBoundFrame(screenFormat,
+ GLFrame.EXISTING_FBO_BINDING,
+ 0);
+ }
+
+ @Override
+ public void open(FilterContext context) {
+ // Bind surface view to us. This will emit a surfaceCreated and surfaceChanged call that
+ // will update our screen width and height.
+ mSurfaceView.unbind();
+ mSurfaceView.bindToListener(this, context.getGLEnvironment());
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Make sure we are bound to a surface before rendering
+ if (!mIsBound) {
+ Log.w("SurfaceRenderFilter",
+ this + ": Ignoring frame as there is no surface to render to!");
+ return;
+ }
+
+ if (mLogVerbose) Log.v(TAG, "Starting frame processing");
+
+ GLEnvironment glEnv = mSurfaceView.getGLEnv();
+ if (glEnv != context.getGLEnvironment()) {
+ throw new RuntimeException("Surface created under different GLEnvironment!");
+ }
+
+
+ // Get input frame
+ Frame input = pullInput("frame");
+ boolean createdFrame = false;
+
+ float currentAspectRatio = (float)input.getFormat().getWidth() / input.getFormat().getHeight();
+ if (currentAspectRatio != mAspectRatio) {
+ if (mLogVerbose) Log.v(TAG, "New aspect ratio: " + currentAspectRatio +", previously: " + mAspectRatio);
+ mAspectRatio = currentAspectRatio;
+ updateTargetRect();
+ }
+
+ // See if we need to copy to GPU
+ Frame gpuFrame = null;
+ if (mLogVerbose) Log.v("SurfaceRenderFilter", "Got input format: " + input.getFormat());
+ int target = input.getFormat().getTarget();
+ if (target != FrameFormat.TARGET_GPU) {
+ gpuFrame = context.getFrameManager().duplicateFrameToTarget(input,
+ FrameFormat.TARGET_GPU);
+ createdFrame = true;
+ } else {
+ gpuFrame = input;
+ }
+
+ // Activate our surface
+ glEnv.activateSurfaceWithId(mSurfaceView.getSurfaceId());
+
+ // Process
+ mProgram.process(gpuFrame, mScreen);
+
+ // And swap buffers
+ glEnv.swapBuffers();
+
+ if (createdFrame) {
+ gpuFrame.release();
+ }
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ updateTargetRect();
+ }
+
+ @Override
+ public void close(FilterContext context) {
+ mSurfaceView.unbind();
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ if (mScreen != null) {
+ mScreen.release();
+ }
+ }
+
+ @Override
+ public synchronized void surfaceCreated(SurfaceHolder holder) {
+ mIsBound = true;
+ }
+
+ @Override
+ public synchronized void surfaceChanged(SurfaceHolder holder,
+ int format,
+ int width,
+ int height) {
+ // If the screen is null, we do not care about surface changes (yet). Once we have a
+ // screen object, we need to keep track of these changes.
+ if (mScreen != null) {
+ mScreenWidth = width;
+ mScreenHeight = height;
+ mScreen.setViewport(0, 0, mScreenWidth, mScreenHeight);
+ updateTargetRect();
+ }
+ }
+
+ @Override
+ public synchronized void surfaceDestroyed(SurfaceHolder holder) {
+ mIsBound = false;
+ }
+
+ private void updateTargetRect() {
+ if (mScreenWidth > 0 && mScreenHeight > 0 && mProgram != null) {
+ float screenAspectRatio = (float)mScreenWidth / mScreenHeight;
+ float relativeAspectRatio = screenAspectRatio / mAspectRatio;
+
+ switch (mRenderMode) {
+ case RENDERMODE_STRETCH:
+ mProgram.setTargetRect(0, 0, 1, 1);
+ break;
+ case RENDERMODE_FIT:
+ if (relativeAspectRatio > 1.0f) {
+ // Screen is wider than the camera, scale down X
+ mProgram.setTargetRect(0.5f - 0.5f / relativeAspectRatio, 0.0f,
+ 1.0f / relativeAspectRatio, 1.0f);
+ } else {
+ // Screen is taller than the camera, scale down Y
+ mProgram.setTargetRect(0.0f, 0.5f - 0.5f * relativeAspectRatio,
+ 1.0f, relativeAspectRatio);
+ }
+ break;
+ case RENDERMODE_FILL_CROP:
+ if (relativeAspectRatio > 1) {
+ // Screen is wider than the camera, crop in Y
+ mProgram.setTargetRect(0.0f, 0.5f - 0.5f * relativeAspectRatio,
+ 1.0f, relativeAspectRatio);
+ } else {
+ // Screen is taller than the camera, crop in X
+ mProgram.setTargetRect(0.5f - 0.5f / relativeAspectRatio, 0.0f,
+ 1.0f / relativeAspectRatio, 1.0f);
+ }
+ break;
+ }
+ }
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/ui/SurfaceTargetFilter.java b/media/mca/filterpacks/java/android/filterpacks/ui/SurfaceTargetFilter.java
new file mode 100644
index 0000000..308d168
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/ui/SurfaceTargetFilter.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.ui;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GenerateFinalPort;
+import android.filterfw.core.GLEnvironment;
+import android.filterfw.core.GLFrame;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+import android.graphics.Rect;
+
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class SurfaceTargetFilter extends Filter {
+
+ private final int RENDERMODE_STRETCH = 0;
+ private final int RENDERMODE_FIT = 1;
+ private final int RENDERMODE_FILL_CROP = 2;
+
+ /** Required. Sets the destination surface for this node. This assumes that
+ * higher-level code is ensuring that the surface is valid, and properly
+ * updates Surface parameters if they change.
+ */
+ @GenerateFinalPort(name = "surface")
+ private Surface mSurface;
+
+ /** Required. Width of the output surface */
+ @GenerateFieldPort(name = "owidth")
+ private int mScreenWidth;
+
+ /** Required. Height of the output surface */
+ @GenerateFieldPort(name = "oheight")
+ private int mScreenHeight;
+
+ /** Optional. Control how the incoming frames are rendered onto the
+ * output. Default is FIT.
+ * RENDERMODE_STRETCH: Just fill the output surfaceView.
+ * RENDERMODE_FIT: Keep aspect ratio and fit without cropping. May
+ * have black bars.
+ * RENDERMODE_FILL_CROP: Keep aspect ratio and fit without black
+ * bars. May crop.
+ */
+ @GenerateFieldPort(name = "renderMode", hasDefault = true)
+ private String mRenderModeString;
+
+ private ShaderProgram mProgram;
+ private GLEnvironment mGlEnv;
+ private GLFrame mScreen;
+ private int mRenderMode = RENDERMODE_FIT;
+ private float mAspectRatio = 1.f;
+
+ private int mSurfaceId = -1;
+
+ private boolean mLogVerbose;
+ private static final String TAG = "SurfaceRenderFilter";
+
+ public SurfaceTargetFilter(String name) {
+ super(name);
+
+ mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+ }
+
+ @Override
+ public void setupPorts() {
+ // Make sure we have a Surface
+ if (mSurface == null) {
+ throw new RuntimeException("NULL Surface passed to SurfaceTargetFilter");
+ }
+
+ // Add input port
+ addMaskedInputPort("frame", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ }
+
+ public void updateRenderMode() {
+ if (mRenderModeString != null) {
+ if (mRenderModeString.equals("stretch")) {
+ mRenderMode = RENDERMODE_STRETCH;
+ } else if (mRenderModeString.equals("fit")) {
+ mRenderMode = RENDERMODE_FIT;
+ } else if (mRenderModeString.equals("fill_crop")) {
+ mRenderMode = RENDERMODE_FILL_CROP;
+ } else {
+ throw new RuntimeException("Unknown render mode '" + mRenderModeString + "'!");
+ }
+ }
+ updateTargetRect();
+ }
+
+ @Override
+ public void prepare(FilterContext context) {
+ mGlEnv = context.getGLEnvironment();
+
+ // Create identity shader to render, and make sure to render upside-down, as textures
+ // are stored internally bottom-to-top.
+ mProgram = ShaderProgram.createIdentity(context);
+ mProgram.setSourceRect(0, 1, 1, -1);
+ mProgram.setClearsOutput(true);
+ mProgram.setClearColor(0.0f, 0.0f, 0.0f);
+
+ MutableFrameFormat screenFormat = ImageFormat.create(mScreenWidth,
+ mScreenHeight,
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ mScreen = (GLFrame)context.getFrameManager().newBoundFrame(screenFormat,
+ GLFrame.EXISTING_FBO_BINDING,
+ 0);
+
+ // Set up cropping
+ updateRenderMode();
+ }
+
+ @Override
+ public void open(FilterContext context) {
+ registerSurface();
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Starting frame processing");
+
+ // Get input frame
+ Frame input = pullInput("frame");
+ boolean createdFrame = false;
+
+ float currentAspectRatio = (float)input.getFormat().getWidth() / input.getFormat().getHeight();
+ if (currentAspectRatio != mAspectRatio) {
+ if (mLogVerbose) Log.v(TAG, "New aspect ratio: " + currentAspectRatio +", previously: " + mAspectRatio);
+ mAspectRatio = currentAspectRatio;
+ updateTargetRect();
+ }
+
+ // See if we need to copy to GPU
+ Frame gpuFrame = null;
+ if (mLogVerbose) Log.v("SurfaceRenderFilter", "Got input format: " + input.getFormat());
+ int target = input.getFormat().getTarget();
+ if (target != FrameFormat.TARGET_GPU) {
+ gpuFrame = context.getFrameManager().duplicateFrameToTarget(input,
+ FrameFormat.TARGET_GPU);
+ createdFrame = true;
+ } else {
+ gpuFrame = input;
+ }
+
+ // Activate our surface
+ mGlEnv.activateSurfaceWithId(mSurfaceId);
+
+ // Process
+ mProgram.process(gpuFrame, mScreen);
+
+ // And swap buffers
+ mGlEnv.swapBuffers();
+
+ if (createdFrame) {
+ gpuFrame.release();
+ }
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ mScreen.setViewport(0, 0, mScreenWidth, mScreenHeight);
+ updateTargetRect();
+ }
+
+ @Override
+ public void close(FilterContext context) {
+ unregisterSurface();
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ if (mScreen != null) {
+ mScreen.release();
+ }
+ }
+
+ private void updateTargetRect() {
+ if (mScreenWidth > 0 && mScreenHeight > 0 && mProgram != null) {
+ float screenAspectRatio = (float)mScreenWidth / mScreenHeight;
+ float relativeAspectRatio = screenAspectRatio / mAspectRatio;
+
+ switch (mRenderMode) {
+ case RENDERMODE_STRETCH:
+ mProgram.setTargetRect(0, 0, 1, 1);
+ break;
+ case RENDERMODE_FIT:
+ if (relativeAspectRatio > 1.0f) {
+ // Screen is wider than the camera, scale down X
+ mProgram.setTargetRect(0.5f - 0.5f / relativeAspectRatio, 0.0f,
+ 1.0f / relativeAspectRatio, 1.0f);
+ } else {
+ // Screen is taller than the camera, scale down Y
+ mProgram.setTargetRect(0.0f, 0.5f - 0.5f * relativeAspectRatio,
+ 1.0f, relativeAspectRatio);
+ }
+ break;
+ case RENDERMODE_FILL_CROP:
+ if (relativeAspectRatio > 1) {
+ // Screen is wider than the camera, crop in Y
+ mProgram.setTargetRect(0.0f, 0.5f - 0.5f * relativeAspectRatio,
+ 1.0f, relativeAspectRatio);
+ } else {
+ // Screen is taller than the camera, crop in X
+ mProgram.setTargetRect(0.5f - 0.5f / relativeAspectRatio, 0.0f,
+ 1.0f / relativeAspectRatio, 1.0f);
+ }
+ break;
+ }
+ }
+ }
+
+ private void registerSurface() {
+ mSurfaceId = mGlEnv.registerSurface(mSurface);
+ if (mSurfaceId < 0) {
+ throw new RuntimeException("Could not register Surface: " + mSurface);
+ }
+ }
+
+ private void unregisterSurface() {
+ if (mSurfaceId > 0) {
+ mGlEnv.unregisterSurfaceId(mSurfaceId);
+ }
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/ui/package-info.java b/media/mca/filterpacks/java/android/filterpacks/ui/package-info.java
new file mode 100644
index 0000000..9ed7d51
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/ui/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * @hide
+ */
+package android.filterpacks.ui;
diff --git a/media/mca/filterpacks/java/android/filterpacks/videoproc/BackDropperFilter.java b/media/mca/filterpacks/java/android/filterpacks/videoproc/BackDropperFilter.java
new file mode 100644
index 0000000..52c9fda
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/videoproc/BackDropperFilter.java
@@ -0,0 +1,976 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.filterpacks.videoproc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GenerateFinalPort;
+import android.filterfw.core.Frame;
+import android.filterfw.core.GLFrame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+import android.opengl.GLES20;
+import android.os.SystemClock;
+import android.util.Log;
+
+import java.lang.ArrayIndexOutOfBoundsException;
+import java.lang.Math;
+import java.util.Arrays;
+import java.nio.ByteBuffer;
+
+/**
+ * @hide
+ */
+public class BackDropperFilter extends Filter {
+ /** User-visible parameters */
+
+ private final int BACKGROUND_STRETCH = 0;
+ private final int BACKGROUND_FIT = 1;
+ private final int BACKGROUND_FILL_CROP = 2;
+
+ @GenerateFieldPort(name = "backgroundFitMode", hasDefault = true)
+ private int mBackgroundFitMode = BACKGROUND_FILL_CROP;
+ @GenerateFieldPort(name = "learningDuration", hasDefault = true)
+ private int mLearningDuration = DEFAULT_LEARNING_DURATION;
+ @GenerateFieldPort(name = "learningVerifyDuration", hasDefault = true)
+ private int mLearningVerifyDuration = DEFAULT_LEARNING_VERIFY_DURATION;
+ @GenerateFieldPort(name = "acceptStddev", hasDefault = true)
+ private float mAcceptStddev = DEFAULT_ACCEPT_STDDEV;
+ @GenerateFieldPort(name = "hierLrgScale", hasDefault = true)
+ private float mHierarchyLrgScale = DEFAULT_HIER_LRG_SCALE;
+ @GenerateFieldPort(name = "hierMidScale", hasDefault = true)
+ private float mHierarchyMidScale = DEFAULT_HIER_MID_SCALE;
+ @GenerateFieldPort(name = "hierSmlScale", hasDefault = true)
+ private float mHierarchySmlScale = DEFAULT_HIER_SML_SCALE;
+
+ // Dimensions of foreground / background mask. Optimum value should take into account only
+ // image contents, NOT dimensions of input video stream.
+ @GenerateFieldPort(name = "maskWidthExp", hasDefault = true)
+ private int mMaskWidthExp = DEFAULT_MASK_WIDTH_EXPONENT;
+ @GenerateFieldPort(name = "maskHeightExp", hasDefault = true)
+ private int mMaskHeightExp = DEFAULT_MASK_HEIGHT_EXPONENT;
+
+ // Levels at which to compute foreground / background decision. Think of them as are deltas
+ // SUBTRACTED from maskWidthExp and maskHeightExp.
+ @GenerateFieldPort(name = "hierLrgExp", hasDefault = true)
+ private int mHierarchyLrgExp = DEFAULT_HIER_LRG_EXPONENT;
+ @GenerateFieldPort(name = "hierMidExp", hasDefault = true)
+ private int mHierarchyMidExp = DEFAULT_HIER_MID_EXPONENT;
+ @GenerateFieldPort(name = "hierSmlExp", hasDefault = true)
+ private int mHierarchySmlExp = DEFAULT_HIER_SML_EXPONENT;
+
+ @GenerateFieldPort(name = "lumScale", hasDefault = true)
+ private float mLumScale = DEFAULT_Y_SCALE_FACTOR;
+ @GenerateFieldPort(name = "chromaScale", hasDefault = true)
+ private float mChromaScale = DEFAULT_UV_SCALE_FACTOR;
+ @GenerateFieldPort(name = "maskBg", hasDefault = true)
+ private float mMaskBg = DEFAULT_MASK_BLEND_BG;
+ @GenerateFieldPort(name = "maskFg", hasDefault = true)
+ private float mMaskFg = DEFAULT_MASK_BLEND_FG;
+ @GenerateFieldPort(name = "exposureChange", hasDefault = true)
+ private float mExposureChange = DEFAULT_EXPOSURE_CHANGE;
+ @GenerateFieldPort(name = "whitebalanceredChange", hasDefault = true)
+ private float mWhiteBalanceRedChange = DEFAULT_WHITE_BALANCE_RED_CHANGE;
+ @GenerateFieldPort(name = "whitebalanceblueChange", hasDefault = true)
+ private float mWhiteBalanceBlueChange = DEFAULT_WHITE_BALANCE_BLUE_CHANGE;
+ @GenerateFieldPort(name = "autowbToggle", hasDefault = true)
+ private int mAutoWBToggle = DEFAULT_WHITE_BALANCE_TOGGLE;
+
+ // TODO: These are not updatable:
+ @GenerateFieldPort(name = "learningAdaptRate", hasDefault = true)
+ private float mAdaptRateLearning = DEFAULT_LEARNING_ADAPT_RATE;
+ @GenerateFieldPort(name = "adaptRateBg", hasDefault = true)
+ private float mAdaptRateBg = DEFAULT_ADAPT_RATE_BG;
+ @GenerateFieldPort(name = "adaptRateFg", hasDefault = true)
+ private float mAdaptRateFg = DEFAULT_ADAPT_RATE_FG;
+ @GenerateFieldPort(name = "maskVerifyRate", hasDefault = true)
+ private float mVerifyRate = DEFAULT_MASK_VERIFY_RATE;
+ @GenerateFieldPort(name = "learningDoneListener", hasDefault = true)
+ private LearningDoneListener mLearningDoneListener = null;
+
+ @GenerateFieldPort(name = "useTheForce", hasDefault = true)
+ private boolean mUseTheForce = false;
+
+ @GenerateFinalPort(name = "provideDebugOutputs", hasDefault = true)
+ private boolean mProvideDebugOutputs = false;
+
+ // Whether to mirror the background or not. For ex, the Camera app
+ // would mirror the preview for the front camera
+ @GenerateFieldPort(name = "mirrorBg", hasDefault = true)
+ private boolean mMirrorBg = false;
+
+ // The orientation of the display. This will change the flipping
+ // coordinates, if we were to mirror the background
+ @GenerateFieldPort(name = "orientation", hasDefault = true)
+ private int mOrientation = 0;
+
+ /** Default algorithm parameter values, for non-shader use */
+
+ // Frame count for learning bg model
+ private static final int DEFAULT_LEARNING_DURATION = 40;
+ // Frame count for learning verification
+ private static final int DEFAULT_LEARNING_VERIFY_DURATION = 10;
+ // Maximum distance (in standard deviations) for considering a pixel as background
+ private static final float DEFAULT_ACCEPT_STDDEV = 0.85f;
+ // Variance threshold scale factor for large scale of hierarchy
+ private static final float DEFAULT_HIER_LRG_SCALE = 0.7f;
+ // Variance threshold scale factor for medium scale of hierarchy
+ private static final float DEFAULT_HIER_MID_SCALE = 0.6f;
+ // Variance threshold scale factor for small scale of hierarchy
+ private static final float DEFAULT_HIER_SML_SCALE = 0.5f;
+ // Width of foreground / background mask.
+ private static final int DEFAULT_MASK_WIDTH_EXPONENT = 8;
+ // Height of foreground / background mask.
+ private static final int DEFAULT_MASK_HEIGHT_EXPONENT = 8;
+ // Area over which to average for large scale (length in pixels = 2^HIERARCHY_*_EXPONENT)
+ private static final int DEFAULT_HIER_LRG_EXPONENT = 3;
+ // Area over which to average for medium scale
+ private static final int DEFAULT_HIER_MID_EXPONENT = 2;
+ // Area over which to average for small scale
+ private static final int DEFAULT_HIER_SML_EXPONENT = 0;
+ // Scale factor for luminance channel in distance calculations (larger = more significant)
+ private static final float DEFAULT_Y_SCALE_FACTOR = 0.40f;
+ // Scale factor for chroma channels in distance calculations
+ private static final float DEFAULT_UV_SCALE_FACTOR = 1.35f;
+ // Mask value to start blending away from background
+ private static final float DEFAULT_MASK_BLEND_BG = 0.65f;
+ // Mask value to start blending away from foreground
+ private static final float DEFAULT_MASK_BLEND_FG = 0.95f;
+ // Exposure stop number to change the brightness of foreground
+ private static final float DEFAULT_EXPOSURE_CHANGE = 1.0f;
+ // White balance change in Red channel for foreground
+ private static final float DEFAULT_WHITE_BALANCE_RED_CHANGE = 0.0f;
+ // White balance change in Blue channel for foreground
+ private static final float DEFAULT_WHITE_BALANCE_BLUE_CHANGE = 0.0f;
+ // Variable to control automatic white balance effect
+ // 0.f -> Auto WB is off; 1.f-> Auto WB is on
+ private static final int DEFAULT_WHITE_BALANCE_TOGGLE = 0;
+
+ // Default rate at which to learn bg model during learning period
+ private static final float DEFAULT_LEARNING_ADAPT_RATE = 0.2f;
+ // Default rate at which to learn bg model from new background pixels
+ private static final float DEFAULT_ADAPT_RATE_BG = 0.0f;
+ // Default rate at which to learn bg model from new foreground pixels
+ private static final float DEFAULT_ADAPT_RATE_FG = 0.0f;
+ // Default rate at which to verify whether background is stable
+ private static final float DEFAULT_MASK_VERIFY_RATE = 0.25f;
+ // Default rate at which to verify whether background is stable
+ private static final int DEFAULT_LEARNING_DONE_THRESHOLD = 20;
+
+ // Default 3x3 matrix, column major, for fitting background 1:1
+ private static final float[] DEFAULT_BG_FIT_TRANSFORM = new float[] {
+ 1.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f
+ };
+
+ /** Default algorithm parameter values, for shader use */
+
+ // Area over which to blur binary mask values (length in pixels = 2^MASK_SMOOTH_EXPONENT)
+ private static final String MASK_SMOOTH_EXPONENT = "2.0";
+ // Scale value for mapping variance distance to fit nicely to 0-1, 8-bit
+ private static final String DISTANCE_STORAGE_SCALE = "0.6";
+ // Scale value for mapping variance to fit nicely to 0-1, 8-bit
+ private static final String VARIANCE_STORAGE_SCALE = "5.0";
+ // Default scale of auto white balance parameters
+ private static final String DEFAULT_AUTO_WB_SCALE = "0.25";
+ // Minimum variance (0-255 scale)
+ private static final String MIN_VARIANCE = "3.0";
+ // Column-major array for 4x4 matrix converting RGB to YCbCr, JPEG definition (no pedestal)
+ private static final String RGB_TO_YUV_MATRIX = "0.299, -0.168736, 0.5, 0.000, " +
+ "0.587, -0.331264, -0.418688, 0.000, " +
+ "0.114, 0.5, -0.081312, 0.000, " +
+ "0.000, 0.5, 0.5, 1.000 ";
+ /** Stream names */
+
+ private static final String[] mInputNames = {"video",
+ "background"};
+
+ private static final String[] mOutputNames = {"video"};
+
+ private static final String[] mDebugOutputNames = {"debug1",
+ "debug2"};
+
+ /** Other private variables */
+
+ private FrameFormat mOutputFormat;
+ private MutableFrameFormat mMemoryFormat;
+ private MutableFrameFormat mMaskFormat;
+ private MutableFrameFormat mAverageFormat;
+
+ private final boolean mLogVerbose;
+ private static final String TAG = "BackDropperFilter";
+
+ /** Shader source code */
+
+ // Shared uniforms and utility functions
+ private static String mSharedUtilShader =
+ "precision mediump float;\n" +
+ "uniform float fg_adapt_rate;\n" +
+ "uniform float bg_adapt_rate;\n" +
+ "const mat4 coeff_yuv = mat4(" + RGB_TO_YUV_MATRIX + ");\n" +
+ "const float dist_scale = " + DISTANCE_STORAGE_SCALE + ";\n" +
+ "const float inv_dist_scale = 1. / dist_scale;\n" +
+ "const float var_scale=" + VARIANCE_STORAGE_SCALE + ";\n" +
+ "const float inv_var_scale = 1. / var_scale;\n" +
+ "const float min_variance = inv_var_scale *" + MIN_VARIANCE + "/ 256.;\n" +
+ "const float auto_wb_scale = " + DEFAULT_AUTO_WB_SCALE + ";\n" +
+ "\n" +
+ // Variance distance in luminance between current pixel and background model
+ "float gauss_dist_y(float y, float mean, float variance) {\n" +
+ " float dist = (y - mean) * (y - mean) / variance;\n" +
+ " return dist;\n" +
+ "}\n" +
+ // Sum of variance distances in chroma between current pixel and background
+ // model
+ "float gauss_dist_uv(vec2 uv, vec2 mean, vec2 variance) {\n" +
+ " vec2 dist = (uv - mean) * (uv - mean) / variance;\n" +
+ " return dist.r + dist.g;\n" +
+ "}\n" +
+ // Select learning rate for pixel based on smoothed decision mask alpha
+ "float local_adapt_rate(float alpha) {\n" +
+ " return mix(bg_adapt_rate, fg_adapt_rate, alpha);\n" +
+ "}\n" +
+ "\n";
+
+ // Distance calculation shader. Calculates a distance metric between the foreground and the
+ // current background model, in both luminance and in chroma (yuv space). Distance is
+ // measured in variances from the mean background value. For chroma, the distance is the sum
+ // of the two individual color channel distances. The distances are output on the b and alpha
+ // channels, r and g are for debug information.
+ // Inputs:
+ // tex_sampler_0: Mip-map for foreground (live) video frame.
+ // tex_sampler_1: Background mean mask.
+ // tex_sampler_2: Background variance mask.
+ // subsample_level: Level on foreground frame's mip-map.
+ private static final String mBgDistanceShader =
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform sampler2D tex_sampler_1;\n" +
+ "uniform sampler2D tex_sampler_2;\n" +
+ "uniform float subsample_level;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 fg_rgb = texture2D(tex_sampler_0, v_texcoord, subsample_level);\n" +
+ " vec4 fg = coeff_yuv * vec4(fg_rgb.rgb, 1.);\n" +
+ " vec4 mean = texture2D(tex_sampler_1, v_texcoord);\n" +
+ " vec4 variance = inv_var_scale * texture2D(tex_sampler_2, v_texcoord);\n" +
+ "\n" +
+ " float dist_y = gauss_dist_y(fg.r, mean.r, variance.r);\n" +
+ " float dist_uv = gauss_dist_uv(fg.gb, mean.gb, variance.gb);\n" +
+ " gl_FragColor = vec4(0.5*fg.rg, dist_scale*dist_y, dist_scale*dist_uv);\n" +
+ "}\n";
+
+ // Foreground/background mask decision shader. Decides whether a frame is in the foreground or
+ // the background using a hierarchical threshold on the distance. Binary foreground/background
+ // mask is placed in the alpha channel. The RGB channels contain debug information.
+ private static final String mBgMaskShader =
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform float accept_variance;\n" +
+ "uniform vec2 yuv_weights;\n" +
+ "uniform float scale_lrg;\n" +
+ "uniform float scale_mid;\n" +
+ "uniform float scale_sml;\n" +
+ "uniform float exp_lrg;\n" +
+ "uniform float exp_mid;\n" +
+ "uniform float exp_sml;\n" +
+ "varying vec2 v_texcoord;\n" +
+ // Decide whether pixel is foreground or background based on Y and UV
+ // distance and maximum acceptable variance.
+ // yuv_weights.x is smaller than yuv_weights.y to discount the influence of shadow
+ "bool is_fg(vec2 dist_yc, float accept_variance) {\n" +
+ " return ( dot(yuv_weights, dist_yc) >= accept_variance );\n" +
+ "}\n" +
+ "void main() {\n" +
+ " vec4 dist_lrg_sc = texture2D(tex_sampler_0, v_texcoord, exp_lrg);\n" +
+ " vec4 dist_mid_sc = texture2D(tex_sampler_0, v_texcoord, exp_mid);\n" +
+ " vec4 dist_sml_sc = texture2D(tex_sampler_0, v_texcoord, exp_sml);\n" +
+ " vec2 dist_lrg = inv_dist_scale * dist_lrg_sc.ba;\n" +
+ " vec2 dist_mid = inv_dist_scale * dist_mid_sc.ba;\n" +
+ " vec2 dist_sml = inv_dist_scale * dist_sml_sc.ba;\n" +
+ " vec2 norm_dist = 0.75 * dist_sml / accept_variance;\n" + // For debug viz
+ " bool is_fg_lrg = is_fg(dist_lrg, accept_variance * scale_lrg);\n" +
+ " bool is_fg_mid = is_fg_lrg || is_fg(dist_mid, accept_variance * scale_mid);\n" +
+ " float is_fg_sml =\n" +
+ " float(is_fg_mid || is_fg(dist_sml, accept_variance * scale_sml));\n" +
+ " float alpha = 0.5 * is_fg_sml + 0.3 * float(is_fg_mid) + 0.2 * float(is_fg_lrg);\n" +
+ " gl_FragColor = vec4(alpha, norm_dist, is_fg_sml);\n" +
+ "}\n";
+
+ // Automatic White Balance parameter decision shader
+ // Use the Gray World assumption that in a white balance corrected image, the average of R, G, B
+ // channel will be a common gray value.
+ // To match the white balance of foreground and background, the average of R, G, B channel of
+ // two videos should match.
+ // Inputs:
+ // tex_sampler_0: Mip-map for foreground (live) video frame.
+ // tex_sampler_1: Mip-map for background (playback) video frame.
+ // pyramid_depth: Depth of input frames' mip-maps.
+ private static final String mAutomaticWhiteBalance =
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform sampler2D tex_sampler_1;\n" +
+ "uniform float pyramid_depth;\n" +
+ "uniform bool autowb_toggle;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 mean_video = texture2D(tex_sampler_0, v_texcoord, pyramid_depth);\n"+
+ " vec4 mean_bg = texture2D(tex_sampler_1, v_texcoord, pyramid_depth);\n" +
+ // If Auto WB is toggled off, the return texture will be a unicolor texture of value 1
+ // If Auto WB is toggled on, the return texture will be a unicolor texture with
+ // adjustment parameters for R and B channels stored in the corresponding channel
+ " float green_normalizer = mean_video.g / mean_bg.g;\n"+
+ " vec4 adjusted_value = vec4(mean_bg.r / mean_video.r * green_normalizer, 1., \n" +
+ " mean_bg.b / mean_video.b * green_normalizer, 1.) * auto_wb_scale; \n" +
+ " gl_FragColor = autowb_toggle ? adjusted_value : vec4(auto_wb_scale);\n" +
+ "}\n";
+
+
+ // Background subtraction shader. Uses a mipmap of the binary mask map to blend smoothly between
+ // foreground and background
+ // Inputs:
+ // tex_sampler_0: Foreground (live) video frame.
+ // tex_sampler_1: Background (playback) video frame.
+ // tex_sampler_2: Foreground/background mask.
+ // tex_sampler_3: Auto white-balance factors.
+ private static final String mBgSubtractShader =
+ "uniform mat3 bg_fit_transform;\n" +
+ "uniform float mask_blend_bg;\n" +
+ "uniform float mask_blend_fg;\n" +
+ "uniform float exposure_change;\n" +
+ "uniform float whitebalancered_change;\n" +
+ "uniform float whitebalanceblue_change;\n" +
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform sampler2D tex_sampler_1;\n" +
+ "uniform sampler2D tex_sampler_2;\n" +
+ "uniform sampler2D tex_sampler_3;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec2 bg_texcoord = (bg_fit_transform * vec3(v_texcoord, 1.)).xy;\n" +
+ " vec4 bg_rgb = texture2D(tex_sampler_1, bg_texcoord);\n" +
+ // The foreground texture is modified by multiplying both manual and auto white balance changes in R and B
+ // channel and multiplying exposure change in all R, G, B channels.
+ " vec4 wb_auto_scale = texture2D(tex_sampler_3, v_texcoord) * exposure_change / auto_wb_scale;\n" +
+ " vec4 wb_manual_scale = vec4(1. + whitebalancered_change, 1., 1. + whitebalanceblue_change, 1.);\n" +
+ " vec4 fg_rgb = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " vec4 fg_adjusted = fg_rgb * wb_manual_scale * wb_auto_scale;\n"+
+ " vec4 mask = texture2D(tex_sampler_2, v_texcoord, \n" +
+ " " + MASK_SMOOTH_EXPONENT + ");\n" +
+ " float alpha = smoothstep(mask_blend_bg, mask_blend_fg, mask.a);\n" +
+ " gl_FragColor = mix(bg_rgb, fg_adjusted, alpha);\n";
+
+ // May the Force... Makes the foreground object translucent blue, with a bright
+ // blue-white outline
+ private static final String mBgSubtractForceShader =
+ " vec4 ghost_rgb = (fg_adjusted * 0.7 + vec4(0.3,0.3,0.4,0.))*0.65 + \n" +
+ " 0.35*bg_rgb;\n" +
+ " float glow_start = 0.75 * mask_blend_bg; \n"+
+ " float glow_max = mask_blend_bg; \n"+
+ " gl_FragColor = mask.a < glow_start ? bg_rgb : \n" +
+ " mask.a < glow_max ? mix(bg_rgb, vec4(0.9,0.9,1.0,1.0), \n" +
+ " (mask.a - glow_start) / (glow_max - glow_start) ) : \n" +
+ " mask.a < mask_blend_fg ? mix(vec4(0.9,0.9,1.0,1.0), ghost_rgb, \n" +
+ " (mask.a - glow_max) / (mask_blend_fg - glow_max) ) : \n" +
+ " ghost_rgb;\n" +
+ "}\n";
+
+ // Background model mean update shader. Skews the current model mean toward the most recent pixel
+ // value for a pixel, weighted by the learning rate and by whether the pixel is classified as
+ // foreground or background.
+ // Inputs:
+ // tex_sampler_0: Mip-map for foreground (live) video frame.
+ // tex_sampler_1: Background mean mask.
+ // tex_sampler_2: Foreground/background mask.
+ // subsample_level: Level on foreground frame's mip-map.
+ private static final String mUpdateBgModelMeanShader =
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform sampler2D tex_sampler_1;\n" +
+ "uniform sampler2D tex_sampler_2;\n" +
+ "uniform float subsample_level;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 fg_rgb = texture2D(tex_sampler_0, v_texcoord, subsample_level);\n" +
+ " vec4 fg = coeff_yuv * vec4(fg_rgb.rgb, 1.);\n" +
+ " vec4 mean = texture2D(tex_sampler_1, v_texcoord);\n" +
+ " vec4 mask = texture2D(tex_sampler_2, v_texcoord, \n" +
+ " " + MASK_SMOOTH_EXPONENT + ");\n" +
+ "\n" +
+ " float alpha = local_adapt_rate(mask.a);\n" +
+ " vec4 new_mean = mix(mean, fg, alpha);\n" +
+ " gl_FragColor = new_mean;\n" +
+ "}\n";
+
+ // Background model variance update shader. Skews the current model variance toward the most
+ // recent variance for the pixel, weighted by the learning rate and by whether the pixel is
+ // classified as foreground or background.
+ // Inputs:
+ // tex_sampler_0: Mip-map for foreground (live) video frame.
+ // tex_sampler_1: Background mean mask.
+ // tex_sampler_2: Background variance mask.
+ // tex_sampler_3: Foreground/background mask.
+ // subsample_level: Level on foreground frame's mip-map.
+ // TODO: to improve efficiency, use single mark for mean + variance, then merge this into
+ // mUpdateBgModelMeanShader.
+ private static final String mUpdateBgModelVarianceShader =
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform sampler2D tex_sampler_1;\n" +
+ "uniform sampler2D tex_sampler_2;\n" +
+ "uniform sampler2D tex_sampler_3;\n" +
+ "uniform float subsample_level;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 fg_rgb = texture2D(tex_sampler_0, v_texcoord, subsample_level);\n" +
+ " vec4 fg = coeff_yuv * vec4(fg_rgb.rgb, 1.);\n" +
+ " vec4 mean = texture2D(tex_sampler_1, v_texcoord);\n" +
+ " vec4 variance = inv_var_scale * texture2D(tex_sampler_2, v_texcoord);\n" +
+ " vec4 mask = texture2D(tex_sampler_3, v_texcoord, \n" +
+ " " + MASK_SMOOTH_EXPONENT + ");\n" +
+ "\n" +
+ " float alpha = local_adapt_rate(mask.a);\n" +
+ " vec4 cur_variance = (fg-mean)*(fg-mean);\n" +
+ " vec4 new_variance = mix(variance, cur_variance, alpha);\n" +
+ " new_variance = max(new_variance, vec4(min_variance));\n" +
+ " gl_FragColor = var_scale * new_variance;\n" +
+ "}\n";
+
+ // Background verification shader. Skews the current background verification mask towards the
+ // most recent frame, weighted by the learning rate.
+ private static final String mMaskVerifyShader =
+ "uniform sampler2D tex_sampler_0;\n" +
+ "uniform sampler2D tex_sampler_1;\n" +
+ "uniform float verify_rate;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " vec4 lastmask = texture2D(tex_sampler_0, v_texcoord);\n" +
+ " vec4 mask = texture2D(tex_sampler_1, v_texcoord);\n" +
+ " float newmask = mix(lastmask.a, mask.a, verify_rate);\n" +
+ " gl_FragColor = vec4(0., 0., 0., newmask);\n" +
+ "}\n";
+
+ /** Shader program objects */
+
+ private ShaderProgram mBgDistProgram;
+ private ShaderProgram mBgMaskProgram;
+ private ShaderProgram mBgSubtractProgram;
+ private ShaderProgram mBgUpdateMeanProgram;
+ private ShaderProgram mBgUpdateVarianceProgram;
+ private ShaderProgram mCopyOutProgram;
+ private ShaderProgram mAutomaticWhiteBalanceProgram;
+ private ShaderProgram mMaskVerifyProgram;
+ private ShaderProgram copyShaderProgram;
+
+ /** Background model storage */
+
+ private boolean mPingPong;
+ private GLFrame mBgMean[];
+ private GLFrame mBgVariance[];
+ private GLFrame mMaskVerify[];
+ private GLFrame mDistance;
+ private GLFrame mAutoWB;
+ private GLFrame mMask;
+ private GLFrame mVideoInput;
+ private GLFrame mBgInput;
+ private GLFrame mMaskAverage;
+
+ /** Overall filter state */
+
+ private boolean isOpen;
+ private int mFrameCount;
+ private boolean mStartLearning;
+ private boolean mBackgroundFitModeChanged;
+ private float mRelativeAspect;
+ private int mPyramidDepth;
+ private int mSubsampleLevel;
+
+ /** Learning listener object */
+
+ public interface LearningDoneListener {
+ public void onLearningDone(BackDropperFilter filter);
+ }
+
+ /** Public Filter methods */
+
+ public BackDropperFilter(String name) {
+ super(name);
+
+ mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+ }
+
+ @Override
+ public void setupPorts() {
+ // Inputs.
+ // TODO: Target should be GPU, but relaxed for now.
+ FrameFormat imageFormat = ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_UNSPECIFIED);
+ for (String inputName : mInputNames) {
+ addMaskedInputPort(inputName, imageFormat);
+ }
+ // Normal outputs
+ for (String outputName : mOutputNames) {
+ addOutputBasedOnInput(outputName, "video");
+ }
+
+ // Debug outputs
+ if (mProvideDebugOutputs) {
+ for (String outputName : mDebugOutputNames) {
+ addOutputBasedOnInput(outputName, "video");
+ }
+ }
+ }
+
+ @Override
+ public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
+ // Create memory format based on video input.
+ MutableFrameFormat format = inputFormat.mutableCopy();
+ // Is this a debug output port? If so, leave dimensions unspecified.
+ if (!Arrays.asList(mOutputNames).contains(portName)) {
+ format.setDimensions(FrameFormat.SIZE_UNSPECIFIED, FrameFormat.SIZE_UNSPECIFIED);
+ }
+ return format;
+ }
+
+ private boolean createMemoryFormat(FrameFormat inputFormat) {
+ // We can't resize because that would require re-learning.
+ if (mMemoryFormat != null) {
+ return false;
+ }
+
+ if (inputFormat.getWidth() == FrameFormat.SIZE_UNSPECIFIED ||
+ inputFormat.getHeight() == FrameFormat.SIZE_UNSPECIFIED) {
+ throw new RuntimeException("Attempting to process input frame with unknown size");
+ }
+
+ mMaskFormat = inputFormat.mutableCopy();
+ int maskWidth = (int)Math.pow(2, mMaskWidthExp);
+ int maskHeight = (int)Math.pow(2, mMaskHeightExp);
+ mMaskFormat.setDimensions(maskWidth, maskHeight);
+
+ mPyramidDepth = Math.max(mMaskWidthExp, mMaskHeightExp);
+ mMemoryFormat = mMaskFormat.mutableCopy();
+ int widthExp = Math.max(mMaskWidthExp, pyramidLevel(inputFormat.getWidth()));
+ int heightExp = Math.max(mMaskHeightExp, pyramidLevel(inputFormat.getHeight()));
+ mPyramidDepth = Math.max(widthExp, heightExp);
+ int memWidth = Math.max(maskWidth, (int)Math.pow(2, widthExp));
+ int memHeight = Math.max(maskHeight, (int)Math.pow(2, heightExp));
+ mMemoryFormat.setDimensions(memWidth, memHeight);
+ mSubsampleLevel = mPyramidDepth - Math.max(mMaskWidthExp, mMaskHeightExp);
+
+ if (mLogVerbose) {
+ Log.v(TAG, "Mask frames size " + maskWidth + " x " + maskHeight);
+ Log.v(TAG, "Pyramid levels " + widthExp + " x " + heightExp);
+ Log.v(TAG, "Memory frames size " + memWidth + " x " + memHeight);
+ }
+
+ mAverageFormat = inputFormat.mutableCopy();
+ mAverageFormat.setDimensions(1,1);
+ return true;
+ }
+
+ public void prepare(FilterContext context){
+ if (mLogVerbose) Log.v(TAG, "Preparing BackDropperFilter!");
+
+ mBgMean = new GLFrame[2];
+ mBgVariance = new GLFrame[2];
+ mMaskVerify = new GLFrame[2];
+ copyShaderProgram = ShaderProgram.createIdentity(context);
+ }
+
+ private void allocateFrames(FrameFormat inputFormat, FilterContext context) {
+ if (!createMemoryFormat(inputFormat)) {
+ return; // All set.
+ }
+ if (mLogVerbose) Log.v(TAG, "Allocating BackDropperFilter frames");
+
+ // Create initial background model values
+ int numBytes = mMaskFormat.getSize();
+ byte[] initialBgMean = new byte[numBytes];
+ byte[] initialBgVariance = new byte[numBytes];
+ byte[] initialMaskVerify = new byte[numBytes];
+ for (int i = 0; i < numBytes; i++) {
+ initialBgMean[i] = (byte)128;
+ initialBgVariance[i] = (byte)10;
+ initialMaskVerify[i] = (byte)0;
+ }
+
+ // Get frames to store background model in
+ for (int i = 0; i < 2; i++) {
+ mBgMean[i] = (GLFrame)context.getFrameManager().newFrame(mMaskFormat);
+ mBgMean[i].setData(initialBgMean, 0, numBytes);
+
+ mBgVariance[i] = (GLFrame)context.getFrameManager().newFrame(mMaskFormat);
+ mBgVariance[i].setData(initialBgVariance, 0, numBytes);
+
+ mMaskVerify[i] = (GLFrame)context.getFrameManager().newFrame(mMaskFormat);
+ mMaskVerify[i].setData(initialMaskVerify, 0, numBytes);
+ }
+
+ // Get frames to store other textures in
+ if (mLogVerbose) Log.v(TAG, "Done allocating texture for Mean and Variance objects!");
+
+ mDistance = (GLFrame)context.getFrameManager().newFrame(mMaskFormat);
+ mMask = (GLFrame)context.getFrameManager().newFrame(mMaskFormat);
+ mAutoWB = (GLFrame)context.getFrameManager().newFrame(mAverageFormat);
+ mVideoInput = (GLFrame)context.getFrameManager().newFrame(mMemoryFormat);
+ mBgInput = (GLFrame)context.getFrameManager().newFrame(mMemoryFormat);
+ mMaskAverage = (GLFrame)context.getFrameManager().newFrame(mAverageFormat);
+
+ // Create shader programs
+ mBgDistProgram = new ShaderProgram(context, mSharedUtilShader + mBgDistanceShader);
+ mBgDistProgram.setHostValue("subsample_level", (float)mSubsampleLevel);
+
+ mBgMaskProgram = new ShaderProgram(context, mSharedUtilShader + mBgMaskShader);
+ mBgMaskProgram.setHostValue("accept_variance", mAcceptStddev * mAcceptStddev);
+ float[] yuvWeights = { mLumScale, mChromaScale };
+ mBgMaskProgram.setHostValue("yuv_weights", yuvWeights );
+ mBgMaskProgram.setHostValue("scale_lrg", mHierarchyLrgScale);
+ mBgMaskProgram.setHostValue("scale_mid", mHierarchyMidScale);
+ mBgMaskProgram.setHostValue("scale_sml", mHierarchySmlScale);
+ mBgMaskProgram.setHostValue("exp_lrg", (float)(mSubsampleLevel + mHierarchyLrgExp));
+ mBgMaskProgram.setHostValue("exp_mid", (float)(mSubsampleLevel + mHierarchyMidExp));
+ mBgMaskProgram.setHostValue("exp_sml", (float)(mSubsampleLevel + mHierarchySmlExp));
+
+ if (mUseTheForce) {
+ mBgSubtractProgram = new ShaderProgram(context, mSharedUtilShader + mBgSubtractShader + mBgSubtractForceShader);
+ } else {
+ mBgSubtractProgram = new ShaderProgram(context, mSharedUtilShader + mBgSubtractShader + "}\n");
+ }
+ mBgSubtractProgram.setHostValue("bg_fit_transform", DEFAULT_BG_FIT_TRANSFORM);
+ mBgSubtractProgram.setHostValue("mask_blend_bg", mMaskBg);
+ mBgSubtractProgram.setHostValue("mask_blend_fg", mMaskFg);
+ mBgSubtractProgram.setHostValue("exposure_change", mExposureChange);
+ mBgSubtractProgram.setHostValue("whitebalanceblue_change", mWhiteBalanceBlueChange);
+ mBgSubtractProgram.setHostValue("whitebalancered_change", mWhiteBalanceRedChange);
+
+
+ mBgUpdateMeanProgram = new ShaderProgram(context, mSharedUtilShader + mUpdateBgModelMeanShader);
+ mBgUpdateMeanProgram.setHostValue("subsample_level", (float)mSubsampleLevel);
+
+ mBgUpdateVarianceProgram = new ShaderProgram(context, mSharedUtilShader + mUpdateBgModelVarianceShader);
+ mBgUpdateVarianceProgram.setHostValue("subsample_level", (float)mSubsampleLevel);
+
+ mCopyOutProgram = ShaderProgram.createIdentity(context);
+
+ mAutomaticWhiteBalanceProgram = new ShaderProgram(context, mSharedUtilShader + mAutomaticWhiteBalance);
+ mAutomaticWhiteBalanceProgram.setHostValue("pyramid_depth", (float)mPyramidDepth);
+ mAutomaticWhiteBalanceProgram.setHostValue("autowb_toggle", mAutoWBToggle);
+
+ mMaskVerifyProgram = new ShaderProgram(context, mSharedUtilShader + mMaskVerifyShader);
+ mMaskVerifyProgram.setHostValue("verify_rate", mVerifyRate);
+
+ if (mLogVerbose) Log.v(TAG, "Shader width set to " + mMemoryFormat.getWidth());
+
+ mRelativeAspect = 1.f;
+
+ mFrameCount = 0;
+ mStartLearning = true;
+ }
+
+ public void process(FilterContext context) {
+ // Grab inputs and ready intermediate frames and outputs.
+ Frame video = pullInput("video");
+ Frame background = pullInput("background");
+ allocateFrames(video.getFormat(), context);
+
+ // Update learning rate after initial learning period
+ if (mStartLearning) {
+ if (mLogVerbose) Log.v(TAG, "Starting learning");
+ mBgUpdateMeanProgram.setHostValue("bg_adapt_rate", mAdaptRateLearning);
+ mBgUpdateMeanProgram.setHostValue("fg_adapt_rate", mAdaptRateLearning);
+ mBgUpdateVarianceProgram.setHostValue("bg_adapt_rate", mAdaptRateLearning);
+ mBgUpdateVarianceProgram.setHostValue("fg_adapt_rate", mAdaptRateLearning);
+ mFrameCount = 0;
+ mStartLearning = false;
+ }
+
+ // Select correct pingpong buffers
+ int inputIndex = mPingPong ? 0 : 1;
+ int outputIndex = mPingPong ? 1 : 0;
+ mPingPong = !mPingPong;
+
+ // Check relative aspect ratios
+ updateBgScaling(video, background, mBackgroundFitModeChanged);
+ mBackgroundFitModeChanged = false;
+
+ // Make copies for input frames to GLFrames
+
+ copyShaderProgram.process(video, mVideoInput);
+ copyShaderProgram.process(background, mBgInput);
+
+ mVideoInput.generateMipMap();
+ mVideoInput.setTextureParameter(GLES20.GL_TEXTURE_MIN_FILTER,
+ GLES20.GL_LINEAR_MIPMAP_NEAREST);
+
+ mBgInput.generateMipMap();
+ mBgInput.setTextureParameter(GLES20.GL_TEXTURE_MIN_FILTER,
+ GLES20.GL_LINEAR_MIPMAP_NEAREST);
+
+ // Process shaders
+ Frame[] distInputs = { mVideoInput, mBgMean[inputIndex], mBgVariance[inputIndex] };
+ mBgDistProgram.process(distInputs, mDistance);
+ mDistance.generateMipMap();
+ mDistance.setTextureParameter(GLES20.GL_TEXTURE_MIN_FILTER,
+ GLES20.GL_LINEAR_MIPMAP_NEAREST);
+
+ mBgMaskProgram.process(mDistance, mMask);
+ mMask.generateMipMap();
+ mMask.setTextureParameter(GLES20.GL_TEXTURE_MIN_FILTER,
+ GLES20.GL_LINEAR_MIPMAP_NEAREST);
+
+ Frame[] autoWBInputs = { mVideoInput, mBgInput };
+ mAutomaticWhiteBalanceProgram.process(autoWBInputs, mAutoWB);
+
+ if (mFrameCount <= mLearningDuration) {
+ // During learning
+ pushOutput("video", video);
+
+ if (mFrameCount == mLearningDuration - mLearningVerifyDuration) {
+ copyShaderProgram.process(mMask, mMaskVerify[outputIndex]);
+
+ mBgUpdateMeanProgram.setHostValue("bg_adapt_rate", mAdaptRateBg);
+ mBgUpdateMeanProgram.setHostValue("fg_adapt_rate", mAdaptRateFg);
+ mBgUpdateVarianceProgram.setHostValue("bg_adapt_rate", mAdaptRateBg);
+ mBgUpdateVarianceProgram.setHostValue("fg_adapt_rate", mAdaptRateFg);
+
+
+ } else if (mFrameCount > mLearningDuration - mLearningVerifyDuration) {
+ // In the learning verification stage, compute background masks and a weighted average
+ // with weights grow exponentially with time
+ Frame[] maskVerifyInputs = {mMaskVerify[inputIndex], mMask};
+ mMaskVerifyProgram.process(maskVerifyInputs, mMaskVerify[outputIndex]);
+ mMaskVerify[outputIndex].generateMipMap();
+ mMaskVerify[outputIndex].setTextureParameter(GLES20.GL_TEXTURE_MIN_FILTER,
+ GLES20.GL_LINEAR_MIPMAP_NEAREST);
+ }
+
+ if (mFrameCount == mLearningDuration) {
+ // In the last verification frame, verify if the verification mask is almost blank
+ // If not, restart learning
+ copyShaderProgram.process(mMaskVerify[outputIndex], mMaskAverage);
+ ByteBuffer mMaskAverageByteBuffer = mMaskAverage.getData();
+ byte[] mask_average = mMaskAverageByteBuffer.array();
+ int bi = (int)(mask_average[3] & 0xFF);
+ if (mLogVerbose) Log.v(TAG, String.format("Mask_average is %d", bi));
+
+ if (bi >= DEFAULT_LEARNING_DONE_THRESHOLD) {
+ mStartLearning = true; // Restart learning
+ } else {
+ if (mLogVerbose) Log.v(TAG, "Learning done");
+ if (mLearningDoneListener != null) {
+ mLearningDoneListener.onLearningDone(this);
+ }
+ }
+ }
+ } else {
+ Frame output = context.getFrameManager().newFrame(video.getFormat());
+ Frame[] subtractInputs = { video, background, mMask, mAutoWB };
+ mBgSubtractProgram.process(subtractInputs, output);
+ pushOutput("video", output);
+ output.release();
+ }
+
+ // Compute mean and variance of the background
+ if (mFrameCount < mLearningDuration - mLearningVerifyDuration ||
+ mAdaptRateBg > 0.0 || mAdaptRateFg > 0.0) {
+ Frame[] meanUpdateInputs = { mVideoInput, mBgMean[inputIndex], mMask };
+ mBgUpdateMeanProgram.process(meanUpdateInputs, mBgMean[outputIndex]);
+ mBgMean[outputIndex].generateMipMap();
+ mBgMean[outputIndex].setTextureParameter(GLES20.GL_TEXTURE_MIN_FILTER,
+ GLES20.GL_LINEAR_MIPMAP_NEAREST);
+
+ Frame[] varianceUpdateInputs = {
+ mVideoInput, mBgMean[inputIndex], mBgVariance[inputIndex], mMask
+ };
+ mBgUpdateVarianceProgram.process(varianceUpdateInputs, mBgVariance[outputIndex]);
+ mBgVariance[outputIndex].generateMipMap();
+ mBgVariance[outputIndex].setTextureParameter(GLES20.GL_TEXTURE_MIN_FILTER,
+ GLES20.GL_LINEAR_MIPMAP_NEAREST);
+ }
+
+ // Provide debug output to two smaller viewers
+ if (mProvideDebugOutputs) {
+ Frame dbg1 = context.getFrameManager().newFrame(video.getFormat());
+ mCopyOutProgram.process(video, dbg1);
+ pushOutput("debug1", dbg1);
+ dbg1.release();
+
+ Frame dbg2 = context.getFrameManager().newFrame(mMemoryFormat);
+ mCopyOutProgram.process(mMask, dbg2);
+ pushOutput("debug2", dbg2);
+ dbg2.release();
+ }
+
+ mFrameCount++;
+
+ if (mLogVerbose) {
+ if (mFrameCount % 30 == 0) {
+ if (startTime == -1) {
+ context.getGLEnvironment().activate();
+ GLES20.glFinish();
+ startTime = SystemClock.elapsedRealtime();
+ } else {
+ context.getGLEnvironment().activate();
+ GLES20.glFinish();
+ long endTime = SystemClock.elapsedRealtime();
+ Log.v(TAG, "Avg. frame duration: " + String.format("%.2f",(endTime-startTime)/30.) +
+ " ms. Avg. fps: " + String.format("%.2f", 1000./((endTime-startTime)/30.)) );
+ startTime = endTime;
+ }
+ }
+ }
+ }
+
+ private long startTime = -1;
+
+ public void close(FilterContext context) {
+ if (mMemoryFormat == null) {
+ return;
+ }
+
+ if (mLogVerbose) Log.v(TAG, "Filter Closing!");
+ for (int i = 0; i < 2; i++) {
+ mBgMean[i].release();
+ mBgVariance[i].release();
+ mMaskVerify[i].release();
+ }
+ mDistance.release();
+ mMask.release();
+ mAutoWB.release();
+ mVideoInput.release();
+ mBgInput.release();
+ mMaskAverage.release();
+
+ mMemoryFormat = null;
+ }
+
+ // Relearn background model
+ synchronized public void relearn() {
+ // Let the processing thread know about learning restart
+ mStartLearning = true;
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ // TODO: Many of these can be made ProgramPorts!
+ if (name.equals("backgroundFitMode")) {
+ mBackgroundFitModeChanged = true;
+ } else if (name.equals("acceptStddev")) {
+ mBgMaskProgram.setHostValue("accept_variance", mAcceptStddev * mAcceptStddev);
+ } else if (name.equals("hierLrgScale")) {
+ mBgMaskProgram.setHostValue("scale_lrg", mHierarchyLrgScale);
+ } else if (name.equals("hierMidScale")) {
+ mBgMaskProgram.setHostValue("scale_mid", mHierarchyMidScale);
+ } else if (name.equals("hierSmlScale")) {
+ mBgMaskProgram.setHostValue("scale_sml", mHierarchySmlScale);
+ } else if (name.equals("hierLrgExp")) {
+ mBgMaskProgram.setHostValue("exp_lrg", (float)(mSubsampleLevel + mHierarchyLrgExp));
+ } else if (name.equals("hierMidExp")) {
+ mBgMaskProgram.setHostValue("exp_mid", (float)(mSubsampleLevel + mHierarchyMidExp));
+ } else if (name.equals("hierSmlExp")) {
+ mBgMaskProgram.setHostValue("exp_sml", (float)(mSubsampleLevel + mHierarchySmlExp));
+ } else if (name.equals("lumScale") || name.equals("chromaScale")) {
+ float[] yuvWeights = { mLumScale, mChromaScale };
+ mBgMaskProgram.setHostValue("yuv_weights", yuvWeights );
+ } else if (name.equals("maskBg")) {
+ mBgSubtractProgram.setHostValue("mask_blend_bg", mMaskBg);
+ } else if (name.equals("maskFg")) {
+ mBgSubtractProgram.setHostValue("mask_blend_fg", mMaskFg);
+ } else if (name.equals("exposureChange")) {
+ mBgSubtractProgram.setHostValue("exposure_change", mExposureChange);
+ } else if (name.equals("whitebalanceredChange")) {
+ mBgSubtractProgram.setHostValue("whitebalancered_change", mWhiteBalanceRedChange);
+ } else if (name.equals("whitebalanceblueChange")) {
+ mBgSubtractProgram.setHostValue("whitebalanceblue_change", mWhiteBalanceBlueChange);
+ } else if (name.equals("autowbToggle")){
+ mAutomaticWhiteBalanceProgram.setHostValue("autowb_toggle", mAutoWBToggle);
+ }
+ }
+
+ private void updateBgScaling(Frame video, Frame background, boolean fitModeChanged) {
+ float foregroundAspect = (float)video.getFormat().getWidth() / video.getFormat().getHeight();
+ float backgroundAspect = (float)background.getFormat().getWidth() / background.getFormat().getHeight();
+ float currentRelativeAspect = foregroundAspect/backgroundAspect;
+ if (currentRelativeAspect != mRelativeAspect || fitModeChanged) {
+ mRelativeAspect = currentRelativeAspect;
+ float xMin = 0.f, xWidth = 1.f, yMin = 0.f, yWidth = 1.f;
+ switch (mBackgroundFitMode) {
+ case BACKGROUND_STRETCH:
+ // Just map 1:1
+ break;
+ case BACKGROUND_FIT:
+ if (mRelativeAspect > 1.0f) {
+ // Foreground is wider than background, scale down
+ // background in X
+ xMin = 0.5f - 0.5f * mRelativeAspect;
+ xWidth = 1.f * mRelativeAspect;
+ } else {
+ // Foreground is taller than background, scale down
+ // background in Y
+ yMin = 0.5f - 0.5f / mRelativeAspect;
+ yWidth = 1 / mRelativeAspect;
+ }
+ break;
+ case BACKGROUND_FILL_CROP:
+ if (mRelativeAspect > 1.0f) {
+ // Foreground is wider than background, crop
+ // background in Y
+ yMin = 0.5f - 0.5f / mRelativeAspect;
+ yWidth = 1.f / mRelativeAspect;
+ } else {
+ // Foreground is taller than background, crop
+ // background in X
+ xMin = 0.5f - 0.5f * mRelativeAspect;
+ xWidth = mRelativeAspect;
+ }
+ break;
+ }
+ // If mirroring is required (for ex. the camera mirrors the preview
+ // in the front camera)
+ // TODO: Backdropper does not attempt to apply any other transformation
+ // than just flipping. However, in the current state, it's "x-axis" is always aligned
+ // with the Camera's width. Hence, we need to define the mirroring based on the camera
+ // orientation. In the future, a cleaner design would be to cast away all the rotation
+ // in a separate place.
+ if (mMirrorBg) {
+ if (mLogVerbose) Log.v(TAG, "Mirroring the background!");
+ // Mirroring in portrait
+ if (mOrientation == 0 || mOrientation == 180) {
+ xWidth = -xWidth;
+ xMin = 1.0f - xMin;
+ } else {
+ // Mirroring in landscape
+ yWidth = -yWidth;
+ yMin = 1.0f - yMin;
+ }
+ }
+ if (mLogVerbose) Log.v(TAG, "bgTransform: xMin, yMin, xWidth, yWidth : " +
+ xMin + ", " + yMin + ", " + xWidth + ", " + yWidth +
+ ", mRelAspRatio = " + mRelativeAspect);
+ // The following matrix is the transpose of the actual matrix
+ float[] bgTransform = {xWidth, 0.f, 0.f,
+ 0.f, yWidth, 0.f,
+ xMin, yMin, 1.f};
+ mBgSubtractProgram.setHostValue("bg_fit_transform", bgTransform);
+ }
+ }
+
+ private int pyramidLevel(int size) {
+ return (int)Math.floor(Math.log10(size) / Math.log10(2)) - 1;
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/videosink/MediaEncoderFilter.java b/media/mca/filterpacks/java/android/filterpacks/videosink/MediaEncoderFilter.java
new file mode 100644
index 0000000..3657d8a
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/videosink/MediaEncoderFilter.java
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.videosink;
+
+import android.content.Context;
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.FrameManager;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GenerateFinalPort;
+import android.filterfw.core.GLFrame;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+import android.filterfw.geometry.Point;
+import android.filterfw.geometry.Quad;
+import android.os.ConditionVariable;
+import android.media.MediaRecorder;
+import android.media.CamcorderProfile;
+import android.filterfw.core.GLEnvironment;
+
+import java.io.IOException;
+import java.io.FileDescriptor;
+import java.util.List;
+import java.util.Set;
+
+import android.util.Log;
+
+/** @hide */
+public class MediaEncoderFilter extends Filter {
+
+ /** User-visible parameters */
+
+ /** Recording state. When set to false, recording will stop, or will not
+ * start if not yet running the graph. Instead, frames are simply ignored.
+ * When switched back to true, recording will restart. This allows a single
+ * graph to both provide preview and to record video. If this is false,
+ * recording settings can be updated while the graph is running.
+ */
+ @GenerateFieldPort(name = "recording", hasDefault = true)
+ private boolean mRecording = true;
+
+ /** Filename to save the output. */
+ @GenerateFieldPort(name = "outputFile", hasDefault = true)
+ private String mOutputFile = new String("/sdcard/MediaEncoderOut.mp4");
+
+ /** File Descriptor to save the output. */
+ @GenerateFieldPort(name = "outputFileDescriptor", hasDefault = true)
+ private FileDescriptor mFd = null;
+
+ /** Input audio source. If not set, no audio will be recorded.
+ * Select from the values in MediaRecorder.AudioSource
+ */
+ @GenerateFieldPort(name = "audioSource", hasDefault = true)
+ private int mAudioSource = NO_AUDIO_SOURCE;
+
+ /** Media recorder info listener, which needs to implement
+ * MediaRecorder.OnInfoListener. Set this to receive notifications about
+ * recording events.
+ */
+ @GenerateFieldPort(name = "infoListener", hasDefault = true)
+ private MediaRecorder.OnInfoListener mInfoListener = null;
+
+ /** Media recorder error listener, which needs to implement
+ * MediaRecorder.OnErrorListener. Set this to receive notifications about
+ * recording errors.
+ */
+ @GenerateFieldPort(name = "errorListener", hasDefault = true)
+ private MediaRecorder.OnErrorListener mErrorListener = null;
+
+ /** Media recording done callback, which needs to implement OnRecordingDoneListener.
+ * Set this to finalize media upon completion of media recording.
+ */
+ @GenerateFieldPort(name = "recordingDoneListener", hasDefault = true)
+ private OnRecordingDoneListener mRecordingDoneListener = null;
+
+ /** Orientation hint. Used for indicating proper video playback orientation.
+ * Units are in degrees of clockwise rotation, valid values are (0, 90, 180,
+ * 270).
+ */
+ @GenerateFieldPort(name = "orientationHint", hasDefault = true)
+ private int mOrientationHint = 0;
+
+ /** Camcorder profile to use. Select from the profiles available in
+ * android.media.CamcorderProfile. If this field is set, it overrides
+ * settings to width, height, framerate, outputFormat, and videoEncoder.
+ */
+ @GenerateFieldPort(name = "recordingProfile", hasDefault = true)
+ private CamcorderProfile mProfile = null;
+
+ /** Frame width to be encoded, defaults to 320.
+ * Actual received frame size has to match this */
+ @GenerateFieldPort(name = "width", hasDefault = true)
+ private int mWidth = 320;
+
+ /** Frame height to to be encoded, defaults to 240.
+ * Actual received frame size has to match */
+ @GenerateFieldPort(name = "height", hasDefault = true)
+ private int mHeight = 240;
+
+ /** Stream framerate to encode the frames at.
+ * By default, frames are encoded at 30 FPS*/
+ @GenerateFieldPort(name = "framerate", hasDefault = true)
+ private int mFps = 30;
+
+ /** The output format to encode the frames in.
+ * Choose an output format from the options in
+ * android.media.MediaRecorder.OutputFormat */
+ @GenerateFieldPort(name = "outputFormat", hasDefault = true)
+ private int mOutputFormat = MediaRecorder.OutputFormat.MPEG_4;
+
+ /** The videoencoder to encode the frames with.
+ * Choose a videoencoder from the options in
+ * android.media.MediaRecorder.VideoEncoder */
+ @GenerateFieldPort(name = "videoEncoder", hasDefault = true)
+ private int mVideoEncoder = MediaRecorder.VideoEncoder.H264;
+
+ /** The input region to read from the frame. The corners of this quad are
+ * mapped to the output rectangle. The input frame ranges from (0,0)-(1,1),
+ * top-left to bottom-right. The corners of the quad are specified in the
+ * order bottom-left, bottom-right, top-left, top-right.
+ */
+ @GenerateFieldPort(name = "inputRegion", hasDefault = true)
+ private Quad mSourceRegion;
+
+ /** The maximum filesize (in bytes) of the recording session.
+ * By default, it will be 0 and will be passed on to the MediaRecorder.
+ * If the limit is zero or negative, MediaRecorder will disable the limit*/
+ @GenerateFieldPort(name = "maxFileSize", hasDefault = true)
+ private long mMaxFileSize = 0;
+
+ /** The maximum duration (in milliseconds) of the recording session.
+ * By default, it will be 0 and will be passed on to the MediaRecorder.
+ * If the limit is zero or negative, MediaRecorder will record indefinitely*/
+ @GenerateFieldPort(name = "maxDurationMs", hasDefault = true)
+ private int mMaxDurationMs = 0;
+
+ /** TimeLapse Interval between frames.
+ * By default, it will be 0. Whether the recording is timelapsed
+ * is inferred based on its value being greater than 0 */
+ @GenerateFieldPort(name = "timelapseRecordingIntervalUs", hasDefault = true)
+ private long mTimeBetweenTimeLapseFrameCaptureUs = 0;
+
+ // End of user visible parameters
+
+ private static final int NO_AUDIO_SOURCE = -1;
+
+ private int mSurfaceId;
+ private ShaderProgram mProgram;
+ private GLFrame mScreen;
+
+ private boolean mRecordingActive = false;
+ private long mTimestampNs = 0;
+ private long mLastTimeLapseFrameRealTimestampNs = 0;
+ private int mNumFramesEncoded = 0;
+ // Used to indicate whether recording is timelapsed.
+ // Inferred based on (mTimeBetweenTimeLapseFrameCaptureUs > 0)
+ private boolean mCaptureTimeLapse = false;
+
+ private boolean mLogVerbose;
+ private static final String TAG = "MediaEncoderFilter";
+
+ // Our hook to the encoder
+ private MediaRecorder mMediaRecorder;
+
+ /** Callback to be called when media recording completes. */
+
+ public interface OnRecordingDoneListener {
+ public void onRecordingDone();
+ }
+
+ public MediaEncoderFilter(String name) {
+ super(name);
+ Point bl = new Point(0, 0);
+ Point br = new Point(1, 0);
+ Point tl = new Point(0, 1);
+ Point tr = new Point(1, 1);
+ mSourceRegion = new Quad(bl, br, tl, tr);
+ mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+ }
+
+ @Override
+ public void setupPorts() {
+ // Add input port- will accept RGBA GLFrames
+ addMaskedInputPort("videoframe", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU));
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Port " + name + " has been updated");
+ if (name.equals("recording")) return;
+ if (name.equals("inputRegion")) {
+ if (isOpen()) updateSourceRegion();
+ return;
+ }
+ // TODO: Not sure if it is possible to update the maxFileSize
+ // when the recording is going on. For now, not doing that.
+ if (isOpen() && mRecordingActive) {
+ throw new RuntimeException("Cannot change recording parameters"
+ + " when the filter is recording!");
+ }
+ }
+
+ private void updateSourceRegion() {
+ // Flip source quad to map to OpenGL origin
+ Quad flippedRegion = new Quad();
+ flippedRegion.p0 = mSourceRegion.p2;
+ flippedRegion.p1 = mSourceRegion.p3;
+ flippedRegion.p2 = mSourceRegion.p0;
+ flippedRegion.p3 = mSourceRegion.p1;
+ mProgram.setSourceRegion(flippedRegion);
+ }
+
+ // update the MediaRecorderParams based on the variables.
+ // These have to be in certain order as per the MediaRecorder
+ // documentation
+ private void updateMediaRecorderParams() {
+ mCaptureTimeLapse = mTimeBetweenTimeLapseFrameCaptureUs > 0;
+ final int GRALLOC_BUFFER = 2;
+ mMediaRecorder.setVideoSource(GRALLOC_BUFFER);
+ if (!mCaptureTimeLapse && (mAudioSource != NO_AUDIO_SOURCE)) {
+ mMediaRecorder.setAudioSource(mAudioSource);
+ }
+ if (mProfile != null) {
+ mMediaRecorder.setProfile(mProfile);
+ mFps = mProfile.videoFrameRate;
+ } else {
+ mMediaRecorder.setOutputFormat(mOutputFormat);
+ mMediaRecorder.setVideoEncoder(mVideoEncoder);
+ mMediaRecorder.setVideoSize(mWidth, mHeight);
+ mMediaRecorder.setVideoFrameRate(mFps);
+ }
+ mMediaRecorder.setOrientationHint(mOrientationHint);
+ mMediaRecorder.setOnInfoListener(mInfoListener);
+ mMediaRecorder.setOnErrorListener(mErrorListener);
+ if (mFd != null) {
+ mMediaRecorder.setOutputFile(mFd);
+ } else {
+ mMediaRecorder.setOutputFile(mOutputFile);
+ }
+ try {
+ mMediaRecorder.setMaxFileSize(mMaxFileSize);
+ } catch (Exception e) {
+ // Following the logic in VideoCamera.java (in Camera app)
+ // We are going to ignore failure of setMaxFileSize here, as
+ // a) The composer selected may simply not support it, or
+ // b) The underlying media framework may not handle 64-bit range
+ // on the size restriction.
+ Log.w(TAG, "Setting maxFileSize on MediaRecorder unsuccessful! "
+ + e.getMessage());
+ }
+ mMediaRecorder.setMaxDuration(mMaxDurationMs);
+ }
+
+ @Override
+ public void prepare(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Preparing");
+
+ mProgram = ShaderProgram.createIdentity(context);
+
+ mRecordingActive = false;
+ }
+
+ @Override
+ public void open(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Opening");
+ updateSourceRegion();
+ if (mRecording) startRecording(context);
+ }
+
+ private void startRecording(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Starting recording");
+
+ // Create a frame representing the screen
+ MutableFrameFormat screenFormat = new MutableFrameFormat(
+ FrameFormat.TYPE_BYTE, FrameFormat.TARGET_GPU);
+ screenFormat.setBytesPerSample(4);
+
+ int width, height;
+ if (mProfile != null) {
+ width = mProfile.videoFrameWidth;
+ height = mProfile.videoFrameHeight;
+ } else {
+ width = mWidth;
+ height = mHeight;
+ }
+ screenFormat.setDimensions(width, height);
+ mScreen = (GLFrame)context.getFrameManager().newBoundFrame(
+ screenFormat, GLFrame.EXISTING_FBO_BINDING, 0);
+
+ // Initialize the media recorder
+
+ mMediaRecorder = new MediaRecorder();
+ updateMediaRecorderParams();
+
+ try {
+ mMediaRecorder.prepare();
+ } catch (IllegalStateException e) {
+ throw e;
+ } catch (IOException e) {
+ throw new RuntimeException("IOException in"
+ + "MediaRecorder.prepare()!", e);
+ } catch (Exception e) {
+ throw new RuntimeException("Unknown Exception in"
+ + "MediaRecorder.prepare()!", e);
+ }
+ // Make sure start() is called before trying to
+ // register the surface. The native window handle needed to create
+ // the surface is initiated in start()
+ mMediaRecorder.start();
+ if (mLogVerbose) Log.v(TAG, "Open: registering surface from Mediarecorder");
+ mSurfaceId = context.getGLEnvironment().
+ registerSurfaceFromMediaRecorder(mMediaRecorder);
+ mNumFramesEncoded = 0;
+ mRecordingActive = true;
+ }
+
+ public boolean skipFrameAndModifyTimestamp(long timestampNs) {
+ // first frame- encode. Don't skip
+ if (mNumFramesEncoded == 0) {
+ mLastTimeLapseFrameRealTimestampNs = timestampNs;
+ mTimestampNs = timestampNs;
+ if (mLogVerbose) Log.v(TAG, "timelapse: FIRST frame, last real t= "
+ + mLastTimeLapseFrameRealTimestampNs +
+ ", setting t = " + mTimestampNs );
+ return false;
+ }
+
+ // Workaround to bypass the first 2 input frames for skipping.
+ // The first 2 output frames from the encoder are: decoder specific info and
+ // the compressed video frame data for the first input video frame.
+ if (mNumFramesEncoded >= 2 && timestampNs <
+ (mLastTimeLapseFrameRealTimestampNs + 1000L * mTimeBetweenTimeLapseFrameCaptureUs)) {
+ // If 2 frames have been already encoded,
+ // Skip all frames from last encoded frame until
+ // sufficient time (mTimeBetweenTimeLapseFrameCaptureUs) has passed.
+ if (mLogVerbose) Log.v(TAG, "timelapse: skipping intermediate frame");
+ return true;
+ } else {
+ // Desired frame has arrived after mTimeBetweenTimeLapseFrameCaptureUs time:
+ // - Reset mLastTimeLapseFrameRealTimestampNs to current time.
+ // - Artificially modify timestampNs to be one frame time (1/framerate) ahead
+ // of the last encoded frame's time stamp.
+ if (mLogVerbose) Log.v(TAG, "timelapse: encoding frame, Timestamp t = " + timestampNs +
+ ", last real t= " + mLastTimeLapseFrameRealTimestampNs +
+ ", interval = " + mTimeBetweenTimeLapseFrameCaptureUs);
+ mLastTimeLapseFrameRealTimestampNs = timestampNs;
+ mTimestampNs = mTimestampNs + (1000000000L / (long)mFps);
+ if (mLogVerbose) Log.v(TAG, "timelapse: encoding frame, setting t = "
+ + mTimestampNs + ", delta t = " + (1000000000L / (long)mFps) +
+ ", fps = " + mFps );
+ return false;
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Starting frame processing");
+
+ GLEnvironment glEnv = context.getGLEnvironment();
+ // Get input frame
+ Frame input = pullInput("videoframe");
+
+ // Check if recording needs to start
+ if (!mRecordingActive && mRecording) {
+ startRecording(context);
+ }
+ // Check if recording needs to stop
+ if (mRecordingActive && !mRecording) {
+ stopRecording(context);
+ }
+
+ if (!mRecordingActive) return;
+
+ if (mCaptureTimeLapse) {
+ if (skipFrameAndModifyTimestamp(input.getTimestamp())) {
+ return;
+ }
+ } else {
+ mTimestampNs = input.getTimestamp();
+ }
+
+ // Activate our surface
+ glEnv.activateSurfaceWithId(mSurfaceId);
+
+ // Process
+ mProgram.process(input, mScreen);
+
+ // Set timestamp from input
+ glEnv.setSurfaceTimestamp(mTimestampNs);
+ // And swap buffers
+ glEnv.swapBuffers();
+ mNumFramesEncoded++;
+ if (mLogVerbose) Log.v(TAG, "numFramesEncoded = " + mNumFramesEncoded);
+ }
+
+ private void stopRecording(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Stopping recording");
+
+ mRecordingActive = false;
+ mNumFramesEncoded = 0;
+ GLEnvironment glEnv = context.getGLEnvironment();
+ // The following call will switch the surface_id to 0
+ // (thus, calling eglMakeCurrent on surface with id 0) and
+ // then call eglDestroy on the surface. Hence, this will
+ // call disconnect the SurfaceMediaSource, which is needed to
+ // be called before calling Stop on the mediarecorder
+ if (mLogVerbose) Log.v(TAG, String.format("Unregistering surface %d", mSurfaceId));
+ glEnv.unregisterSurfaceId(mSurfaceId);
+ try {
+ mMediaRecorder.stop();
+ } catch (RuntimeException e) {
+ throw new MediaRecorderStopException("MediaRecorder.stop() failed!", e);
+ }
+ mMediaRecorder.release();
+ mMediaRecorder = null;
+
+ mScreen.release();
+ mScreen = null;
+
+ // Use an EffectsRecorder callback to forward a media finalization
+ // call so that it creates the video thumbnail, and whatever else needs
+ // to be done to finalize media.
+ if (mRecordingDoneListener != null) {
+ mRecordingDoneListener.onRecordingDone();
+ }
+ }
+
+ @Override
+ public void close(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Closing");
+ if (mRecordingActive) stopRecording(context);
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ // Release all the resources associated with the MediaRecorder
+ // and GLFrame members
+ if (mMediaRecorder != null) {
+ mMediaRecorder.release();
+ }
+ if (mScreen != null) {
+ mScreen.release();
+ }
+
+ }
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/videosink/MediaRecorderStopException.java b/media/mca/filterpacks/java/android/filterpacks/videosink/MediaRecorderStopException.java
new file mode 100644
index 0000000..dbf9768
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/videosink/MediaRecorderStopException.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.videosink;
+
+import java.lang.RuntimeException;
+import android.util.Log;
+
+/** @hide **/
+public class MediaRecorderStopException extends RuntimeException {
+
+ private static final String TAG = "MediaRecorderStopException";
+
+ public MediaRecorderStopException(String msg) {
+ super(msg);
+ }
+
+ public MediaRecorderStopException() {
+ super();
+ }
+
+ public MediaRecorderStopException(String msg, Throwable t) {
+ super(msg, t);
+ }
+
+ public MediaRecorderStopException(Throwable t) {
+ super(t);
+ }
+}
+
diff --git a/media/mca/filterpacks/java/android/filterpacks/videosrc/CameraSource.java b/media/mca/filterpacks/java/android/filterpacks/videosrc/CameraSource.java
new file mode 100644
index 0000000..2c474ab
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/videosrc/CameraSource.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.videosrc;
+
+import android.content.Context;
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.FrameManager;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GenerateFinalPort;
+import android.filterfw.core.GLFrame;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+import android.graphics.SurfaceTexture;
+import android.hardware.Camera;
+import android.os.ConditionVariable;
+import android.opengl.Matrix;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class CameraSource extends Filter {
+
+ /** User-visible parameters */
+
+ /** Camera ID to use for input. Defaults to 0. */
+ @GenerateFieldPort(name = "id", hasDefault = true)
+ private int mCameraId = 0;
+
+ /** Frame width to request from camera. Actual size may not match requested. */
+ @GenerateFieldPort(name = "width", hasDefault = true)
+ private int mWidth = 320;
+
+ /** Frame height to request from camera. Actual size may not match requested. */
+ @GenerateFieldPort(name = "height", hasDefault = true)
+ private int mHeight = 240;
+
+ /** Stream framerate to request from camera. Actual frame rate may not match requested. */
+ @GenerateFieldPort(name = "framerate", hasDefault = true)
+ private int mFps = 30;
+
+ /** Whether the filter should always wait for a new frame from the camera
+ * before providing output. If set to false, the filter will keep
+ * outputting the last frame it received from the camera if multiple process
+ * calls are received before the next update from the Camera. Defaults to true.
+ */
+ @GenerateFinalPort(name = "waitForNewFrame", hasDefault = true)
+ private boolean mWaitForNewFrame = true;
+
+ private Camera mCamera;
+ private GLFrame mCameraFrame;
+ private SurfaceTexture mSurfaceTexture;
+ private ShaderProgram mFrameExtractor;
+ private MutableFrameFormat mOutputFormat;
+
+ private float[] mCameraTransform;
+ private float[] mMappedCoords;
+ // These default source coordinates perform the necessary flip
+ // for converting from OpenGL origin to MFF/Bitmap origin.
+ private static final float[] mSourceCoords = { 0, 1, 0, 1,
+ 1, 1, 0, 1,
+ 0, 0, 0, 1,
+ 1, 0, 0, 1 };
+
+ private static final int NEWFRAME_TIMEOUT = 100; //ms
+ private static final int NEWFRAME_TIMEOUT_REPEAT = 10;
+
+ private boolean mNewFrameAvailable;
+
+ private Camera.Parameters mCameraParameters;
+
+ private static final String mFrameShader =
+ "#extension GL_OES_EGL_image_external : require\n" +
+ "precision mediump float;\n" +
+ "uniform samplerExternalOES tex_sampler_0;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " gl_FragColor = texture2D(tex_sampler_0, v_texcoord);\n" +
+ "}\n";
+
+ private final boolean mLogVerbose;
+ private static final String TAG = "CameraSource";
+
+ public CameraSource(String name) {
+ super(name);
+ mCameraTransform = new float[16];
+ mMappedCoords = new float[16];
+
+ mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+ }
+
+ @Override
+ public void setupPorts() {
+ // Add input port
+ addOutputPort("video", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU));
+ }
+
+ private void createFormats() {
+ mOutputFormat = ImageFormat.create(mWidth, mHeight,
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ }
+
+ @Override
+ public void prepare(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Preparing");
+ // Compile shader TODO: Move to onGLEnvSomething?
+ mFrameExtractor = new ShaderProgram(context, mFrameShader);
+ }
+
+ @Override
+ public void open(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Opening");
+ // Open camera
+ mCamera = Camera.open(mCameraId);
+
+ // Set parameters
+ getCameraParameters();
+ mCamera.setParameters(mCameraParameters);
+
+ // Create frame formats
+ createFormats();
+
+ // Bind it to our camera frame
+ mCameraFrame = (GLFrame)context.getFrameManager().newBoundFrame(mOutputFormat,
+ GLFrame.EXTERNAL_TEXTURE,
+ 0);
+ mSurfaceTexture = new SurfaceTexture(mCameraFrame.getTextureId());
+ try {
+ mCamera.setPreviewTexture(mSurfaceTexture);
+ } catch (IOException e) {
+ throw new RuntimeException("Could not bind camera surface texture: " +
+ e.getMessage() + "!");
+ }
+
+ // Connect SurfaceTexture to callback
+ mSurfaceTexture.setOnFrameAvailableListener(onCameraFrameAvailableListener);
+ // Start the preview
+ mNewFrameAvailable = false;
+ mCamera.startPreview();
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Processing new frame");
+
+ if (mWaitForNewFrame) {
+ int waitCount = 0;
+ while (!mNewFrameAvailable) {
+ if (waitCount == NEWFRAME_TIMEOUT_REPEAT) {
+ throw new RuntimeException("Timeout waiting for new frame");
+ }
+ try {
+ this.wait(NEWFRAME_TIMEOUT);
+ } catch (InterruptedException e) {
+ if (mLogVerbose) Log.v(TAG, "Interrupted while waiting for new frame");
+ }
+ }
+ mNewFrameAvailable = false;
+ if (mLogVerbose) Log.v(TAG, "Got new frame");
+ }
+
+ mSurfaceTexture.updateTexImage();
+
+ if (mLogVerbose) Log.v(TAG, "Using frame extractor in thread: " + Thread.currentThread());
+ mSurfaceTexture.getTransformMatrix(mCameraTransform);
+ Matrix.multiplyMM(mMappedCoords, 0,
+ mCameraTransform, 0,
+ mSourceCoords, 0);
+ mFrameExtractor.setSourceRegion(mMappedCoords[0], mMappedCoords[1],
+ mMappedCoords[4], mMappedCoords[5],
+ mMappedCoords[8], mMappedCoords[9],
+ mMappedCoords[12], mMappedCoords[13]);
+
+ Frame output = context.getFrameManager().newFrame(mOutputFormat);
+ mFrameExtractor.process(mCameraFrame, output);
+
+ long timestamp = mSurfaceTexture.getTimestamp();
+ if (mLogVerbose) Log.v(TAG, "Timestamp: " + (timestamp / 1000000000.0) + " s");
+ output.setTimestamp(timestamp);
+
+ pushOutput("video", output);
+
+ // Release pushed frame
+ output.release();
+
+ if (mLogVerbose) Log.v(TAG, "Done processing new frame");
+ }
+
+ @Override
+ public void close(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Closing");
+
+ mCamera.release();
+ mCamera = null;
+ mSurfaceTexture.release();
+ mSurfaceTexture = null;
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ if (mCameraFrame != null) {
+ mCameraFrame.release();
+ }
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (name.equals("framerate")) {
+ getCameraParameters();
+ int closestRange[] = findClosestFpsRange(mFps, mCameraParameters);
+ mCameraParameters.setPreviewFpsRange(closestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
+ closestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
+ mCamera.setParameters(mCameraParameters);
+ }
+ }
+
+ synchronized public Camera.Parameters getCameraParameters() {
+ boolean closeCamera = false;
+ if (mCameraParameters == null) {
+ if (mCamera == null) {
+ mCamera = Camera.open(mCameraId);
+ closeCamera = true;
+ }
+ mCameraParameters = mCamera.getParameters();
+
+ if (closeCamera) {
+ mCamera.release();
+ mCamera = null;
+ }
+ }
+
+ int closestSize[] = findClosestSize(mWidth, mHeight, mCameraParameters);
+ mWidth = closestSize[0];
+ mHeight = closestSize[1];
+ mCameraParameters.setPreviewSize(mWidth, mHeight);
+
+ int closestRange[] = findClosestFpsRange(mFps, mCameraParameters);
+
+ mCameraParameters.setPreviewFpsRange(closestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
+ closestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
+
+ return mCameraParameters;
+ }
+
+ /** Update camera parameters. Image resolution cannot be changed. */
+ synchronized public void setCameraParameters(Camera.Parameters params) {
+ params.setPreviewSize(mWidth, mHeight);
+ mCameraParameters = params;
+ if (isOpen()) {
+ mCamera.setParameters(mCameraParameters);
+ }
+ }
+
+ private int[] findClosestSize(int width, int height, Camera.Parameters parameters) {
+ List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
+ int closestWidth = -1;
+ int closestHeight = -1;
+ int smallestWidth = previewSizes.get(0).width;
+ int smallestHeight = previewSizes.get(0).height;
+ for (Camera.Size size : previewSizes) {
+ // Best match defined as not being larger in either dimension than
+ // the requested size, but as close as possible. The below isn't a
+ // stable selection (reording the size list can give different
+ // results), but since this is a fallback nicety, that's acceptable.
+ if ( size.width <= width &&
+ size.height <= height &&
+ size.width >= closestWidth &&
+ size.height >= closestHeight) {
+ closestWidth = size.width;
+ closestHeight = size.height;
+ }
+ if ( size.width < smallestWidth &&
+ size.height < smallestHeight) {
+ smallestWidth = size.width;
+ smallestHeight = size.height;
+ }
+ }
+ if (closestWidth == -1) {
+ // Requested size is smaller than any listed size; match with smallest possible
+ closestWidth = smallestWidth;
+ closestHeight = smallestHeight;
+ }
+
+ if (mLogVerbose) {
+ Log.v(TAG,
+ "Requested resolution: (" + width + ", " + height
+ + "). Closest match: (" + closestWidth + ", "
+ + closestHeight + ").");
+ }
+ int[] closestSize = {closestWidth, closestHeight};
+ return closestSize;
+ }
+
+ private int[] findClosestFpsRange(int fps, Camera.Parameters params) {
+ List<int[]> supportedFpsRanges = params.getSupportedPreviewFpsRange();
+ int[] closestRange = supportedFpsRanges.get(0);
+ for (int[] range : supportedFpsRanges) {
+ if (range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] < fps*1000 &&
+ range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] > fps*1000 &&
+ range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] >
+ closestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] &&
+ range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] <
+ closestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]) {
+ closestRange = range;
+ }
+ }
+ if (mLogVerbose) Log.v(TAG, "Requested fps: " + fps
+ + ".Closest frame rate range: ["
+ + closestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] / 1000.
+ + ","
+ + closestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] / 1000.
+ + "]");
+
+ return closestRange;
+ }
+
+ private SurfaceTexture.OnFrameAvailableListener onCameraFrameAvailableListener =
+ new SurfaceTexture.OnFrameAvailableListener() {
+ @Override
+ public void onFrameAvailable(SurfaceTexture surfaceTexture) {
+ if (mLogVerbose) Log.v(TAG, "New frame from camera");
+ synchronized(CameraSource.this) {
+ mNewFrameAvailable = true;
+ CameraSource.this.notify();
+ }
+ }
+ };
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/videosrc/MediaSource.java b/media/mca/filterpacks/java/android/filterpacks/videosrc/MediaSource.java
new file mode 100644
index 0000000..9c40cec
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/videosrc/MediaSource.java
@@ -0,0 +1,567 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.videosrc;
+
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.FrameManager;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GenerateFinalPort;
+import android.filterfw.core.GLFrame;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+import android.graphics.SurfaceTexture;
+import android.media.MediaPlayer;
+import android.os.ConditionVariable;
+import android.opengl.Matrix;
+import android.view.Surface;
+
+import java.io.IOException;
+import java.io.FileDescriptor;
+import java.lang.IllegalArgumentException;
+import java.util.List;
+import java.util.Set;
+
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class MediaSource extends Filter {
+
+ /** User-visible parameters */
+
+ /** The source URL for the media source. Can be an http: link to a remote
+ * resource, or a file: link to a local media file
+ */
+ @GenerateFieldPort(name = "sourceUrl", hasDefault = true)
+ private String mSourceUrl = "";
+
+ /** An open asset file descriptor to a local media source. Default is null */
+ @GenerateFieldPort(name = "sourceAsset", hasDefault = true)
+ private AssetFileDescriptor mSourceAsset = null;
+
+ /** Whether the media source is a URL or an asset file descriptor. Defaults
+ * to false.
+ */
+ @GenerateFieldPort(name = "sourceIsUrl", hasDefault = true)
+ private boolean mSelectedIsUrl = false;
+
+ /** Whether the filter will always wait for a new video frame, or whether it
+ * will output an old frame again if a new frame isn't available. Defaults
+ * to true.
+ */
+ @GenerateFinalPort(name = "waitForNewFrame", hasDefault = true)
+ private boolean mWaitForNewFrame = true;
+
+ /** Whether the media source should loop automatically or not. Defaults to
+ * true.
+ */
+ @GenerateFieldPort(name = "loop", hasDefault = true)
+ private boolean mLooping = true;
+
+ /** Volume control. Currently sound is piped directly to the speakers, so
+ * this defaults to mute.
+ */
+ @GenerateFieldPort(name = "volume", hasDefault = true)
+ private float mVolume = 0.f;
+
+ /** Orientation. This controls the output orientation of the video. Valid
+ * values are 0, 90, 180, 270
+ */
+ @GenerateFieldPort(name = "orientation", hasDefault = true)
+ private int mOrientation = 0;
+
+ private MediaPlayer mMediaPlayer;
+ private GLFrame mMediaFrame;
+ private SurfaceTexture mSurfaceTexture;
+ private ShaderProgram mFrameExtractor;
+ private MutableFrameFormat mOutputFormat;
+ private int mWidth, mHeight;
+
+ // Total timeouts will be PREP_TIMEOUT*PREP_TIMEOUT_REPEAT
+ private static final int PREP_TIMEOUT = 100; // ms
+ private static final int PREP_TIMEOUT_REPEAT = 100;
+ private static final int NEWFRAME_TIMEOUT = 100; //ms
+ private static final int NEWFRAME_TIMEOUT_REPEAT = 10;
+
+ // This is an identity shader; not using the default identity
+ // shader because reading from a SurfaceTexture requires the
+ // GL_OES_EGL_image_external extension.
+ private final String mFrameShader =
+ "#extension GL_OES_EGL_image_external : require\n" +
+ "precision mediump float;\n" +
+ "uniform samplerExternalOES tex_sampler_0;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " gl_FragColor = texture2D(tex_sampler_0, v_texcoord);\n" +
+ "}\n";
+
+ // The following transforms enable rotation of the decoded source.
+ // These are multiplied with the transform obtained from the
+ // SurfaceTexture to get the final transform to be set on the media source.
+ // Currently, given a device orientation, the MediaSource rotates in such a way
+ // that the source is displayed upright. A particular use case
+ // is "Background Replacement" feature in the Camera app
+ // where the MediaSource rotates the source to align with the camera feed and pass it
+ // on to the backdropper filter. The backdropper only does the blending
+ // and does not have to do any rotation
+ // (except for mirroring in case of front camera).
+ // TODO: Currently the rotations are spread over a bunch of stages in the
+ // pipeline. A cleaner design
+ // could be to cast away all the rotation in a separate filter or attach a transform
+ // to the frame so that MediaSource itself need not know about any rotation.
+ private static final float[] mSourceCoords_0 = { 1, 1, 0, 1,
+ 0, 1, 0, 1,
+ 1, 0, 0, 1,
+ 0, 0, 0, 1 };
+ private static final float[] mSourceCoords_270 = { 0, 1, 0, 1,
+ 0, 0, 0, 1,
+ 1, 1, 0, 1,
+ 1, 0, 0, 1 };
+ private static final float[] mSourceCoords_180 = { 0, 0, 0, 1,
+ 1, 0, 0, 1,
+ 0, 1, 0, 1,
+ 1, 1, 0, 1 };
+ private static final float[] mSourceCoords_90 = { 1, 0, 0, 1,
+ 1, 1, 0, 1,
+ 0, 0, 0, 1,
+ 0, 1, 0, 1 };
+
+ private boolean mGotSize;
+ private boolean mPrepared;
+ private boolean mPlaying;
+ private boolean mNewFrameAvailable;
+ private boolean mOrientationUpdated;
+ private boolean mPaused;
+ private boolean mCompleted;
+
+ private final boolean mLogVerbose;
+ private static final String TAG = "MediaSource";
+
+ public MediaSource(String name) {
+ super(name);
+ mNewFrameAvailable = false;
+
+ mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+ }
+
+ @Override
+ public void setupPorts() {
+ // Add input port
+ addOutputPort("video", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU));
+ }
+
+ private void createFormats() {
+ mOutputFormat = ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ }
+
+ @Override
+ protected void prepare(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Preparing MediaSource");
+
+ mFrameExtractor = new ShaderProgram(context, mFrameShader);
+ // SurfaceTexture defines (0,0) to be bottom-left. The filter framework
+ // defines (0,0) as top-left, so do the flip here.
+ mFrameExtractor.setSourceRect(0, 1, 1, -1);
+
+ createFormats();
+ }
+
+ @Override
+ public void open(FilterContext context) {
+ if (mLogVerbose) {
+ Log.v(TAG, "Opening MediaSource");
+ if (mSelectedIsUrl) {
+ Log.v(TAG, "Current URL is " + mSourceUrl);
+ } else {
+ Log.v(TAG, "Current source is Asset!");
+ }
+ }
+
+ mMediaFrame = (GLFrame)context.getFrameManager().newBoundFrame(
+ mOutputFormat,
+ GLFrame.EXTERNAL_TEXTURE,
+ 0);
+
+ mSurfaceTexture = new SurfaceTexture(mMediaFrame.getTextureId());
+
+ if (!setupMediaPlayer(mSelectedIsUrl)) {
+ throw new RuntimeException("Error setting up MediaPlayer!");
+ }
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ // Note: process is synchronized by its caller in the Filter base class
+ if (mLogVerbose) Log.v(TAG, "Processing new frame");
+
+ if (mMediaPlayer == null) {
+ // Something went wrong in initialization or parameter updates
+ throw new NullPointerException("Unexpected null media player!");
+ }
+
+ if (mCompleted) {
+ // Video playback is done, so close us down
+ closeOutputPort("video");
+ return;
+ }
+
+ if (!mPlaying) {
+ int waitCount = 0;
+ if (mLogVerbose) Log.v(TAG, "Waiting for preparation to complete");
+ while (!mGotSize || !mPrepared) {
+ try {
+ this.wait(PREP_TIMEOUT);
+ } catch (InterruptedException e) {
+ // ignoring
+ }
+ if (mCompleted) {
+ // Video playback is done, so close us down
+ closeOutputPort("video");
+ return;
+ }
+ waitCount++;
+ if (waitCount == PREP_TIMEOUT_REPEAT) {
+ mMediaPlayer.release();
+ throw new RuntimeException("MediaPlayer timed out while preparing!");
+ }
+ }
+ if (mLogVerbose) Log.v(TAG, "Starting playback");
+ mMediaPlayer.start();
+ }
+
+ // Use last frame if paused, unless just starting playback, in which case
+ // we want at least one valid frame before pausing
+ if (!mPaused || !mPlaying) {
+ if (mWaitForNewFrame) {
+ if (mLogVerbose) Log.v(TAG, "Waiting for new frame");
+
+ int waitCount = 0;
+ while (!mNewFrameAvailable) {
+ if (waitCount == NEWFRAME_TIMEOUT_REPEAT) {
+ if (mCompleted) {
+ // Video playback is done, so close us down
+ closeOutputPort("video");
+ return;
+ } else {
+ throw new RuntimeException("Timeout waiting for new frame!");
+ }
+ }
+ try {
+ this.wait(NEWFRAME_TIMEOUT);
+ } catch (InterruptedException e) {
+ if (mLogVerbose) Log.v(TAG, "interrupted");
+ // ignoring
+ }
+ waitCount++;
+ }
+ mNewFrameAvailable = false;
+ if (mLogVerbose) Log.v(TAG, "Got new frame");
+ }
+
+ mSurfaceTexture.updateTexImage();
+ mOrientationUpdated = true;
+ }
+ if (mOrientationUpdated) {
+ float[] surfaceTransform = new float[16];
+ mSurfaceTexture.getTransformMatrix(surfaceTransform);
+
+ float[] sourceCoords = new float[16];
+ switch (mOrientation) {
+ default:
+ case 0:
+ Matrix.multiplyMM(sourceCoords, 0,
+ surfaceTransform, 0,
+ mSourceCoords_0, 0);
+ break;
+ case 90:
+ Matrix.multiplyMM(sourceCoords, 0,
+ surfaceTransform, 0,
+ mSourceCoords_90, 0);
+ break;
+ case 180:
+ Matrix.multiplyMM(sourceCoords, 0,
+ surfaceTransform, 0,
+ mSourceCoords_180, 0);
+ break;
+ case 270:
+ Matrix.multiplyMM(sourceCoords, 0,
+ surfaceTransform, 0,
+ mSourceCoords_270, 0);
+ break;
+ }
+ if (mLogVerbose) {
+ Log.v(TAG, "OrientationHint = " + mOrientation);
+ String temp = String.format("SetSourceRegion: %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f",
+ sourceCoords[4], sourceCoords[5],sourceCoords[0], sourceCoords[1],
+ sourceCoords[12], sourceCoords[13],sourceCoords[8], sourceCoords[9]);
+ Log.v(TAG, temp);
+ }
+ mFrameExtractor.setSourceRegion(sourceCoords[4], sourceCoords[5],
+ sourceCoords[0], sourceCoords[1],
+ sourceCoords[12], sourceCoords[13],
+ sourceCoords[8], sourceCoords[9]);
+ mOrientationUpdated = false;
+ }
+
+ Frame output = context.getFrameManager().newFrame(mOutputFormat);
+ mFrameExtractor.process(mMediaFrame, output);
+
+ long timestamp = mSurfaceTexture.getTimestamp();
+ if (mLogVerbose) Log.v(TAG, "Timestamp: " + (timestamp / 1000000000.0) + " s");
+ output.setTimestamp(timestamp);
+
+ pushOutput("video", output);
+ output.release();
+
+ mPlaying = true;
+ }
+
+ @Override
+ public void close(FilterContext context) {
+ if (mMediaPlayer.isPlaying()) {
+ mMediaPlayer.stop();
+ }
+ mPrepared = false;
+ mGotSize = false;
+ mPlaying = false;
+ mPaused = false;
+ mCompleted = false;
+ mNewFrameAvailable = false;
+
+ mMediaPlayer.release();
+ mMediaPlayer = null;
+ mSurfaceTexture.release();
+ mSurfaceTexture = null;
+ if (mLogVerbose) Log.v(TAG, "MediaSource closed");
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ if (mMediaFrame != null) {
+ mMediaFrame.release();
+ }
+ }
+
+ // When updating the port values of the filter, users can update sourceIsUrl to switch
+ // between using URL objects or Assets.
+ // If updating only sourceUrl/sourceAsset, MediaPlayer gets reset if the current player
+ // uses Url objects/Asset.
+ // Otherwise the new sourceUrl/sourceAsset is stored and will be used when users switch
+ // sourceIsUrl next time.
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Parameter update");
+ if (name.equals("sourceUrl")) {
+ if (isOpen()) {
+ if (mLogVerbose) Log.v(TAG, "Opening new source URL");
+ if (mSelectedIsUrl) {
+ setupMediaPlayer(mSelectedIsUrl);
+ }
+ }
+ } else if (name.equals("sourceAsset") ) {
+ if (isOpen()) {
+ if (mLogVerbose) Log.v(TAG, "Opening new source FD");
+ if (!mSelectedIsUrl) {
+ setupMediaPlayer(mSelectedIsUrl);
+ }
+ }
+ } else if (name.equals("loop")) {
+ if (isOpen()) {
+ mMediaPlayer.setLooping(mLooping);
+ }
+ } else if (name.equals("sourceIsUrl")) {
+ if (isOpen()){
+ if (mSelectedIsUrl){
+ if (mLogVerbose) Log.v(TAG, "Opening new source URL");
+ } else {
+ if (mLogVerbose) Log.v(TAG, "Opening new source Asset");
+ }
+ setupMediaPlayer(mSelectedIsUrl);
+ }
+ } else if (name.equals("volume")) {
+ if (isOpen()) {
+ mMediaPlayer.setVolume(mVolume, mVolume);
+ }
+ } else if (name.equals("orientation") && mGotSize) {
+ if (mOrientation == 0 || mOrientation == 180) {
+ mOutputFormat.setDimensions(mWidth, mHeight);
+ } else {
+ mOutputFormat.setDimensions(mHeight, mWidth);
+ }
+ mOrientationUpdated = true;
+ }
+ }
+
+ synchronized public void pauseVideo(boolean pauseState) {
+ if (isOpen()) {
+ if (pauseState && !mPaused) {
+ mMediaPlayer.pause();
+ } else if (!pauseState && mPaused) {
+ mMediaPlayer.start();
+ }
+ }
+ mPaused = pauseState;
+ }
+
+ /** Creates a media player, sets it up, and calls prepare */
+ synchronized private boolean setupMediaPlayer(boolean useUrl) {
+ mPrepared = false;
+ mGotSize = false;
+ mPlaying = false;
+ mPaused = false;
+ mCompleted = false;
+ mNewFrameAvailable = false;
+
+ if (mLogVerbose) Log.v(TAG, "Setting up playback.");
+
+ if (mMediaPlayer != null) {
+ // Clean up existing media players
+ if (mLogVerbose) Log.v(TAG, "Resetting existing MediaPlayer.");
+ mMediaPlayer.reset();
+ } else {
+ // Create new media player
+ if (mLogVerbose) Log.v(TAG, "Creating new MediaPlayer.");
+ mMediaPlayer = new MediaPlayer();
+ }
+
+ if (mMediaPlayer == null) {
+ throw new RuntimeException("Unable to create a MediaPlayer!");
+ }
+
+ // Set up data sources, etc
+ try {
+ if (useUrl) {
+ if (mLogVerbose) Log.v(TAG, "Setting MediaPlayer source to URI " + mSourceUrl);
+ mMediaPlayer.setDataSource(mSourceUrl);
+ } else {
+ if (mLogVerbose) Log.v(TAG, "Setting MediaPlayer source to asset " + mSourceAsset);
+ mMediaPlayer.setDataSource(mSourceAsset.getFileDescriptor(), mSourceAsset.getStartOffset(), mSourceAsset.getLength());
+ }
+ } catch(IOException e) {
+ mMediaPlayer.release();
+ mMediaPlayer = null;
+ if (useUrl) {
+ throw new RuntimeException(String.format("Unable to set MediaPlayer to URL %s!", mSourceUrl), e);
+ } else {
+ throw new RuntimeException(String.format("Unable to set MediaPlayer to asset %s!", mSourceAsset), e);
+ }
+ } catch(IllegalArgumentException e) {
+ mMediaPlayer.release();
+ mMediaPlayer = null;
+ if (useUrl) {
+ throw new RuntimeException(String.format("Unable to set MediaPlayer to URL %s!", mSourceUrl), e);
+ } else {
+ throw new RuntimeException(String.format("Unable to set MediaPlayer to asset %s!", mSourceAsset), e);
+ }
+ }
+
+ mMediaPlayer.setLooping(mLooping);
+ mMediaPlayer.setVolume(mVolume, mVolume);
+
+ // Bind it to our media frame
+ Surface surface = new Surface(mSurfaceTexture);
+ mMediaPlayer.setSurface(surface);
+ surface.release();
+
+ // Connect Media Player to callbacks
+
+ mMediaPlayer.setOnVideoSizeChangedListener(onVideoSizeChangedListener);
+ mMediaPlayer.setOnPreparedListener(onPreparedListener);
+ mMediaPlayer.setOnCompletionListener(onCompletionListener);
+
+ // Connect SurfaceTexture to callback
+ mSurfaceTexture.setOnFrameAvailableListener(onMediaFrameAvailableListener);
+
+ if (mLogVerbose) Log.v(TAG, "Preparing MediaPlayer.");
+ mMediaPlayer.prepareAsync();
+
+ return true;
+ }
+
+ private MediaPlayer.OnVideoSizeChangedListener onVideoSizeChangedListener =
+ new MediaPlayer.OnVideoSizeChangedListener() {
+ public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
+ if (mLogVerbose) Log.v(TAG, "MediaPlayer sent dimensions: " + width + " x " + height);
+ if (!mGotSize) {
+ if (mOrientation == 0 || mOrientation == 180) {
+ mOutputFormat.setDimensions(width, height);
+ } else {
+ mOutputFormat.setDimensions(height, width);
+ }
+ mWidth = width;
+ mHeight = height;
+ } else {
+ if (mOutputFormat.getWidth() != width ||
+ mOutputFormat.getHeight() != height) {
+ Log.e(TAG, "Multiple video size change events received!");
+ }
+ }
+ synchronized(MediaSource.this) {
+ mGotSize = true;
+ MediaSource.this.notify();
+ }
+ }
+ };
+
+ private MediaPlayer.OnPreparedListener onPreparedListener =
+ new MediaPlayer.OnPreparedListener() {
+ public void onPrepared(MediaPlayer mp) {
+ if (mLogVerbose) Log.v(TAG, "MediaPlayer is prepared");
+ synchronized(MediaSource.this) {
+ mPrepared = true;
+ MediaSource.this.notify();
+ }
+ }
+ };
+
+ private MediaPlayer.OnCompletionListener onCompletionListener =
+ new MediaPlayer.OnCompletionListener() {
+ public void onCompletion(MediaPlayer mp) {
+ if (mLogVerbose) Log.v(TAG, "MediaPlayer has completed playback");
+ synchronized(MediaSource.this) {
+ mCompleted = true;
+ }
+ }
+ };
+
+ private SurfaceTexture.OnFrameAvailableListener onMediaFrameAvailableListener =
+ new SurfaceTexture.OnFrameAvailableListener() {
+ public void onFrameAvailable(SurfaceTexture surfaceTexture) {
+ if (mLogVerbose) Log.v(TAG, "New frame from media player");
+ synchronized(MediaSource.this) {
+ if (mLogVerbose) Log.v(TAG, "New frame: notify");
+ mNewFrameAvailable = true;
+ MediaSource.this.notify();
+ if (mLogVerbose) Log.v(TAG, "New frame: notify done");
+ }
+ }
+ };
+
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java
new file mode 100644
index 0000000..37fa242
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.filterpacks.videosrc;
+
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.FrameManager;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GenerateFinalPort;
+import android.filterfw.core.GLFrame;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+import android.graphics.SurfaceTexture;
+import android.media.MediaPlayer;
+import android.os.ConditionVariable;
+import android.opengl.Matrix;
+
+import java.io.IOException;
+import java.io.FileDescriptor;
+import java.lang.IllegalArgumentException;
+import java.util.List;
+import java.util.Set;
+
+import android.util.Log;
+
+/** <p>A filter that converts textures from a SurfaceTexture object into frames for
+ * processing in the filter framework.</p>
+ *
+ * <p>To use, connect up the sourceListener callback, and then when executing
+ * the graph, use the SurfaceTexture object passed to the callback to feed
+ * frames into the filter graph. For example, pass the SurfaceTexture into
+ * {#link
+ * android.hardware.Camera.setPreviewTexture(android.graphics.SurfaceTexture)}.
+ * This filter is intended for applications that need for flexibility than the
+ * CameraSource and MediaSource provide. Note that the application needs to
+ * provide width and height information for the SurfaceTextureSource, which it
+ * should obtain from wherever the SurfaceTexture data is coming from to avoid
+ * unnecessary resampling.</p>
+ *
+ * @hide
+ */
+public class SurfaceTextureSource extends Filter {
+
+ /** User-visible parameters */
+
+ /** The callback interface for the sourceListener parameter */
+ public interface SurfaceTextureSourceListener {
+ public void onSurfaceTextureSourceReady(SurfaceTexture source);
+ }
+ /** A callback to send the internal SurfaceTexture object to, once it is
+ * created. This callback will be called when the the filter graph is
+ * preparing to execute, but before any processing has actually taken
+ * place. The SurfaceTexture object passed to this callback is the only way
+ * to feed this filter. When the filter graph is shutting down, this
+ * callback will be called again with null as the source.
+ *
+ * This callback may be called from an arbitrary thread, so it should not
+ * assume it is running in the UI thread in particular.
+ */
+ @GenerateFinalPort(name = "sourceListener")
+ private SurfaceTextureSourceListener mSourceListener;
+
+ /** The width of the output image frame. If the texture width for the
+ * SurfaceTexture source is known, use it here to minimize resampling. */
+ @GenerateFieldPort(name = "width")
+ private int mWidth;
+
+ /** The height of the output image frame. If the texture height for the
+ * SurfaceTexture source is known, use it here to minimize resampling. */
+ @GenerateFieldPort(name = "height")
+ private int mHeight;
+
+ /** Whether the filter will always wait for a new frame from its
+ * SurfaceTexture, or whether it will output an old frame again if a new
+ * frame isn't available. The filter will always wait for the first frame,
+ * to avoid outputting a blank frame. Defaults to true.
+ */
+ @GenerateFieldPort(name = "waitForNewFrame", hasDefault = true)
+ private boolean mWaitForNewFrame = true;
+
+ /** Maximum timeout before signaling error when waiting for a new frame. Set
+ * this to zero to disable the timeout and wait indefinitely. In milliseconds.
+ */
+ @GenerateFieldPort(name = "waitTimeout", hasDefault = true)
+ private int mWaitTimeout = 1000;
+
+ /** Whether a timeout is an exception-causing failure, or just causes the
+ * filter to close.
+ */
+ @GenerateFieldPort(name = "closeOnTimeout", hasDefault = true)
+ private boolean mCloseOnTimeout = false;
+
+ // Variables for input->output conversion
+ private GLFrame mMediaFrame;
+ private ShaderProgram mFrameExtractor;
+ private SurfaceTexture mSurfaceTexture;
+ private MutableFrameFormat mOutputFormat;
+ private ConditionVariable mNewFrameAvailable;
+ private boolean mFirstFrame;
+
+ private float[] mFrameTransform;
+ private float[] mMappedCoords;
+ // These default source coordinates perform the necessary flip
+ // for converting from MFF/Bitmap origin to OpenGL origin.
+ private static final float[] mSourceCoords = { 0, 1, 0, 1,
+ 1, 1, 0, 1,
+ 0, 0, 0, 1,
+ 1, 0, 0, 1 };
+ // Shader for output
+ private final String mRenderShader =
+ "#extension GL_OES_EGL_image_external : require\n" +
+ "precision mediump float;\n" +
+ "uniform samplerExternalOES tex_sampler_0;\n" +
+ "varying vec2 v_texcoord;\n" +
+ "void main() {\n" +
+ " gl_FragColor = texture2D(tex_sampler_0, v_texcoord);\n" +
+ "}\n";
+
+ // Variables for logging
+
+ private static final String TAG = "SurfaceTextureSource";
+ private static final boolean mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+
+ public SurfaceTextureSource(String name) {
+ super(name);
+ mNewFrameAvailable = new ConditionVariable();
+ mFrameTransform = new float[16];
+ mMappedCoords = new float[16];
+ }
+
+ @Override
+ public void setupPorts() {
+ // Add input port
+ addOutputPort("video", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU));
+ }
+
+ private void createFormats() {
+ mOutputFormat = ImageFormat.create(mWidth, mHeight,
+ ImageFormat.COLORSPACE_RGBA,
+ FrameFormat.TARGET_GPU);
+ }
+
+ @Override
+ protected void prepare(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Preparing SurfaceTextureSource");
+
+ createFormats();
+
+ // Prepare input
+ mMediaFrame = (GLFrame)context.getFrameManager().newBoundFrame(mOutputFormat,
+ GLFrame.EXTERNAL_TEXTURE,
+ 0);
+
+ // Prepare output
+ mFrameExtractor = new ShaderProgram(context, mRenderShader);
+ }
+
+ @Override
+ public void open(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Opening SurfaceTextureSource");
+ // Create SurfaceTexture anew each time - it can use substantial memory.
+ mSurfaceTexture = new SurfaceTexture(mMediaFrame.getTextureId());
+ // Connect SurfaceTexture to callback
+ mSurfaceTexture.setOnFrameAvailableListener(onFrameAvailableListener);
+ // Connect SurfaceTexture to source
+ mSourceListener.onSurfaceTextureSourceReady(mSurfaceTexture);
+ mFirstFrame = true;
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Processing new frame");
+
+ // First, get new frame if available
+ if (mWaitForNewFrame || mFirstFrame) {
+ boolean gotNewFrame;
+ if (mWaitTimeout != 0) {
+ gotNewFrame = mNewFrameAvailable.block(mWaitTimeout);
+ if (!gotNewFrame) {
+ if (!mCloseOnTimeout) {
+ throw new RuntimeException("Timeout waiting for new frame");
+ } else {
+ if (mLogVerbose) Log.v(TAG, "Timeout waiting for a new frame. Closing.");
+ closeOutputPort("video");
+ return;
+ }
+ }
+ } else {
+ mNewFrameAvailable.block();
+ }
+ mNewFrameAvailable.close();
+ mFirstFrame = false;
+ }
+
+ mSurfaceTexture.updateTexImage();
+
+ mSurfaceTexture.getTransformMatrix(mFrameTransform);
+ Matrix.multiplyMM(mMappedCoords, 0,
+ mFrameTransform, 0,
+ mSourceCoords, 0);
+ mFrameExtractor.setSourceRegion(mMappedCoords[0], mMappedCoords[1],
+ mMappedCoords[4], mMappedCoords[5],
+ mMappedCoords[8], mMappedCoords[9],
+ mMappedCoords[12], mMappedCoords[13]);
+ // Next, render to output
+ Frame output = context.getFrameManager().newFrame(mOutputFormat);
+ mFrameExtractor.process(mMediaFrame, output);
+
+ output.setTimestamp(mSurfaceTexture.getTimestamp());
+
+ pushOutput("video", output);
+ output.release();
+ }
+
+ @Override
+ public void close(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "SurfaceTextureSource closed");
+ mSourceListener.onSurfaceTextureSourceReady(null);
+ mSurfaceTexture.release();
+ mSurfaceTexture = null;
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ if (mMediaFrame != null) {
+ mMediaFrame.release();
+ }
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (name.equals("width") || name.equals("height") ) {
+ mOutputFormat.setDimensions(mWidth, mHeight);
+ }
+ }
+
+ private SurfaceTexture.OnFrameAvailableListener onFrameAvailableListener =
+ new SurfaceTexture.OnFrameAvailableListener() {
+ public void onFrameAvailable(SurfaceTexture surfaceTexture) {
+ if (mLogVerbose) Log.v(TAG, "New frame from SurfaceTexture");
+ mNewFrameAvailable.open();
+ }
+ };
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureTarget.java b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureTarget.java
new file mode 100644
index 0000000..436caab
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureTarget.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.filterpacks.videosrc;
+
+import android.filterfw.core.Filter;
+import android.filterfw.core.FilterContext;
+import android.filterfw.core.FilterSurfaceView;
+import android.filterfw.core.Frame;
+import android.filterfw.core.FrameFormat;
+import android.filterfw.core.GenerateFieldPort;
+import android.filterfw.core.GenerateFinalPort;
+import android.filterfw.core.GLEnvironment;
+import android.filterfw.core.GLFrame;
+import android.filterfw.core.KeyValueMap;
+import android.filterfw.core.MutableFrameFormat;
+import android.filterfw.core.NativeProgram;
+import android.filterfw.core.NativeFrame;
+import android.filterfw.core.Program;
+import android.filterfw.core.ShaderProgram;
+import android.filterfw.format.ImageFormat;
+
+import android.filterfw.geometry.Quad;
+import android.filterfw.geometry.Point;
+
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+import android.graphics.Rect;
+import android.graphics.SurfaceTexture;
+
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class SurfaceTextureTarget extends Filter {
+
+ private final int RENDERMODE_STRETCH = 0;
+ private final int RENDERMODE_FIT = 1;
+ private final int RENDERMODE_FILL_CROP = 2;
+ private final int RENDERMODE_CUSTOMIZE = 3;
+
+ /** Required. Sets the destination surfaceTexture.
+ */
+ @GenerateFinalPort(name = "surfaceTexture")
+ private SurfaceTexture mSurfaceTexture;
+
+ /** Required. Sets the width of the output surfaceTexture images */
+ @GenerateFinalPort(name = "width")
+ private int mScreenWidth;
+
+ /** Required. Sets the height of the output surfaceTexture images */
+ @GenerateFinalPort(name = "height")
+ private int mScreenHeight;
+
+
+ /** Optional. Control how the incoming frames are rendered onto the
+ * output. Default is FIT.
+ * RENDERMODE_STRETCH: Just fill the output surfaceView.
+ * RENDERMODE_FIT: Keep aspect ratio and fit without cropping. May
+ * have black bars.
+ * RENDERMODE_FILL_CROP: Keep aspect ratio and fit without black
+ * bars. May crop.
+ */
+ @GenerateFieldPort(name = "renderMode", hasDefault = true)
+ private String mRenderModeString;
+
+ @GenerateFieldPort(name = "sourceQuad", hasDefault = true)
+ private Quad mSourceQuad = new Quad(new Point(0.0f, 1.0f),
+ new Point(1.0f, 1.0f),
+ new Point(0.0f, 0.0f),
+ new Point(1.0f, 0.0f));
+
+ @GenerateFieldPort(name = "targetQuad", hasDefault = true)
+ private Quad mTargetQuad = new Quad(new Point(0.0f, 0.0f),
+ new Point(1.0f, 0.0f),
+ new Point(0.0f, 1.0f),
+ new Point(1.0f, 1.0f));
+
+ private int mSurfaceId;
+
+ private ShaderProgram mProgram;
+ private GLFrame mScreen;
+ private int mRenderMode = RENDERMODE_FIT;
+ private float mAspectRatio = 1.f;
+
+ private boolean mLogVerbose;
+ private static final String TAG = "SurfaceTextureTarget";
+
+ public SurfaceTextureTarget(String name) {
+ super(name);
+
+ mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
+ }
+
+ @Override
+ public void setupPorts() {
+ // Make sure we have a SurfaceView
+ if (mSurfaceTexture == null) {
+ throw new RuntimeException("Null SurfaceTexture passed to SurfaceTextureTarget");
+ }
+
+ // Add input port - will accept anything that's 4-channel.
+ addMaskedInputPort("frame", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
+ }
+
+ public void updateRenderMode() {
+ if (mRenderModeString != null) {
+ if (mRenderModeString.equals("stretch")) {
+ mRenderMode = RENDERMODE_STRETCH;
+ } else if (mRenderModeString.equals("fit")) {
+ mRenderMode = RENDERMODE_FIT;
+ } else if (mRenderModeString.equals("fill_crop")) {
+ mRenderMode = RENDERMODE_FILL_CROP;
+ } else if (mRenderModeString.equals("customize")) {
+ mRenderMode = RENDERMODE_CUSTOMIZE;
+ } else {
+ throw new RuntimeException("Unknown render mode '" + mRenderModeString + "'!");
+ }
+ }
+ updateTargetRect();
+ }
+
+ @Override
+ public void prepare(FilterContext context) {
+ // Create identity shader to render, and make sure to render upside-down, as textures
+ // are stored internally bottom-to-top.
+ mProgram = ShaderProgram.createIdentity(context);
+ mProgram.setSourceRect(0, 1, 1, -1);
+ mProgram.setClearColor(0.0f, 0.0f, 0.0f);
+
+ updateRenderMode();
+
+ // Create a frame representing the screen
+ MutableFrameFormat screenFormat = new MutableFrameFormat(FrameFormat.TYPE_BYTE,
+ FrameFormat.TARGET_GPU);
+ screenFormat.setBytesPerSample(4);
+ screenFormat.setDimensions(mScreenWidth, mScreenHeight);
+ mScreen = (GLFrame)context.getFrameManager().newBoundFrame(screenFormat,
+ GLFrame.EXISTING_FBO_BINDING,
+ 0);
+ }
+
+ @Override
+ public void open(FilterContext context) {
+ // Set up SurfaceTexture internals
+ mSurfaceId = context.getGLEnvironment().registerSurfaceTexture(mSurfaceTexture, mScreenWidth, mScreenHeight);
+ }
+
+ @Override
+ public void process(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Starting frame processing");
+
+ GLEnvironment glEnv = context.getGLEnvironment();
+
+ // Get input frame
+ Frame input = pullInput("frame");
+ boolean createdFrame = false;
+
+ float currentAspectRatio = (float)input.getFormat().getWidth() / input.getFormat().getHeight();
+ if (currentAspectRatio != mAspectRatio) {
+ if (mLogVerbose) Log.v(TAG, "New aspect ratio: " + currentAspectRatio +", previously: " + mAspectRatio);
+ mAspectRatio = currentAspectRatio;
+ updateTargetRect();
+ }
+
+ // See if we need to copy to GPU
+ Frame gpuFrame = null;
+ if (mLogVerbose) Log.v("SurfaceTextureTarget", "Got input format: " + input.getFormat());
+
+ int target = input.getFormat().getTarget();
+ if (target != FrameFormat.TARGET_GPU) {
+ gpuFrame = context.getFrameManager().duplicateFrameToTarget(input,
+ FrameFormat.TARGET_GPU);
+ createdFrame = true;
+ } else {
+ gpuFrame = input;
+ }
+
+ // Activate our surface
+ glEnv.activateSurfaceWithId(mSurfaceId);
+
+ // Process
+ mProgram.process(gpuFrame, mScreen);
+
+ glEnv.setSurfaceTimestamp(input.getTimestamp());
+
+ // And swap buffers
+ glEnv.swapBuffers();
+
+ if (createdFrame) {
+ gpuFrame.release();
+ }
+ }
+
+ @Override
+ public void fieldPortValueUpdated(String name, FilterContext context) {
+ updateRenderMode();
+ }
+
+ @Override
+ public void tearDown(FilterContext context) {
+ if (mScreen != null) {
+ mScreen.release();
+ }
+ }
+
+ private void updateTargetRect() {
+ if (mScreenWidth > 0 && mScreenHeight > 0 && mProgram != null) {
+ float screenAspectRatio = (float)mScreenWidth / mScreenHeight;
+ float relativeAspectRatio = screenAspectRatio / mAspectRatio;
+
+ if (relativeAspectRatio == 1.0f && mRenderMode != RENDERMODE_CUSTOMIZE) {
+ mProgram.setClearsOutput(false);
+ } else {
+ switch (mRenderMode) {
+ case RENDERMODE_STRETCH:
+ mProgram.setTargetRect(0, 0, 1, 1);
+ mTargetQuad.p0.set(0f, 0.0f);
+ mTargetQuad.p1.set(1f, 0.0f);
+ mTargetQuad.p2.set(0f, 1.0f);
+ mTargetQuad.p3.set(1f, 1.0f);
+ mProgram.setClearsOutput(false);
+ break;
+ case RENDERMODE_FIT:
+ if (relativeAspectRatio > 1.0f) {
+ // Screen is wider than the camera, scale down X
+ mTargetQuad.p0.set(0.5f - 0.5f / relativeAspectRatio, 0.0f);
+ mTargetQuad.p1.set(0.5f + 0.5f / relativeAspectRatio, 0.0f);
+ mTargetQuad.p2.set(0.5f - 0.5f / relativeAspectRatio, 1.0f);
+ mTargetQuad.p3.set(0.5f + 0.5f / relativeAspectRatio, 1.0f);
+
+ } else {
+ // Screen is taller than the camera, scale down Y
+ mTargetQuad.p0.set(0.0f, 0.5f - 0.5f * relativeAspectRatio);
+ mTargetQuad.p1.set(1.0f, 0.5f - 0.5f * relativeAspectRatio);
+ mTargetQuad.p2.set(0.0f, 0.5f + 0.5f * relativeAspectRatio);
+ mTargetQuad.p3.set(1.0f, 0.5f + 0.5f * relativeAspectRatio);
+ }
+ mProgram.setClearsOutput(true);
+ break;
+ case RENDERMODE_FILL_CROP:
+ if (relativeAspectRatio > 1) {
+ // Screen is wider than the camera, crop in Y
+ mTargetQuad.p0.set(0.0f, 0.5f - 0.5f * relativeAspectRatio);
+ mTargetQuad.p1.set(1.0f, 0.5f - 0.5f * relativeAspectRatio);
+ mTargetQuad.p2.set(0.0f, 0.5f + 0.5f * relativeAspectRatio);
+ mTargetQuad.p3.set(1.0f, 0.5f + 0.5f * relativeAspectRatio);
+ } else {
+ // Screen is taller than the camera, crop in X
+ mTargetQuad.p0.set(0.5f - 0.5f / relativeAspectRatio, 0.0f);
+ mTargetQuad.p1.set(0.5f + 0.5f / relativeAspectRatio, 0.0f);
+ mTargetQuad.p2.set(0.5f - 0.5f / relativeAspectRatio, 1.0f);
+ mTargetQuad.p3.set(0.5f + 0.5f / relativeAspectRatio, 1.0f);
+ }
+ mProgram.setClearsOutput(true);
+ break;
+ case RENDERMODE_CUSTOMIZE:
+ ((ShaderProgram) mProgram).setSourceRegion(mSourceQuad);
+ break;
+ }
+ ((ShaderProgram) mProgram).setTargetRegion(mTargetQuad);
+ }
+ }
+ }
+}
diff --git a/media/mca/filterpacks/java/android/filterpacks/videosrc/package-info.java b/media/mca/filterpacks/java/android/filterpacks/videosrc/package-info.java
new file mode 100644
index 0000000..d8fd0bd
--- /dev/null
+++ b/media/mca/filterpacks/java/android/filterpacks/videosrc/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * @hide
+ */
+package android.filterpacks.videosrc;
diff --git a/media/mca/filterpacks/native/base/geometry.cpp b/media/mca/filterpacks/native/base/geometry.cpp
new file mode 100644
index 0000000..7812d50
--- /dev/null
+++ b/media/mca/filterpacks/native/base/geometry.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cutils/log.h>
+#include <cmath>
+
+#include "geometry.h"
+
+namespace android {
+namespace filterfw {
+
+float Point::Length() const {
+ return std::sqrt(x_ * x_ + y_ * y_);
+}
+
+bool Point::ScaleTo(float new_length) {
+ float length = Length();
+ if (length == 0.0f) {
+ return false;
+ }
+ x_ *= new_length / length;
+ y_ *= new_length / length;
+ return true;
+}
+
+float Point::Distance(const Point& p0, const Point& p1) {
+ Point diff = p1 - p0;
+ return diff.Length();
+}
+
+Point Point::operator+(const Point& other) const {
+ Point out;
+ out.x_ = x_ + other.x_;
+ out.y_ = y_ + other.y_;
+ return out;
+}
+
+Point Point::operator-(const Point& other) const {
+ Point out;
+ out.x_ = x_ - other.x_;
+ out.y_ = y_ - other.y_;
+ return out;
+}
+
+Point Point::operator*(float factor) const {
+ Point out;
+ out.x_ = factor * x_;
+ out.y_ = factor * y_;
+ return out;
+}
+
+void Point::Rotate90Clockwise() {
+ const float x = x_;
+ x_ = y_;
+ y_ = -x;
+}
+
+bool Rect::ExpandToAspectRatio(float ratio) {
+ if (width <= 0.0f || height <= 0.0f || ratio <= 0.0f) {
+ return false;
+ }
+
+ const float current_ratio = width / height;
+ if (current_ratio < ratio) {
+ const float dx = width * (ratio / current_ratio - 1.0f);
+ x -= dx / 2.0f;
+ width += dx;
+ } else {
+ const float dy = height * (current_ratio / ratio - 1.0f);
+ y -= dy / 2.0f;
+ height += dy;
+ }
+ return true;
+}
+
+bool Rect::ExpandToMinLength(float length) {
+ if (width <= 0.0f || height <= 0.0f || length <= 0.0f) {
+ return false;
+ }
+
+ const float current_length = width > height ? width : height;
+ if (length > current_length) {
+ const float dx = width * (length / current_length - 1.0f);
+ x -= dx / 2.0f;
+ width += dx;
+ const float dy = height * (length / current_length - 1.0f);
+ y -= dy / 2.0f;
+ height += dy;
+ }
+ return true;
+}
+
+bool Rect::ScaleWithLengthLimit(float factor, float max_length) {
+ if (width <= 0.0f || height <= 0.0f || factor <= 0.0f) {
+ return false;
+ }
+
+ const float current_length = width > height ? width : height;
+ if (current_length >= max_length) {
+ return true;
+ }
+
+ float f = factor;
+ if (current_length * f > max_length) {
+ f *= max_length / (current_length * f);
+ }
+
+ const float dx = width * (f - 1.0f);
+ x -= dx / 2.0f;
+ width += dx;
+ const float dy = height * (f - 1.0f);
+ y -= dy / 2.0f;
+ height += dy;
+ return true;
+}
+
+const Point& Quad::point(int ix) const {
+ ALOG_ASSERT(ix < static_cast<int>(points_.size()), "Access out of bounds");
+ return points_[ix];
+}
+
+bool SlantedRect::FromCenterAxisAndLengths(const Point& center,
+ const Point& vert_axis,
+ const Point& lengths) {
+ Point dy = vert_axis;
+ if (!dy.ScaleTo(lengths.y() / 2.0f)) {
+ ALOGE("Illegal axis: %f %f", vert_axis.x(), vert_axis.y());
+ return false;
+ }
+
+ Point dx = dy;
+ dx.Rotate90Clockwise();
+ dx.ScaleTo(lengths.x() / 2.0f);
+
+ points_[0] = center - dx - dy;
+ points_[1] = center + dx - dy;
+ points_[2] = center - dx + dy;
+ points_[3] = center + dx + dy;
+
+ width_ = lengths.x();
+ height_ = lengths.y();
+
+ return true;
+}
+
+} // namespace filterfw
+} // namespace android
diff --git a/media/mca/filterpacks/native/base/geometry.h b/media/mca/filterpacks/native/base/geometry.h
new file mode 100644
index 0000000..40a9343
--- /dev/null
+++ b/media/mca/filterpacks/native/base/geometry.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FILTERFW_FILTERPACKS_BASE_GEOMETRY_H
+#define ANDROID_FILTERFW_FILTERPACKS_BASE_GEOMETRY_H
+
+#include <vector>
+
+namespace android {
+namespace filterfw {
+
+// This is an initial implementation of some geometrical structures. This is
+// likely to grow and become more sophisticated in the future.
+
+class Point {
+ public:
+ Point() : x_(0.0f), y_(0.0f) {}
+ Point(float x, float y) : x_(x), y_(y) {}
+
+ float x() const { return x_; }
+ float y() const { return y_; }
+
+ float Length() const;
+ bool ScaleTo(float new_length);
+ static float Distance(const Point& p0, const Point& p1);
+
+ // Add more of these as needed:
+ Point operator+(const Point& other) const;
+ Point operator-(const Point& other) const;
+ Point operator*(float factor) const;
+
+ void Rotate90Clockwise();
+
+ private:
+ float x_, y_;
+};
+
+class Quad {
+ public:
+ Quad() : points_(4) {}
+ virtual ~Quad() {}
+
+ Quad(const Point& p0, const Point& p1, const Point& p2, const Point& p3)
+ : points_(4) {
+ points_[0] = p0;
+ points_[1] = p1;
+ points_[2] = p2;
+ points_[3] = p3;
+ }
+
+ const std::vector<Point>& points() const { return points_; }
+ const Point& point(int ix) const;
+
+ protected:
+ std::vector<Point> points_;
+};
+
+class SlantedRect : public Quad {
+ public:
+ SlantedRect() : width_(0.0f), height_(0.0f) {}
+ virtual ~SlantedRect() {}
+
+ bool FromCenterAxisAndLengths(const Point& center,
+ const Point& vert_axis,
+ const Point& lenghts);
+
+ float width() const { return width_; }
+ float height() const { return height_; }
+
+ private:
+ float width_;
+ float height_;
+};
+
+struct Rect {
+ float x, y, width, height;
+
+ Rect() {
+ x = y = 0.0f;
+ width = height = 1.0f;
+ }
+
+ Rect(float x, float y, float width, float height) {
+ this->x = x;
+ this->y = y;
+ this->width = width;
+ this->height = height;
+ }
+
+ bool ExpandToAspectRatio(float ratio);
+ bool ExpandToMinLength(float length);
+ bool ScaleWithLengthLimit(float factor, float max_length);
+};
+
+} // namespace filterfw
+} // namespace android
+
+#endif // ANDROID_FILTERFW_FILTERPACKS_BASE_GEOMETRY_H
diff --git a/media/mca/filterpacks/native/base/time_util.cpp b/media/mca/filterpacks/native/base/time_util.cpp
new file mode 100644
index 0000000..1a78a95
--- /dev/null
+++ b/media/mca/filterpacks/native/base/time_util.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "time_util.h"
+#include "utilities.h"
+
+#include <cutils/log.h>
+#include <sys/time.h>
+#include <map>
+
+namespace android {
+namespace filterfw {
+
+uint64_t getTimeUs() {
+ static long basesec;
+ struct timeval tv;
+ uint64_t nowtime;
+ gettimeofday(&tv, 0);
+ if (basesec == 0) {
+ basesec = tv.tv_sec;
+ }
+ nowtime = (uint64_t)(tv.tv_sec - basesec) * (uint64_t)1000000 +
+ (uint64_t)tv.tv_usec;
+ return nowtime;
+}
+
+const uint64_t NamedStopWatch::kDefaultLoggingPeriodInFrames = 100;
+
+NamedStopWatch::NamedStopWatch(const std::string& name)
+ : mName(name),
+ mLoggingPeriodInFrames(kDefaultLoggingPeriodInFrames),
+ mStartUSec(0),
+ mNumCalls(0),
+ mTotalUSec(0) {
+}
+
+void NamedStopWatch::Start() {
+ mStartUSec = getTimeUs();
+}
+
+void NamedStopWatch::Stop() {
+ if (!mStartUSec) {
+ return;
+ }
+ uint64_t stopUSec = getTimeUs();
+ if (stopUSec > mStartUSec) {
+ ++mNumCalls;
+ mTotalUSec += stopUSec - mStartUSec;
+ if (mNumCalls % mLoggingPeriodInFrames == 0) {
+ const float mSec = TotalUSec() * 1.0E-3f / NumCalls();
+ ALOGE("%s: %f ms", Name().c_str(), mSec);
+ }
+ }
+ mStartUSec = 0;
+}
+
+namespace {
+static NamedStopWatch* GetWatchForName(const std::string& watch_name) {
+ // TODO: this leaks the NamedStopWatch objects. Replace it with a
+ // singleton to avoid that and make it thread safe.
+ static std::map<std::string, NamedStopWatch*> watches;
+ NamedStopWatch* watch = FindPtrOrNull(watches, watch_name);
+ if (!watch) {
+ watch = new NamedStopWatch(watch_name);
+ watches[watch_name] = watch;
+ }
+ return watch;
+};
+} // namespace
+
+ScopedTimer::ScopedTimer(const std::string& stop_watch_name) {
+ mWatch = GetWatchForName(stop_watch_name);
+ mWatch->Start();
+}
+
+} // namespace filterfw
+} // namespace android
diff --git a/media/mca/filterpacks/native/base/time_util.h b/media/mca/filterpacks/native/base/time_util.h
new file mode 100644
index 0000000..60d76c6
--- /dev/null
+++ b/media/mca/filterpacks/native/base/time_util.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FILTERFW_FILTERPACKS_BASE_TIME_UTIL_H
+#define ANDROID_FILTERFW_FILTERPACKS_BASE_TIME_UTIL_H
+
+#include <string>
+#include <utils/RefBase.h>
+
+#define LOG_MFF_RUNNING_TIMES 0
+
+namespace android {
+namespace filterfw {
+
+uint64_t getTimeUs();
+
+class NamedStopWatch : public RefBase {
+ public:
+ static const uint64_t kDefaultLoggingPeriodInFrames;
+
+ explicit NamedStopWatch(const std::string& name);
+ void Start();
+ void Stop();
+
+ void SetName(const std::string& name) { mName = name; }
+ void SetLoggingPeriodInFrames(uint64_t numFrames) {
+ mLoggingPeriodInFrames = numFrames;
+ }
+
+ const std::string& Name() const { return mName; }
+ uint64_t NumCalls() const { return mNumCalls; }
+ uint64_t TotalUSec() const { return mTotalUSec; }
+
+ private:
+ std::string mName;
+ uint64_t mLoggingPeriodInFrames;
+ uint64_t mStartUSec;
+ uint64_t mNumCalls;
+ uint64_t mTotalUSec;
+};
+
+class ScopedTimer {
+ public:
+ explicit ScopedTimer(const std::string& stop_watch_name);
+ explicit ScopedTimer(NamedStopWatch* watch)
+ : mWatch(watch) { mWatch->Start(); }
+ ~ScopedTimer() { mWatch->Stop(); }
+
+ private:
+ NamedStopWatch* mWatch;
+};
+
+} // namespace filterfw
+} // namespace android
+
+#endif // ANDROID_FILTERFW_FILTERPACKS_BASE_TIME_UTIL_H
diff --git a/media/mca/filterpacks/native/base/utilities.h b/media/mca/filterpacks/native/base/utilities.h
new file mode 100644
index 0000000..302e177
--- /dev/null
+++ b/media/mca/filterpacks/native/base/utilities.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FILTERFW_FILTERPACKS_BASE_UTILITIES_H
+#define ANDROID_FILTERFW_FILTERPACKS_BASE_UTILITIES_H
+
+#include <set>
+#include <utility>
+
+namespace android {
+namespace filterfw {
+
+// Convenience Macro to make copy constructor and assignment operator private
+// (thereby disallowing copying and assigning).
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&); \
+ void operator=(const TypeName&)
+
+// A macro to disallow all the implicit constructors, namely the
+// default constructor, copy constructor and operator= functions.
+#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+ TypeName(); \
+ DISALLOW_COPY_AND_ASSIGN(TypeName)
+
+// STLDeleteContainerPointers()
+// For a range within a container of pointers, calls delete
+// (non-array version) on these pointers.
+// NOTE: for these three functions, we could just implement a DeleteObject
+// functor and then call for_each() on the range and functor, but this
+// requires us to pull in all of algorithm.h, which seems expensive.
+// For hash_[multi]set, it is important that this deletes behind the iterator
+// because the hash_set may call the hash function on the iterator when it is
+// advanced, which could result in the hash function trying to deference a
+// stale pointer.
+template <class ForwardIterator>
+void STLDeleteContainerPointers(ForwardIterator begin,
+ ForwardIterator end) {
+ while (begin != end) {
+ ForwardIterator temp = begin;
+ ++begin;
+ delete *temp;
+ }
+}
+
+// Given an STL container consisting of (key, value) pairs, STLDeleteValues
+// deletes all the "value" components and clears the container. Does nothing
+// in the case it's given a NULL pointer.
+template <class T>
+void STLDeleteValues(T *v) {
+ if (!v) return;
+ for (typename T::iterator i = v->begin(); i != v->end(); ++i) {
+ delete i->second;
+ }
+ v->clear();
+}
+
+// Perform a lookup in a map or hash_map.
+// If the key is present a const pointer to the associated value is returned,
+// otherwise a NULL pointer is returned.
+template <class Collection>
+const typename Collection::value_type::second_type*
+FindOrNull(const Collection& collection,
+ const typename Collection::value_type::first_type& key) {
+ typename Collection::const_iterator it = collection.find(key);
+ if (it == collection.end()) {
+ return 0;
+ }
+ return &it->second;
+}
+
+// A simple class that gives checklist functionality: There are essemtially two
+// operations defined on a CheckList:
+// - Adding a new (unchecked) item.
+// - Checking off an item.
+// When checking off the last remaining item CheckItem() returns true.
+template<typename T>
+class CheckList {
+ public:
+ // Add a new unchecked item. Does nothing if item is already in checklist.
+ void AddItem(const T& item);
+
+ // Check off an item in the checklist. Returns true if all items have been
+ // checked.
+ bool CheckItem(const T& item);
+
+ // Clear the checklist.
+ void Clear() {
+ items_.clear();
+ }
+
+ private:
+ std::set<T> items_;
+};
+
+template<typename T>
+void CheckList<T>::AddItem(const T& item) {
+ if (!ContainsKey(items_, item))
+ items_.insert(item);
+}
+
+template<typename T>
+bool CheckList<T>::CheckItem(const T& item) {
+ typename std::set<T>::iterator iter = items_.find(item);
+ if (iter != items_.end())
+ items_.erase(iter);
+ return items_.empty();
+}
+
+// Perform a lookup in a map or hash_map whose values are pointers.
+// If the key is present a const pointer to the associated value is returned,
+// otherwise a NULL pointer is returned.
+// This function does not distinguish between a missing key and a key mapped
+// to a NULL value.
+template <class Collection>
+const typename Collection::value_type::second_type
+FindPtrOrNull(const Collection& collection,
+ const typename Collection::value_type::first_type& key) {
+ typename Collection::const_iterator it = collection.find(key);
+ if (it == collection.end()) {
+ return 0;
+ }
+ return it->second;
+}
+
+// Test to see if a set, map, hash_set or hash_map contains a particular key.
+// Returns true if the key is in the collection.
+template <typename Collection, typename Key>
+bool ContainsKey(const Collection& collection, const Key& key) {
+ return collection.find(key) != collection.end();
+}
+
+// Insert a new key and value into a map or hash_map.
+// If the key is not present in the map the key and value are
+// inserted, otherwise nothing happens. True indicates that an insert
+// took place, false indicates the key was already present.
+template <class Collection, class Key, class Value>
+bool InsertIfNotPresent(Collection * const collection,
+ const Key& key, const Value& value) {
+ std::pair<typename Collection::iterator, bool> ret =
+ collection->insert(typename Collection::value_type(key, value));
+ return ret.second;
+}
+
+} // namespace filterfw
+} // namespace android
+
+#endif // ANDROID_FILTERFW_FILTERPACKS_BASE_UTILITIES_H
diff --git a/media/mca/filterpacks/native/base/vec_types.h b/media/mca/filterpacks/native/base/vec_types.h
new file mode 100644
index 0000000..65967c9
--- /dev/null
+++ b/media/mca/filterpacks/native/base/vec_types.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FILTERFW_FILTERPACKS_BASE_VEC_TYPES_H
+#define ANDROID_FILTERFW_FILTERPACKS_BASE_VEC_TYPES_H
+
+namespace android {
+namespace filterfw {
+
+template < class T, int dim>
+class VecBase {
+ public:
+ T data[dim];
+ VecBase() {}
+ VecBase<T,dim>& operator = (const VecBase<T, dim> &x) {
+ memcpy(data, x.data, sizeof(T)*dim);
+ return *this;
+ }
+ T & operator [] (int i) {
+ // out of boundary not checked
+ return data[i];
+ }
+ const T & operator [] (int i) const {
+ // out of boundary not checked
+ return data[i];
+ }
+ T Length() {
+ double sum = 0;
+ for (int i = 0; i < dim; ++i)
+ sum += static_cast<double> (data[i] * data[i]);
+ return static_cast<T>(sqrt(sum));
+ }
+};
+
+template < class T, int dim>
+class Vec : public VecBase<T,dim> {
+ public:
+ Vec() {}
+ Vec<T,dim>& operator = (const Vec<T, dim> &x) {
+ memcpy(this->data, x.data, sizeof(T)*dim);
+ return *this;
+ }
+};
+
+template <class T, int dim>
+Vec<T, dim> operator + (const Vec<T,dim> &x, const Vec<T,dim> &y) {
+ Vec<T, dim> out;
+ for (int i = 0; i < dim; i++)
+ out.data[i] = x.data[i] + y.data[i];
+ return out;
+}
+
+template <class T, int dim>
+Vec<T, dim> operator - (const Vec<T,dim> &x, const Vec<T,dim> &y) {
+ Vec<T, dim> out;
+ for (int i = 0; i < dim; i++)
+ out.data[i] = x.data[i] - y.data[i];
+ return out;
+}
+
+template <class T, int dim>
+Vec<T, dim> operator * (const Vec<T,dim> &x, const Vec<T,dim> &y) {
+ Vec<T, dim> out;
+ for (int i = 0; i < dim; i++)
+ out.data[i] = x.data[i] * y.data[i];
+ return out;
+}
+
+template <class T, int dim>
+Vec<T, dim> operator / (const Vec<T,dim> &x, const Vec<T,dim> &y) {
+ Vec<T, dim> out;
+ for (int i = 0; i < dim; i++)
+ out.data[i] = x.data[i] / y.data[i];
+ return out;
+}
+
+template <class T, int dim>
+T dot(const Vec<T,dim> &x, const Vec<T,dim> &y) {
+ T out = 0;
+ for (int i = 0; i < dim; i++)
+ out += x.data[i] * y.data[i];
+ return out;
+}
+
+template <class T, int dim>
+Vec<T, dim> operator * (const Vec<T,dim> &x, T scale) {
+ Vec<T, dim> out;
+ for (int i = 0; i < dim; i++)
+ out.data[i] = x.data[i] * scale;
+ return out;
+}
+
+template <class T, int dim>
+Vec<T, dim> operator / (const Vec<T,dim> &x, T scale) {
+ Vec<T, dim> out;
+ for (int i = 0; i < dim; i++)
+ out.data[i] = x.data[i] / scale;
+ return out;
+}
+
+template <class T, int dim>
+Vec<T, dim> operator + (const Vec<T,dim> &x, T val) {
+ Vec<T, dim> out;
+ for (int i = 0; i < dim; i++)
+ out.data[i] = x.data[i] + val;
+ return out;
+}
+
+// specialization for vec2, vec3, vec4 float
+template<>
+class Vec<float, 2> : public VecBase<float, 2> {
+public:
+ Vec() {}
+ Vec(float x, float y) {
+ data[0] = x;
+ data[1] = y;
+ }
+ Vec<float, 2>& operator = (const Vec<float, 2> &x) {
+ memcpy(data, x.data, sizeof(float)*2);
+ return *this;
+ }
+};
+
+template<>
+class Vec<float, 3> {
+public:
+ float data[3];
+ Vec() {}
+ Vec(float x, float y, float z) {
+ data[0] = x;
+ data[1] = y;
+ data[2] = z;
+ }
+ Vec<float, 3>& operator = (const Vec<float, 3> &x) {
+ memcpy(data, x.data, sizeof(float)*3);
+ return *this;
+ }
+};
+
+template<>
+class Vec<float, 4> {
+public:
+ float data[4];
+ Vec() {}
+ Vec(float x, float y, float z, float w) {
+ data[0] = x;
+ data[1] = y;
+ data[2] = z;
+ data[3] = w;
+ }
+ Vec<float, 4>& operator = (const Vec<float, 4> &x) {
+ memcpy(data, x.data, sizeof(float)*4);
+ return *this;
+ }
+};
+
+typedef Vec<float,2> Vec2f;
+typedef Vec<float,3> Vec3f;
+typedef Vec<float,4> Vec4f;
+
+} // namespace filterfw
+} // namespace android
+
+#endif // ANDROID_FILTERFW_FILTERPACKS_BASE_VEC_TYPES_H
diff --git a/media/mca/filterpacks/native/imageproc/brightness.c b/media/mca/filterpacks/native/imageproc/brightness.c
new file mode 100644
index 0000000..f4addf1
--- /dev/null
+++ b/media/mca/filterpacks/native/imageproc/brightness.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/log.h>
+#include <stdlib.h>
+
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "MCA", __VA_ARGS__)
+#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, "MCA", __VA_ARGS__)
+#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "MCA", __VA_ARGS__)
+
+typedef struct {
+ float brightness;
+} BrightnessParameters;
+
+typedef union {
+ int value;
+ char rgba[4];
+} Pixel;
+
+void brightness_init(void** user_data) {
+ (*user_data) = malloc(sizeof(BrightnessParameters));
+}
+
+void brightness_teardown(void* user_data) {
+ free(user_data);
+}
+
+void brightness_setvalue(const char* key, const char* value, void* user_data) {
+ if (strcmp(key, "brightness") == 0)
+ ((BrightnessParameters*)user_data)->brightness = atof(value);
+ else
+ LOGE("Unknown parameter: %s!", key);
+}
+
+int brightness_process(const char** inputs,
+ const int* input_sizes,
+ int input_count,
+ char* output,
+ int output_size,
+ void* user_data) {
+ // Make sure we have exactly one input
+ if (input_count != 1) {
+ LOGE("Brightness: Incorrect input count! Expected 1 but got %d!", input_count);
+ return 0;
+ }
+
+ // Make sure sizes match up
+ if (input_sizes[0] != output_size) {
+ LOGE("Brightness: Input-output sizes do not match up. %d vs. %d!", input_sizes[0], output_size);
+ return 0;
+ }
+
+ // Get the input and output pointers
+ const int* input_ptr = (int*)inputs[0];
+ int* output_ptr = (int*)output;
+ const int* end_ptr = input_ptr + (output_size / 4);
+ if (!input_ptr || !output_ptr) {
+ LOGE("Brightness: No input or output pointer found!");
+ return 0;
+ }
+
+ // Get the parameters
+ BrightnessParameters* params = (BrightnessParameters*)user_data;
+ const float brightness = params->brightness;
+
+ // Run the brightness adjustment
+ const int factor = (int)(brightness * 255.0f);
+ Pixel pixel;
+ while (input_ptr < end_ptr) {
+ pixel.value = *(input_ptr++);
+
+ const short r = (pixel.rgba[0] * factor) / 255;
+ const short g = (pixel.rgba[1] * factor) / 255;
+ const short b = (pixel.rgba[2] * factor) / 255;
+
+ *(output_ptr++) = (r > 255 ? 255 : r)
+ | ((g > 255 ? 255 : g) << 8)
+ | ((b > 255 ? 255 : b) << 16)
+ | (pixel.rgba[3] << 24);
+ }
+
+ return 1;
+}
+
diff --git a/media/mca/filterpacks/native/imageproc/contrast.c b/media/mca/filterpacks/native/imageproc/contrast.c
new file mode 100644
index 0000000..ea8c8d2
--- /dev/null
+++ b/media/mca/filterpacks/native/imageproc/contrast.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/log.h>
+#include <stdlib.h>
+
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "MCA", __VA_ARGS__)
+#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, "MCA", __VA_ARGS__)
+#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "MCA", __VA_ARGS__)
+
+typedef struct {
+ float contrast;
+} ContrastParameters;
+
+void contrast_init(void** user_data) {
+ (*user_data) = malloc(sizeof(ContrastParameters));
+}
+
+void contrast_teardown(void* user_data) {
+ free(user_data);
+}
+
+void contrast_setvalue(const char* key, const char* value, void* user_data) {
+ if (strcmp(key, "contrast") == 0)
+ ((ContrastParameters*)user_data)->contrast = atof(value);
+ else
+ LOGE("Unknown parameter: %s!", key);
+}
+
+int contrast_process(const char** inputs,
+ const int* input_sizes,
+ int input_count,
+ char* output,
+ int output_size,
+ void* user_data) {
+ // Make sure we have exactly one input
+ if (input_count != 1) {
+ LOGE("Contrast: Incorrect input count! Expected 1 but got %d!", input_count);
+ return 0;
+ }
+
+ // Make sure sizes match up
+ if (input_sizes[0] != output_size) {
+ LOGE("Contrast: Input-output sizes do not match up. %d vs. %d!", input_sizes[0], output_size);
+ return 0;
+ }
+
+ // Get the input and output pointers
+ const char* input_ptr = inputs[0];
+ char* output_ptr = output;
+ if (!input_ptr || !output_ptr) {
+ LOGE("Contrast: No input or output pointer found!");
+ return 0;
+ }
+
+ // Get the parameters
+ ContrastParameters* params = (ContrastParameters*)user_data;
+ const float contrast = params->contrast;
+
+ // Run the contrast adjustment
+ int i;
+ for (i = 0; i < output_size; ++i) {
+ float px = *(input_ptr++) / 255.0;
+ px -= 0.5;
+ px *= contrast;
+ px += 0.5;
+ *(output_ptr++) = (char)(px > 1.0 ? 255.0 : (px < 0.0 ? 0.0 : px * 255.0));
+ }
+
+ return 1;
+}
+
diff --git a/media/mca/filterpacks/native/imageproc/invert.c b/media/mca/filterpacks/native/imageproc/invert.c
new file mode 100644
index 0000000..5938aac
--- /dev/null
+++ b/media/mca/filterpacks/native/imageproc/invert.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/log.h>
+
+int invert_process(const char** inputs,
+ const int* input_sizes,
+ int input_count,
+ char* output,
+ int output_size,
+ void* user_data) {
+ // Make sure we have exactly one input
+ if (input_count != 1)
+ return 0;
+
+ // Make sure sizes match up
+ if (input_sizes[0] != output_size)
+ return 0;
+
+ // Get the input and output pointers
+ const char* input_ptr = inputs[0];
+ char* output_ptr = output;
+ if (!input_ptr || !output_ptr)
+ return 0;
+
+ // Run the inversion
+ int i;
+ for (i = 0; i < output_size; ++i)
+ *(output_ptr++) = 255 - *(input_ptr++);
+
+ return 1;
+}
+
diff --git a/media/mca/filterpacks/native/imageproc/to_rgba.c b/media/mca/filterpacks/native/imageproc/to_rgba.c
new file mode 100644
index 0000000..bf4db2a
--- /dev/null
+++ b/media/mca/filterpacks/native/imageproc/to_rgba.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+
+int gray_to_rgb_process(const char** inputs,
+ const int* input_sizes,
+ int input_count,
+ char* output,
+ int output_size,
+ void* user_data) {
+ // Make sure we have exactly one input
+ if (input_count != 1)
+ return 0;
+
+ // Make sure sizes match up
+ if (input_sizes[0] != output_size/3)
+ return 0;
+
+ // Get the input and output pointers
+ const char* input_ptr = inputs[0];
+ char* output_ptr = output;
+ if (!input_ptr || !output_ptr)
+ return 0;
+
+ // Run the conversion
+ int i;
+ for (i = 0; i < input_sizes[0]; ++i) {
+ *(output_ptr++) = *(input_ptr);
+ *(output_ptr++) = *(input_ptr);
+ *(output_ptr++) = *(input_ptr++);
+ }
+
+ return 1;
+}
+
+int rgba_to_rgb_process(const char** inputs,
+ const int* input_sizes,
+ int input_count,
+ char* output,
+ int output_size,
+ void* user_data) {
+ // Make sure we have exactly one input
+ if (input_count != 1)
+ return 0;
+
+ // Make sure sizes match up
+ if (input_sizes[0]/4 != output_size/3)
+ return 0;
+
+ // Get the input and output pointers
+ const char* input_ptr = inputs[0];
+ char* output_ptr = output;
+ if (!input_ptr || !output_ptr)
+ return 0;
+
+ // Run the conversion
+ int i;
+ for (i = 0; i < input_sizes[0] / 4; ++i) {
+ *(output_ptr++) = *(input_ptr++);
+ *(output_ptr++) = *(input_ptr++);
+ *(output_ptr++) = *(input_ptr++);
+ ++input_ptr;
+ }
+
+ return 1;
+}
+
+int gray_to_rgba_process(const char** inputs,
+ const int* input_sizes,
+ int input_count,
+ char* output,
+ int output_size,
+ void* user_data) {
+ // Make sure we have exactly one input
+ if (input_count != 1)
+ return 0;
+
+ // Make sure sizes match up
+ if (input_sizes[0] != output_size/4)
+ return 0;
+
+ // Get the input and output pointers
+ const char* input_ptr = inputs[0];
+ char* output_ptr = output;
+ if (!input_ptr || !output_ptr)
+ return 0;
+
+ // Run the conversion
+ int i;
+ for (i = 0; i < input_sizes[0]; ++i) {
+ *(output_ptr++) = *(input_ptr);
+ *(output_ptr++) = *(input_ptr);
+ *(output_ptr++) = *(input_ptr++);
+ *(output_ptr++) = 255;
+ }
+
+ return 1;
+}
+
+int rgb_to_rgba_process(const char** inputs,
+ const int* input_sizes,
+ int input_count,
+ char* output,
+ int output_size,
+ void* user_data) {
+ // Make sure we have exactly one input
+ if (input_count != 1)
+ return 0;
+
+ // Make sure sizes match up
+ if (input_sizes[0]/3 != output_size/4)
+ return 0;
+
+ // Get the input and output pointers
+ const char* input_ptr = inputs[0];
+ char* output_ptr = output;
+ if (!input_ptr || !output_ptr)
+ return 0;
+
+ // Run the conversion
+ int i;
+ for (i = 0; i < output_size / 4; ++i) {
+ *(output_ptr++) = *(input_ptr++);
+ *(output_ptr++) = *(input_ptr++);
+ *(output_ptr++) = *(input_ptr++);
+ *(output_ptr++) = 255;
+ }
+
+ return 1;
+}
+
diff --git a/media/mca/samples/Android.mk b/media/mca/samples/Android.mk
new file mode 100644
index 0000000..b1ce91e
--- /dev/null
+++ b/media/mca/samples/Android.mk
@@ -0,0 +1,21 @@
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# Build all native libraries
+#
+include $(call all-subdir-makefiles)
+
+
diff --git a/media/mca/samples/CameraEffectsRecordingSample/Android.mk b/media/mca/samples/CameraEffectsRecordingSample/Android.mk
new file mode 100644
index 0000000..d3c4336
--- /dev/null
+++ b/media/mca/samples/CameraEffectsRecordingSample/Android.mk
@@ -0,0 +1,34 @@
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Build activity
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := CameraEffectsRecordingSample
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_PACKAGE)
+
+# ============================================================
+
+# Also build all of the sub-targets under this one: the shared library.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/media/mca/samples/CameraEffectsRecordingSample/AndroidManifest.xml b/media/mca/samples/CameraEffectsRecordingSample/AndroidManifest.xml
new file mode 100644
index 0000000..a65129d
--- /dev/null
+++ b/media/mca/samples/CameraEffectsRecordingSample/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ android:versionCode="1"
+ android:versionName="1.0" package="android.media.filterfw.samples">
+ <uses-sdk android:minSdkVersion="3" />
+ <uses-permission android:name="android.permission.CAMERA" />
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-feature android:name="android.hardware.camera" />
+ <uses-feature android:name="android.hardware.camera.autofocus" />
+ <application android:label="@string/app_name"
+ android:debuggable="true">
+ <activity android:name=".CameraEffectsRecordingSample"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/media/mca/samples/CameraEffectsRecordingSample/java/android/media/filterfw/samples/CameraEffectsRecordingSample.java b/media/mca/samples/CameraEffectsRecordingSample/java/android/media/filterfw/samples/CameraEffectsRecordingSample.java
new file mode 100644
index 0000000..c0c3034
--- /dev/null
+++ b/media/mca/samples/CameraEffectsRecordingSample/java/android/media/filterfw/samples/CameraEffectsRecordingSample.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media.filterfw.samples;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Environment;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.SurfaceView;
+import android.view.KeyEvent;
+import android.widget.Button;
+import android.filterfw.GraphEnvironment;
+import android.filterfw.core.GraphRunner;
+import android.filterpacks.videosink.MediaEncoderFilter;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.content.Intent;
+
+public class CameraEffectsRecordingSample extends Activity {
+
+ private Button mRunButton;
+ private SurfaceView mCameraView;
+
+ private GraphRunner mRunner;
+ private int mCameraId = 0;
+ private String mOutFileName = Environment.getExternalStorageDirectory().toString() +
+ "/CameraEffectsRecordingSample.mp4";
+
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+ mRunButton = (Button) findViewById(R.id.runbutton);
+ mCameraView = (SurfaceView) findViewById(R.id.cameraview);
+ mRunButton.setOnClickListener(mRunButtonClick);
+
+ Intent intent = getIntent();
+ if (intent.hasExtra("OUTPUT_FILENAME")) {
+ mOutFileName = intent.getStringExtra("OUTPUT_FILENAME");
+ }
+ // Set up the references and load the filter graph
+ createGraph();
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_CAMERA:
+ mRunButton.performClick();
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ private void createGraph() {
+ Bitmap sourceBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.android);
+ GraphEnvironment graphEnvironment = new GraphEnvironment();
+ graphEnvironment.createGLEnvironment();
+ graphEnvironment.addReferences("cameraView", mCameraView);
+ graphEnvironment.addReferences("cameraId", mCameraId);
+ graphEnvironment.addReferences("outputFileName", mOutFileName);
+ int graphId = graphEnvironment.loadGraph(this, R.raw.cameraeffectsrecordingsample);
+ mRunner = graphEnvironment.getRunner(graphId, GraphEnvironment.MODE_ASYNCHRONOUS);
+ }
+
+ protected void onPause() {
+ super.onPause();
+ if (mRunner.isRunning()) {
+ mRunner.stop();
+ mRunButton.setText("Record");
+ }
+ }
+
+ private OnClickListener mRunButtonClick = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mRunner.isRunning()) {
+ mRunner.stop();
+ mRunButton.setText("Record");
+ } else {
+ mRunner.run();
+ mRunButton.setText("Stop");
+ }
+ }
+ };
+}
diff --git a/media/mca/samples/CameraEffectsRecordingSample/res/drawable/android.jpg b/media/mca/samples/CameraEffectsRecordingSample/res/drawable/android.jpg
new file mode 100644
index 0000000..7c26a8d
--- /dev/null
+++ b/media/mca/samples/CameraEffectsRecordingSample/res/drawable/android.jpg
Binary files differ
diff --git a/media/mca/samples/CameraEffectsRecordingSample/res/layout/main.xml b/media/mca/samples/CameraEffectsRecordingSample/res/layout/main.xml
new file mode 100644
index 0000000..0cc07ce
--- /dev/null
+++ b/media/mca/samples/CameraEffectsRecordingSample/res/layout/main.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <Button
+ android:id="@+id/runbutton"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Record" />
+ <android.filterfw.core.FilterSurfaceView
+ android:id="@+id/cameraview"
+ android:layout_height="fill_parent"
+ android:layout_width="fill_parent"
+ android:layout_weight="1" />
+</LinearLayout>
diff --git a/media/mca/samples/CameraEffectsRecordingSample/res/raw/cameraeffectsrecordingsample.graph b/media/mca/samples/CameraEffectsRecordingSample/res/raw/cameraeffectsrecordingsample.graph
new file mode 100644
index 0000000..f3ed667
--- /dev/null
+++ b/media/mca/samples/CameraEffectsRecordingSample/res/raw/cameraeffectsrecordingsample.graph
@@ -0,0 +1,57 @@
+// Copyright (C) 2011 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// A simple graph that displays the raw camera stream.
+
+@setting autoBranch = "synced";
+
+// Imports ---------------------------------------------------
+@import android.filterpacks.videosrc;
+@import android.filterpacks.ui;
+@import android.filterpacks.videosink;
+@import android.filterpacks.base;
+
+// Externals -------------------------------------------------
+@external cameraView;
+@external outputFileName;
+@external cameraId;
+
+@set widthLoc = 320;
+@set heightLoc = 240;
+
+// Filters ---------------------------------------------------
+@filter CameraSource camera {
+ id = $cameraId;
+ width = $widthLoc;
+ height = $heightLoc;
+ waitForNewFrame = false;
+}
+
+@filter MediaEncoderFilter mediaEncoder {
+ width = $widthLoc;
+ height = $heightLoc;
+ outputFile = $outputFileName;
+}
+
+@filter SurfaceRenderFilter renderer {
+ surfaceView = $cameraView;
+}
+
+
+// Connections -----------------------------------------------
+// Camera->Preview, Camera->Encoder
+// Render camera output on to a surface
+@connect camera[video] => renderer[frame];
+// Also pass it to an encoder
+@connect camera[video] => mediaEncoder[videoframe];
diff --git a/media/mca/samples/CameraEffectsRecordingSample/res/values/strings.xml b/media/mca/samples/CameraEffectsRecordingSample/res/values/strings.xml
new file mode 100644
index 0000000..6491043
--- /dev/null
+++ b/media/mca/samples/CameraEffectsRecordingSample/res/values/strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ -->
+<resources>
+ <string name="app_name">CameraEffectsRecordingSample</string>
+</resources>
diff --git a/media/mca/structgen.py b/media/mca/structgen.py
new file mode 100644
index 0000000..437326c
--- /dev/null
+++ b/media/mca/structgen.py
@@ -0,0 +1,367 @@
+#!/usr/bin/env python
+
+#
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import os
+import sys
+
+hFileTemplate = """/**
+ * This file is auto-generated by platform/system/media/mca/structgen.py! Do NOT modify!
+ **/
+
+#ifndef %s
+#define %s
+
+%s
+
+#endif // %s
+"""
+
+jniFileTemplate = """/**
+ * This file is auto-generated by platform/system/media/mca/structgen.py! Do NOT modify!
+ **/
+
+#include <stdint.h>
+#include "native/%s.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "jni.h"
+
+// Helper functions ////////////////////////////////////////////////////////////////////////////////
+%s* Get%sAtIndex(JNIEnv* env, jobject buffer, int index) {
+ jclass base_class = (*env)->FindClass(env, "android/filterfw/core/NativeBuffer");
+ jfieldID ptr_field = (*env)->GetFieldID(env, base_class, "mDataPointer", "J");
+ uintptr_t data_ptr = (*env)->GetLongField(env, buffer, ptr_field);
+ %s* array = (%s*)data_ptr;
+ (*env)->DeleteLocalRef(env, base_class);
+ return &array[index];
+}
+
+// Declarations ////////////////////////////////////////////////////////////////////////////////////
+JNIEXPORT jint JNICALL
+Java_%s_getElementSize(JNIEnv* env, jobject thiz);
+
+%s
+
+#ifdef __cplusplus
+}
+#endif
+
+// Implementation //////////////////////////////////////////////////////////////////////////////////
+jint Java_%s_getElementSize(JNIEnv* env, jobject thiz) {
+ return sizeof(%s);
+}
+
+%s
+"""
+
+javaFileTemplate = """/**
+ * This file is auto-generated by platform/system/media/mca/structgen.py! Do NOT modify!
+ **/
+
+package %s;
+
+import android.filterfw.core.NativeBuffer;
+
+%s
+"""
+
+
+def ToJavaName(cname, start_upper_at = 1):
+ lower = cname.split("_")
+ upper = [c.title() for c in lower]
+ return "".join(lower[:start_upper_at] + upper[start_upper_at:])
+
+def ToJNIPackage(package, jclassname):
+ return "%s_%s" % (package.replace(".", "_"), jclassname)
+
+def ToMacroDefName(cname, pname):
+ return "%s_%s" % (pname.replace(".", "_").upper(), cname.upper())
+
+class ParseError:
+ def __init__(self, lineno, message):
+ self.lineno = lineno
+ self.message = message
+
+ def __str__(self):
+ return "On line %d: %s" % (self.lineno, self.message)
+
+class FieldType_BasePOD:
+ def __init__(self, name, structname, jclassname, package, javatype, ctype, jtype, defval):
+ self.name = name
+ self.structname = structname
+ self.jclassname = jclassname
+ self.package = package
+ self.javatype = javatype
+ self.ctype = ctype
+ self.jtype = jtype
+ self.defval = defval
+
+ def cString(self):
+ return " %s %s;" % (self.ctype, self.name)
+
+ def javaGetter(self):
+ return " public %s get%s(int index) {\n"\
+ " assertReadable();\n"\
+ " return nativeGet%s(index);\n"\
+ " }" % (self.javatype, ToJavaName(self.name, 0), ToJavaName(self.name, 0))
+
+ def javaSetter(self):
+ return " public void set%s(int index, %s value) {\n"\
+ " assertWritable();\n"\
+ " nativeSet%s(index, value);\n"\
+ " }" % (ToJavaName(self.name, 0), self.javatype, ToJavaName(self.name, 0))
+
+ def javaNativeGetter(self):
+ return " private native %s nativeGet%s(int index);"\
+ % (self.javatype, ToJavaName(self.name, 0))
+
+ def javaNativeSetter(self):
+ return " private native boolean nativeSet%s(int index, %s value);"\
+ % (ToJavaName(self.name, 0), self.javatype)
+
+ def jniGetterDefString(self):
+ return "JNIEXPORT %s JNICALL\n" \
+ "Java_%s_nativeGet%s(JNIEnv* env, jobject thiz, jint index);" \
+ % (self.jtype, ToJNIPackage(self.package, self.jclassname), ToJavaName(self.name, 0))
+
+ def jniGetterImplString(self):
+ return \
+ "%s Java_%s_nativeGet%s(JNIEnv* env, jobject thiz, jint index) {\n"\
+ " %s* instance = Get%sAtIndex(env, thiz, index);\n"\
+ " return instance ? instance->%s : %s;\n"\
+ "}\n" % (self.jtype, ToJNIPackage(self.package, self.jclassname), ToJavaName(self.name, 0),\
+ self.structname, self.structname, self.name, self.defval)
+
+ def jniSetterDefString(self):
+ return "JNIEXPORT jboolean JNICALL\n" \
+ "Java_%s_nativeSet%s(JNIEnv* env, jobject thiz, jint index, %s value);" \
+ % (ToJNIPackage(self.package, self.jclassname), ToJavaName(self.name, 0), self.jtype)
+
+ def jniSetterImplString(self):
+ return \
+ "jboolean Java_%s_nativeSet%s(JNIEnv* env, jobject thiz, jint index, %s value) {\n"\
+ " %s* instance = Get%sAtIndex(env, thiz, index);\n"\
+ " if (instance) {\n"\
+ " instance->%s = value;\n"\
+ " return JNI_TRUE;\n"\
+ " }\n"\
+ " return JNI_FALSE;\n"\
+ "}\n" % (ToJNIPackage(self.package, self.jclassname), ToJavaName(self.name, 0),\
+ self.jtype, self.structname, self.structname, self.name)
+
+class FieldType_Float(FieldType_BasePOD):
+ def __init__(self, name, structname, jclassname, package):
+ FieldType_BasePOD.__init__(self, name, structname, jclassname, package, "float", "float", "jfloat", "0.0")
+
+class FieldType_Int(FieldType_BasePOD):
+ def __init__(self, name, structname, jclassname, package):
+ FieldType_BasePOD.__init__(self, name, structname, jclassname, package, "int", "int", "jint", "0")
+
+class FieldType_Long(FieldType_BasePOD):
+ def __init__(self, name, structname, jclassname, package):
+ FieldType_BasePOD.__init__(self, name, structname, jclassname, package, "long", "long long", "jlong", "0")
+
+class StructSpec:
+
+ def parseTextFile(self, filepath):
+ # Init
+ self.name = None
+ self.package = None
+ self.fields = []
+ self.structname = None
+ self.jclassname = None
+ self.libname = None
+
+ # Open the file
+ txtfile = open(filepath)
+
+ # Parse it line by line
+ lineno = 0
+ for line in txtfile:
+ # Split line into components
+ linecomps = line.split()
+ if len(linecomps) == 0:
+ continue
+
+ # Execute command
+ cmd = linecomps[0]
+ if cmd == "@name":
+ self.commandArgAssert(linecomps, 1, lineno)
+ self.name = linecomps[1]
+ if not self.structname:
+ self.structname = self.name
+ if not self.jclassname:
+ self.jclassname = self.name
+ elif cmd == "@package":
+ self.commandArgAssert(linecomps, 1, lineno)
+ self.package = linecomps[1]
+ elif cmd == "@libname":
+ self.commandArgAssert(linecomps, 1, lineno)
+ self.libname = linecomps[1]
+ elif cmd == "@structname":
+ self.commandArgAssert(linecomps, 1, lineno)
+ self.structname = linecomps[1]
+ elif cmd == "@javaclassname":
+ self.commandArgAssert(linecomps, 1, lineno)
+ self.jclassname = linecomps[1]
+ elif cmd == "@field":
+ self.commandArgAssert(linecomps, 2, lineno)
+ typestr = linecomps[1]
+ if typestr == "int":
+ fieldtype = FieldType_Int(linecomps[2], self.structname, self.jclassname, self.package)
+ elif typestr == "long":
+ fieldtype = FieldType_Long(linecomps[2], self.structname, self.jclassname, self.package)
+ elif typestr == "float":
+ fieldtype = FieldType_Float(linecomps[2], self.structname, self.jclassname, self.package)
+ else:
+ raise ParseError(lineno, "Unknown field type '%s'!" % typestr)
+ self.fields.append(fieldtype)
+ else:
+ raise ParseError(lineno, "Unknown command: '%s'!" % cmd)
+
+ lineno = lineno + 1
+
+ # Make sure we have all required info
+ if not self.name:
+ raise ParseError(lineno, "Required field '@name' missing!")
+ elif not self.package:
+ raise ParseError(lineno, "Required field '@package' missing!")
+ elif not self.libname:
+ raise ParseError(lineno, "Required field '@libname' missing!")
+
+ # Normalize values
+ if self.libname[:3] == "lib":
+ self.libname = self.libname[3:]
+
+ def commandArgAssert(self, linecomps, expectedcount, lineno):
+ foundcount = len(linecomps) - 1
+ if foundcount < expectedcount:
+ raise ParseError(lineno, "Not enough arguments specifed for command '%s'! Expected %d, " \
+ "but got only %d!" % (linecomps[0], expectedcount, foundcount))
+ elif foundcount > expectedcount + 1:
+ raise ParseError(lineno, "Too many arguments specifed for command '%s'! Expected %d, " \
+ "but got %d!" % (linecomps[0], expectedcount, foundcount))
+
+
+ def cStructString(self):
+ cfields = [f.cString() for f in self.fields]
+ return "typedef struct Struct%s {\n%s\n} %s;\n" % (self.structname,\
+ "\n".join(cfields),\
+ self.structname)
+
+ def javaClassString(self):
+ jgetters = [f.javaGetter() for f in self.fields]
+ jsetters = [f.javaSetter() for f in self.fields]
+ jnativesetters = [f.javaNativeSetter() for f in self.fields]
+ jnativegetters = [f.javaNativeGetter() for f in self.fields]
+ return "public class %s extends NativeBuffer {\n\n"\
+ " public %s() {\n"\
+ " super();\n"\
+ " }\n"\
+ "\n"\
+ " public %s(int count) {\n"\
+ " super(count);\n"\
+ " }\n"\
+ "\n"\
+ " public native int getElementSize();\n"\
+ "\n"\
+ "%s\n\n"\
+ "%s\n\n"\
+ "%s\n\n"\
+ "%s\n\n"\
+ " static {\n"\
+ " System.loadLibrary(\"%s\");\n"\
+ " }\n"\
+ "\n"\
+ "};\n" % (self.jclassname,\
+ self.jclassname,\
+ self.jclassname,\
+ "\n\n".join(jgetters),\
+ "\n\n".join(jsetters),\
+ "\n\n".join(jnativegetters),\
+ "\n\n".join(jnativesetters),\
+ self.libname)
+
+ def jniDeclString(self):
+ jnigetters = [f.jniGetterDefString() for f in self.fields]
+ jnisetters = [f.jniSetterDefString() for f in self.fields]
+ return "\n\n".join(jnigetters + jnisetters)
+
+ def jniImplString(self):
+ jnigetters = [f.jniGetterImplString() for f in self.fields]
+ jnisetters = [f.jniSetterImplString() for f in self.fields]
+ return "\n\n".join(jnigetters + jnisetters)
+
+ def hFileString(self):
+ defname = ToMacroDefName(self.structname, self.package)
+ return hFileTemplate % (defname, defname, self.cStructString(), defname)
+
+ def javaFileString(self):
+ return javaFileTemplate % (self.package, self.javaClassString())
+
+ def jniFileString(self):
+ return jniFileTemplate % (self.structname.lower(),\
+ self.structname,\
+ self.structname,\
+ self.structname,\
+ self.structname,\
+ ToJNIPackage(self.package, self.jclassname),\
+ self.jniDeclString(),\
+ ToJNIPackage(self.package, self.jclassname),\
+ self.structname,
+ self.jniImplString())
+
+def main(argv):
+ if len(argv) != 2:
+ print("Usage: %s <file.struct>" % argv[0])
+ return -1
+
+ filepath = argv[1]
+
+ structspec = StructSpec()
+ structspec.parseTextFile(filepath)
+
+ hfilename = "%s.h" % structspec.structname.lower()
+ javafilename = "%s.java" % structspec.jclassname
+ jnifilename = "jni_%s.c" % structspec.structname.lower()
+
+ javapackagepath = structspec.package.replace('.','/')
+
+ rootdir = os.path.dirname(filepath)
+ hfilepath = "%s/../native/%s" % (rootdir, hfilename)
+ javafilepath = "%s/../java/%s/%s" % (rootdir, javapackagepath, javafilename)
+ jnifilepath = "%s/../jni/%s" % (rootdir, jnifilename)
+
+ hfile = open(hfilepath, 'w')
+ hfile.write(structspec.hFileString())
+ hfile.close()
+
+ javafile = open(javafilepath, 'w')
+ javafile.write(structspec.javaFileString())
+ javafile.close()
+
+ jnifile = open(jnifilepath, 'w')
+ jnifile.write(structspec.jniFileString())
+ jnifile.close()
+
+
+if __name__ == "__main__":
+ sys.exit(main(sys.argv))
diff --git a/media/mca/tests/Android.mk b/media/mca/tests/Android.mk
new file mode 100644
index 0000000..2abd7f6
--- /dev/null
+++ b/media/mca/tests/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+# Include all test java files.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CameraEffectsTests
+
+LOCAL_INSTRUMENTATION_FOR := CameraEffectsRecordingSample
+
+include $(BUILD_PACKAGE)
+
+
diff --git a/data/etc/android.hardware.faketouch.multitouch.jazzhand.xml b/media/mca/tests/AndroidManifest.xml
index 7f0e70b..5133640 100644
--- a/data/etc/android.hardware.faketouch.multitouch.jazzhand.xml
+++ b/media/mca/tests/AndroidManifest.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
+<!-- Copyright (C) 2008 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.
@@ -14,11 +14,16 @@
limitations under the License.
-->
-<!-- This is the standard set of features for an indirect touch input device that supports
- independently-trackable multiple-finger multitouch. -->
-<permissions>
- <feature name="android.hardware.faketouch.multitouch" />
- <feature name="android.hardware.faketouch.multitouch.distinct" />
- <feature name="android.hardware.faketouch.multitouch.jazzhand" />
- <feature name="android.hardware.faketouch" />
-</permissions>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.camera.mediaeffects.tests">
+
+ <uses-permission android:name="android.permission.INJECT_EVENTS" />
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="android.media.filterfw.samples"
+ android:label="Tests for Camera Effects Recording."/>
+</manifest>
diff --git a/media/mca/tests/src/android/camera/mediaeffects/tests/functional/EffectsVideoCapture.java b/media/mca/tests/src/android/camera/mediaeffects/tests/functional/EffectsVideoCapture.java
new file mode 100644
index 0000000..474b00f
--- /dev/null
+++ b/media/mca/tests/src/android/camera/mediaeffects/tests/functional/EffectsVideoCapture.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.camera.mediaeffects.tests.functional;
+
+import android.media.filterfw.samples.CameraEffectsRecordingSample;
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.Intent;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.view.KeyEvent;
+import android.util.Log;
+import android.content.Intent;
+import android.os.Environment;
+import android.media.MediaMetadataRetriever;
+import android.net.Uri;
+import java.io.File;
+
+public class EffectsVideoCapture extends ActivityInstrumentationTestCase2
+ <CameraEffectsRecordingSample> {
+ private static final String TAG = "EffectsVideoCaptureTest";
+ private static final long WAIT_FOR_PREVIEW = 4 * 1000; // 4 seconds
+
+ public EffectsVideoCapture() {
+ super(CameraEffectsRecordingSample.class);
+ }
+
+ private void captureVideos(String reportTag, Instrumentation inst) throws Exception{
+ int total_num_of_videos = 1;
+ int video_duration = 4 * 1000; // 4 seconds
+
+ Log.v(TAG, reportTag);
+ for (int i = 0; i < total_num_of_videos; i++) {
+ Thread.sleep(WAIT_FOR_PREVIEW);
+ // record a video
+ inst.sendCharacterSync(KeyEvent.KEYCODE_CAMERA);
+ Thread.sleep(video_duration);
+ inst.sendCharacterSync(KeyEvent.KEYCODE_CAMERA);
+ }
+ }
+
+ @LargeTest
+ public void testBackEffectsVideoCapture() throws Exception {
+ Instrumentation inst = getInstrumentation();
+
+ Intent intent = new Intent();
+ intent.setClass(getInstrumentation().getTargetContext(),
+ CameraEffectsRecordingSample.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra("OUTPUT_FILENAME", Environment.getExternalStorageDirectory().toString()
+ + "/CameraEffectsRecordingTest.mp4");
+ Activity act = inst.startActivitySync(intent);
+ captureVideos("Back Camera Video Capture\n", inst);
+ act.finish();
+
+ // Verification
+ File file = new File(Environment.getExternalStorageDirectory(),
+ "CameraEffectsRecordingTest.mp4");
+ Uri uri = Uri.fromFile(file);
+ verify(getActivity(), uri);
+ }
+
+ // Verify result code, result data, and the duration.
+ private void verify(CameraEffectsRecordingSample activity, Uri uri) throws Exception {
+ assertNotNull(uri);
+ // Verify the video file
+ MediaMetadataRetriever retriever = new MediaMetadataRetriever();
+ retriever.setDataSource(activity, uri);
+ String duration = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_DURATION);
+ assertNotNull(duration);
+ int durationValue = Integer.parseInt(duration);
+ Log.v(TAG, "Video duration is " + durationValue);
+ assertTrue(durationValue > 0);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index f721cdc..9306127 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -93,7 +93,7 @@ import com.android.systemui.statusbar.policy.NotificationRowLayout;
public class PhoneStatusBar extends BaseStatusBar {
static final String TAG = "PhoneStatusBar";
public static final boolean DEBUG = false;
- public static final boolean SPEW = true;
+ public static final boolean SPEW = DEBUG;
public static final boolean DUMPTRUCK = true; // extra dumpsys info
// additional instrumentation for testing purposes; intended to be left on during development
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index bcba3c2..4ebabd3 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -23,6 +23,7 @@ import com.android.internal.R;
import android.app.ActivityManagerNative;
import android.app.AlertDialog;
+import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
@@ -31,14 +32,18 @@ import android.content.IntentFilter;
import android.content.pm.UserInfo;
import android.media.AudioManager;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemProperties;
+import android.os.Vibrator;
import android.provider.Settings;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.util.Log;
+import android.view.IWindowManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -77,6 +82,8 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
private ToggleAction.State mAirplaneState = ToggleAction.State.Off;
private boolean mIsWaitingForEcmExit = false;
+ private IWindowManager mIWindowManager;
+
/**
* @param context everything needs a context :(
*/
@@ -119,7 +126,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
* @return A new dialog.
*/
private AlertDialog createDialog() {
- mSilentModeAction = new SilentModeAction(mAudioManager, mHandler);
+ mSilentModeAction = new SilentModeAction(mContext, mAudioManager, mHandler);
mAirplaneModeOn = new ToggleAction(
R.drawable.ic_lock_airplane_mode,
@@ -200,6 +207,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
public void onPress() {
try {
ActivityManagerNative.getDefault().switchUser(user.id);
+ getWindowManager().lockNow();
} catch (RemoteException re) {
Log.e(TAG, "Couldn't switch user " + re);
}
@@ -540,10 +548,15 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
private final AudioManager mAudioManager;
private final Handler mHandler;
+ private final boolean mHasVibrator;
+ private final Context mContext;
- SilentModeAction(AudioManager audioManager, Handler handler) {
+ SilentModeAction(Context context, AudioManager audioManager, Handler handler) {
mAudioManager = audioManager;
mHandler = handler;
+ mContext = context;
+ Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
+ mHasVibrator = vibrator != null && vibrator.hasVibrator();
}
private int ringerModeToIndex(int ringerMode) {
@@ -567,6 +580,9 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
// Set up click handler
itemView.setTag(i);
itemView.setOnClickListener(this);
+ if (itemView.getId() == R.id.option2 && !mHasVibrator) {
+ itemView.setVisibility(View.GONE);
+ }
}
return v;
}
@@ -667,4 +683,12 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
intent.putExtra("state", on);
mContext.sendBroadcast(intent);
}
+
+ private IWindowManager getWindowManager() {
+ if (mIWindowManager == null) {
+ IBinder b = ServiceManager.getService(Context.WINDOW_SERVICE);
+ mIWindowManager = IWindowManager.Stub.asInterface(b);
+ }
+ return mIWindowManager;
+ }
}
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index a85b605..eb024e9 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -170,6 +170,15 @@ class AppWidgetService extends IAppWidgetService.Stub
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
mContext.registerReceiver(mBroadcastReceiver, sdFilter);
+
+ IntentFilter userFilter = new IntentFilter();
+ userFilter.addAction(Intent.ACTION_USER_REMOVED);
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ onUserRemoved(intent.getIntExtra(Intent.EXTRA_USERID, -1));
+ }
+ }, userFilter);
}
@Override
@@ -192,19 +201,6 @@ class AppWidgetService extends IAppWidgetService.Stub
getImplForUser().deleteAllHosts();
}
- void cancelBroadcasts(Provider p) {
- if (p.broadcast != null) {
- mAlarmManager.cancel(p.broadcast);
- long token = Binder.clearCallingIdentity();
- try {
- p.broadcast.cancel();
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- p.broadcast = null;
- }
- }
-
@Override
public void bindAppWidgetId(int appWidgetId, ComponentName provider) throws RemoteException {
getImplForUser().bindAppWidgetId(appWidgetId, provider);
@@ -222,8 +218,15 @@ class AppWidgetService extends IAppWidgetService.Stub
return getImplForUser().startListening(host, packageName, hostId, updatedViews);
}
- // TODO: Call this from PackageManagerService when a user is removed
- public void removeUser(int userId) {
+ public void onUserRemoved(int userId) {
+ AppWidgetServiceImpl impl = mAppWidgetServices.get(userId);
+ if (userId < 1) return;
+
+ if (impl == null) {
+ AppWidgetServiceImpl.getSettingsFile(userId).delete();
+ } else {
+ impl.onUserRemoved();
+ }
}
private AppWidgetServiceImpl getImplForUser() {
diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java
index 182a884..b24823e 100644
--- a/services/java/com/android/server/AppWidgetServiceImpl.java
+++ b/services/java/com/android/server/AppWidgetServiceImpl.java
@@ -1484,9 +1484,13 @@ class AppWidgetServiceImpl {
}
}
+ static File getSettingsFile(int userId) {
+ return new File("/data/system/users/" + userId + "/" + SETTINGS_FILENAME);
+ }
+
AtomicFile savedStateFile() {
File dir = new File("/data/system/users/" + mUserId);
- File settingsFile = new File(dir, SETTINGS_FILENAME);
+ File settingsFile = getSettingsFile(mUserId);
if (!dir.exists()) {
dir.mkdirs();
if (mUserId == 0) {
@@ -1500,6 +1504,16 @@ class AppWidgetServiceImpl {
return new AtomicFile(settingsFile);
}
+ void onUserRemoved() {
+ // prune the ones we don't want to keep
+ int N = mInstalledProviders.size();
+ for (int i = N - 1; i >= 0; i--) {
+ Provider p = mInstalledProviders.get(i);
+ cancelBroadcasts(p);
+ }
+ getSettingsFile(mUserId).delete();
+ }
+
void addProvidersForPackageLocked(String pkgName) {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.setPackage(pkgName);
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index bd8ae17..a0d5beb 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -201,9 +201,9 @@ class BackupManagerService extends IBackupManager.Stub {
BackupHandler mBackupHandler;
PendingIntent mRunBackupIntent, mRunInitIntent;
BroadcastReceiver mRunBackupReceiver, mRunInitReceiver;
- // map UIDs to the set of backup client services within that UID's app set
- final SparseArray<HashSet<ApplicationInfo>> mBackupParticipants
- = new SparseArray<HashSet<ApplicationInfo>>();
+ // map UIDs to the set of participating packages under that UID
+ final SparseArray<HashSet<String>> mBackupParticipants
+ = new SparseArray<HashSet<String>>();
// set of backup services that have pending changes
class BackupRequest {
public String packageName;
@@ -960,7 +960,6 @@ class BackupManagerService extends IBackupManager.Stub {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
filter.addDataScheme("package");
mContext.registerReceiver(mBroadcastReceiver, filter);
// Register for events related to sdcard installation.
@@ -1239,12 +1238,13 @@ class BackupManagerService extends IBackupManager.Stub {
// Enqueue a new backup of every participant
synchronized (mBackupParticipants) {
- int N = mBackupParticipants.size();
+ final int N = mBackupParticipants.size();
for (int i=0; i<N; i++) {
- int uid = mBackupParticipants.keyAt(i);
- HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
- for (ApplicationInfo app: participants) {
- dataChangedImpl(app.packageName);
+ HashSet<String> participants = mBackupParticipants.valueAt(i);
+ if (participants != null) {
+ for (String packageName : participants) {
+ dataChangedImpl(packageName);
+ }
}
}
}
@@ -1302,8 +1302,7 @@ class BackupManagerService extends IBackupManager.Stub {
Bundle extras = intent.getExtras();
String pkgList[] = null;
if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
- Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
- Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
+ Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
Uri uri = intent.getData();
if (uri == null) {
return;
@@ -1312,14 +1311,8 @@ class BackupManagerService extends IBackupManager.Stub {
if (pkgName != null) {
pkgList = new String[] { pkgName };
}
- if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
- // use the existing "add with replacement" logic
- if (MORE_DEBUG) Slog.d(TAG, "PACKAGE_REPLACED, updating package " + pkgName);
- added = replacing = true;
- } else {
- added = Intent.ACTION_PACKAGE_ADDED.equals(action);
- replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
- }
+ added = Intent.ACTION_PACKAGE_ADDED.equals(action);
+ replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
} else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
added = true;
pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
@@ -1331,20 +1324,23 @@ class BackupManagerService extends IBackupManager.Stub {
if (pkgList == null || pkgList.length == 0) {
return;
}
+
+ final int uid = extras.getInt(Intent.EXTRA_UID);
if (added) {
synchronized (mBackupParticipants) {
if (replacing) {
- updatePackageParticipantsLocked(pkgList);
- } else {
- addPackageParticipantsLocked(pkgList);
+ // This is the package-replaced case; we just remove the entry
+ // under the old uid and fall through to re-add.
+ removePackageParticipantsLocked(pkgList, uid);
}
+ addPackageParticipantsLocked(pkgList);
}
} else {
if (replacing) {
// The package is being updated. We'll receive a PACKAGE_ADDED shortly.
} else {
synchronized (mBackupParticipants) {
- removePackageParticipantsLocked(pkgList);
+ removePackageParticipantsLocked(pkgList, uid);
}
}
}
@@ -1391,12 +1387,12 @@ class BackupManagerService extends IBackupManager.Stub {
for (PackageInfo pkg : targetPkgs) {
if (packageName == null || pkg.packageName.equals(packageName)) {
int uid = pkg.applicationInfo.uid;
- HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
+ HashSet<String> set = mBackupParticipants.get(uid);
if (set == null) {
- set = new HashSet<ApplicationInfo>();
+ set = new HashSet<String>();
mBackupParticipants.put(uid, set);
}
- set.add(pkg.applicationInfo);
+ set.add(pkg.packageName);
if (MORE_DEBUG) Slog.v(TAG, "Agent found; added");
// If we've never seen this app before, schedule a backup for it
@@ -1410,63 +1406,36 @@ class BackupManagerService extends IBackupManager.Stub {
}
// Remove the given packages' entries from our known active set.
- void removePackageParticipantsLocked(String[] packageNames) {
+ void removePackageParticipantsLocked(String[] packageNames, int oldUid) {
if (packageNames == null) {
Slog.w(TAG, "removePackageParticipants with null list");
return;
}
- if (DEBUG) Slog.v(TAG, "removePackageParticipantsLocked: #" + packageNames.length);
- List<PackageInfo> knownPackages = allAgentPackages();
+ if (DEBUG) Slog.v(TAG, "removePackageParticipantsLocked: uid=" + oldUid
+ + " #" + packageNames.length);
for (String pkg : packageNames) {
- removePackageParticipantsLockedInner(pkg, knownPackages);
+ // Known previous UID, so we know which package set to check
+ HashSet<String> set = mBackupParticipants.get(oldUid);
+ if (set != null && set.contains(pkg)) {
+ removePackageFromSetLocked(set, pkg);
+ if (set.isEmpty()) {
+ if (MORE_DEBUG) Slog.v(TAG, " last one of this uid; purging set");
+ mBackupParticipants.remove(oldUid);
+ }
+ }
}
}
- private void removePackageParticipantsLockedInner(String packageName,
- List<PackageInfo> allPackages) {
- if (MORE_DEBUG) {
- Slog.v(TAG, "removePackageParticipantsLockedInner (" + packageName
- + ") removing from " + allPackages.size() + " entries");
- for (PackageInfo p : allPackages) {
- Slog.v(TAG, " - " + p.packageName);
- }
- }
- for (PackageInfo pkg : allPackages) {
- if (packageName == null || pkg.packageName.equals(packageName)) {
- /*
- int uid = -1;
- try {
- PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
- uid = info.applicationInfo.uid;
- } catch (NameNotFoundException e) {
- // we don't know this package name, so just skip it for now
- continue;
- }
- */
- final int uid = pkg.applicationInfo.uid;
- if (MORE_DEBUG) Slog.i(TAG, " found pkg " + packageName + " uid=" + uid);
-
- HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
- if (set != null) {
- // Find the existing entry with the same package name, and remove it.
- // We can't just remove(app) because the instances are different.
- for (ApplicationInfo entry: set) {
- if (MORE_DEBUG) Slog.i(TAG, " checking against " + entry.packageName);
- if (entry.packageName.equals(pkg)) {
- if (MORE_DEBUG) Slog.v(TAG, " removing participant " + pkg);
- set.remove(entry);
- removeEverBackedUp(pkg.packageName);
- break;
- }
- }
- if (set.size() == 0) {
- mBackupParticipants.delete(uid);
- }
- } else {
- if (MORE_DEBUG) Slog.i(TAG, " ... not found in uid mapping");
- }
- }
+ private void removePackageFromSetLocked(final HashSet<String> set,
+ final String packageName) {
+ if (set.contains(packageName)) {
+ // Found it. Remove this one package from the bookkeeping, and
+ // if it's the last participating app under this uid we drop the
+ // (now-empty) set as well.
+ if (MORE_DEBUG) Slog.v(TAG, " removing participant " + packageName);
+ removeEverBackedUp(packageName);
+ set.remove(packageName);
}
}
@@ -1497,24 +1466,6 @@ class BackupManagerService extends IBackupManager.Stub {
return packages;
}
- // Reset the given package's known backup participants. Unlike add/remove, the update
- // action cannot be passed a null package name.
- void updatePackageParticipantsLocked(String[] packageNames) {
- if (packageNames == null) {
- Slog.e(TAG, "updatePackageParticipants called with null package list");
- return;
- }
- if (DEBUG) Slog.v(TAG, "updatePackageParticipantsLocked: #" + packageNames.length);
-
- if (packageNames.length > 0) {
- List<PackageInfo> allApps = allAgentPackages();
- for (String packageName : packageNames) {
- removePackageParticipantsLockedInner(packageName, allApps);
- addPackageParticipantsLockedInner(packageName, allApps);
- }
- }
- }
-
// Called from the backup task: record that the given app has been successfully
// backed up at least once
void logBackupComplete(String packageName) {
@@ -4772,11 +4723,11 @@ class BackupManagerService extends IBackupManager.Stub {
}
private void dataChangedImpl(String packageName) {
- HashSet<ApplicationInfo> targets = dataChangedTargets(packageName);
+ HashSet<String> targets = dataChangedTargets(packageName);
dataChangedImpl(packageName, targets);
}
- private void dataChangedImpl(String packageName, HashSet<ApplicationInfo> targets) {
+ private void dataChangedImpl(String packageName, HashSet<String> targets) {
// Record that we need a backup pass for the caller. Since multiple callers
// may share a uid, we need to note all candidates within that uid and schedule
// a backup pass for each of them.
@@ -4790,27 +4741,23 @@ class BackupManagerService extends IBackupManager.Stub {
synchronized (mQueueLock) {
// Note that this client has made data changes that need to be backed up
- for (ApplicationInfo app : targets) {
- // validate the caller-supplied package name against the known set of
- // packages associated with this uid
- if (app.packageName.equals(packageName)) {
- // Add the caller to the set of pending backups. If there is
- // one already there, then overwrite it, but no harm done.
- BackupRequest req = new BackupRequest(packageName);
- if (mPendingBackups.put(app.packageName, req) == null) {
- if (DEBUG) Slog.d(TAG, "Now staging backup of " + packageName);
-
- // Journal this request in case of crash. The put()
- // operation returned null when this package was not already
- // in the set; we want to avoid touching the disk redundantly.
- writeToJournalLocked(packageName);
+ if (targets.contains(packageName)) {
+ // Add the caller to the set of pending backups. If there is
+ // one already there, then overwrite it, but no harm done.
+ BackupRequest req = new BackupRequest(packageName);
+ if (mPendingBackups.put(packageName, req) == null) {
+ if (DEBUG) Slog.d(TAG, "Now staging backup of " + packageName);
+
+ // Journal this request in case of crash. The put()
+ // operation returned null when this package was not already
+ // in the set; we want to avoid touching the disk redundantly.
+ writeToJournalLocked(packageName);
- if (MORE_DEBUG) {
- int numKeys = mPendingBackups.size();
- Slog.d(TAG, "Now awaiting backup for " + numKeys + " participants:");
- for (BackupRequest b : mPendingBackups.values()) {
- Slog.d(TAG, " + " + b);
- }
+ if (MORE_DEBUG) {
+ int numKeys = mPendingBackups.size();
+ Slog.d(TAG, "Now awaiting backup for " + numKeys + " participants:");
+ for (BackupRequest b : mPendingBackups.values()) {
+ Slog.d(TAG, " + " + b);
}
}
}
@@ -4819,7 +4766,7 @@ class BackupManagerService extends IBackupManager.Stub {
}
// Note: packageName is currently unused, but may be in the future
- private HashSet<ApplicationInfo> dataChangedTargets(String packageName) {
+ private HashSet<String> dataChangedTargets(String packageName) {
// If the caller does not hold the BACKUP permission, it can only request a
// backup of its own data.
if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
@@ -4831,11 +4778,11 @@ class BackupManagerService extends IBackupManager.Stub {
// a caller with full permission can ask to back up any participating app
// !!! TODO: allow backup of ANY app?
- HashSet<ApplicationInfo> targets = new HashSet<ApplicationInfo>();
+ HashSet<String> targets = new HashSet<String>();
synchronized (mBackupParticipants) {
int N = mBackupParticipants.size();
for (int i = 0; i < N; i++) {
- HashSet<ApplicationInfo> s = mBackupParticipants.valueAt(i);
+ HashSet<String> s = mBackupParticipants.valueAt(i);
if (s != null) {
targets.addAll(s);
}
@@ -4862,7 +4809,7 @@ class BackupManagerService extends IBackupManager.Stub {
// ----- IBackupManager binder interface -----
public void dataChanged(final String packageName) {
- final HashSet<ApplicationInfo> targets = dataChangedTargets(packageName);
+ final HashSet<String> targets = dataChangedTargets(packageName);
if (targets == null) {
Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
+ " uid=" + Binder.getCallingUid());
@@ -4889,7 +4836,7 @@ class BackupManagerService extends IBackupManager.Stub {
// If the caller does not hold the BACKUP permission, it can only request a
// wipe of its own backed-up data.
- HashSet<ApplicationInfo> apps;
+ HashSet<String> apps;
if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
apps = mBackupParticipants.get(Binder.getCallingUid());
@@ -4897,30 +4844,27 @@ class BackupManagerService extends IBackupManager.Stub {
// a caller with full permission can ask to back up any participating app
// !!! TODO: allow data-clear of ANY app?
if (DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps");
- apps = new HashSet<ApplicationInfo>();
+ apps = new HashSet<String>();
int N = mBackupParticipants.size();
for (int i = 0; i < N; i++) {
- HashSet<ApplicationInfo> s = mBackupParticipants.valueAt(i);
+ HashSet<String> s = mBackupParticipants.valueAt(i);
if (s != null) {
apps.addAll(s);
}
}
}
- // now find the given package in the set of candidate apps
- for (ApplicationInfo app : apps) {
- if (app.packageName.equals(packageName)) {
- if (DEBUG) Slog.v(TAG, "Found the app - running clear process");
- // found it; fire off the clear request
- synchronized (mQueueLock) {
- long oldId = Binder.clearCallingIdentity();
- mWakelock.acquire();
- Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR,
- new ClearParams(getTransport(mCurrentTransport), info));
- mBackupHandler.sendMessage(msg);
- Binder.restoreCallingIdentity(oldId);
- }
- break;
+ // Is the given app an available participant?
+ if (apps.contains(packageName)) {
+ if (DEBUG) Slog.v(TAG, "Found the app - running clear process");
+ // found it; fire off the clear request
+ synchronized (mQueueLock) {
+ long oldId = Binder.clearCallingIdentity();
+ mWakelock.acquire();
+ Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR,
+ new ClearParams(getTransport(mCurrentTransport), info));
+ mBackupHandler.sendMessage(msg);
+ Binder.restoreCallingIdentity(oldId);
}
}
}
@@ -5838,9 +5782,9 @@ class BackupManagerService extends IBackupManager.Stub {
int uid = mBackupParticipants.keyAt(i);
pw.print(" uid: ");
pw.println(uid);
- HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
- for (ApplicationInfo app: participants) {
- pw.println(" " + app.packageName);
+ HashSet<String> participants = mBackupParticipants.valueAt(i);
+ for (String app: participants) {
+ pw.println(" " + app);
}
}
diff --git a/services/java/com/android/server/ClipboardService.java b/services/java/com/android/server/ClipboardService.java
index 062ab74..2e2a278 100644
--- a/services/java/com/android/server/ClipboardService.java
+++ b/services/java/com/android/server/ClipboardService.java
@@ -34,6 +34,7 @@ import android.os.Parcel;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.os.UserId;
import android.util.Pair;
import android.util.Slog;
@@ -204,7 +205,7 @@ public class ClipboardService extends IClipboard.Stub {
PackageInfo pi;
try {
pi = mPm.getPackageInfo(pkg, 0);
- if (pi.applicationInfo.uid != uid) {
+ if (!UserId.isSameApp(pi.applicationInfo.uid, uid)) {
throw new SecurityException("Calling uid " + uid
+ " does not own package " + pkg);
}
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index 8ee12bc..6d83f30 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -25,9 +25,11 @@ import android.app.PendingIntent;
import android.app.WallpaperInfo;
import android.app.backup.BackupManager;
import android.app.backup.WallpaperBackupHelper;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -36,6 +38,7 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Environment;
import android.os.FileUtils;
import android.os.IBinder;
import android.os.RemoteException;
@@ -401,41 +404,48 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
wallpaper.wallpaperObserver.stopWatching();
}
}
-
+
public void systemReady() {
if (DEBUG) Slog.v(TAG, "systemReady");
WallpaperData wallpaper = mWallpaperMap.get(0);
switchWallpaper(wallpaper);
wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper);
wallpaper.wallpaperObserver.startWatching();
- ActivityManagerService ams = (ActivityManagerService) ServiceManager
- .getService(Context.ACTIVITY_SERVICE);
- ams.addUserListener(new ActivityManagerService.UserListener() {
-
- @Override
- public void onUserChanged(int userId) {
- switchUser(userId);
- }
+ IntentFilter userFilter = new IntentFilter();
+ userFilter.addAction(Intent.ACTION_USER_SWITCHED);
+ userFilter.addAction(Intent.ACTION_USER_REMOVED);
+ mContext.registerReceiver(new BroadcastReceiver() {
@Override
- public void onUserAdded(int userId) {
- }
-
- @Override
- public void onUserRemoved(int userId) {
- }
-
- @Override
- public void onUserLoggedOut(int userId) {
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+ switchUser(intent.getIntExtra(Intent.EXTRA_USERID, 0));
+ } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
+ removeUser(intent.getIntExtra(Intent.EXTRA_USERID, 0));
+ }
}
-
- });
+ }, userFilter);
}
String getName() {
return mWallpaperMap.get(0).name;
}
+ void removeUser(int userId) {
+ synchronized (mLock) {
+ WallpaperData wallpaper = mWallpaperMap.get(userId);
+ if (wallpaper != null) {
+ wallpaper.wallpaperObserver.stopWatching();
+ mWallpaperMap.remove(userId);
+ }
+ File wallpaperFile = new File(getWallpaperDir(userId), WALLPAPER);
+ wallpaperFile.delete();
+ File wallpaperInfoFile = new File(getWallpaperDir(userId), WALLPAPER_INFO);
+ wallpaperInfoFile.delete();
+ }
+ }
+
void switchUser(int userId) {
synchronized (mLock) {
mCurrentUserId = userId;
@@ -861,7 +871,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub {
}
private static JournaledFile makeJournaledFile(int userId) {
- final String base = "/data/system/users/" + userId + "/" + WALLPAPER_INFO;
+ final String base = getWallpaperDir(userId) + "/" + WALLPAPER_INFO;
return new JournaledFile(new File(base), new File(base + ".tmp"));
}
diff --git a/services/java/com/android/server/WiredAccessoryObserver.java b/services/java/com/android/server/WiredAccessoryObserver.java
index a7a46dd..9b4eddc 100644
--- a/services/java/com/android/server/WiredAccessoryObserver.java
+++ b/services/java/com/android/server/WiredAccessoryObserver.java
@@ -66,7 +66,7 @@ class WiredAccessoryObserver extends UEventObserver {
public String getDevName() { return mDevName; }
public String getDevPath() {
- return String.format("DEVPATH=/devices/virtual/switch/%s", mDevName);
+ return String.format("/devices/virtual/switch/%s", mDevName);
}
public String getSwitchStatePath() {
@@ -158,7 +158,7 @@ class WiredAccessoryObserver extends UEventObserver {
init(); // set initial status
for (int i = 0; i < uEventInfo.size(); ++i) {
UEventInfo uei = uEventInfo.get(i);
- startObserving(uei.getDevPath());
+ startObserving("DEVPATH="+uei.getDevPath());
}
}
}
@@ -168,29 +168,20 @@ class WiredAccessoryObserver extends UEventObserver {
if (LOG) Slog.v(TAG, "Headset UEVENT: " + event.toString());
try {
+ String devPath = event.get("DEVPATH");
String name = event.get("SWITCH_NAME");
int state = Integer.parseInt(event.get("SWITCH_STATE"));
- updateState(name, state);
+ updateState(devPath, name, state);
} catch (NumberFormatException e) {
Slog.e(TAG, "Could not parse switch state from event " + event);
}
}
- private synchronized final void updateState(String name, int state)
+ private synchronized final void updateState(String devPath, String name, int state)
{
- // FIXME: When ueventd informs of a change in state for a switch, it does not have to be
- // the case that the name reported by /sys/class/switch/<device>/name is the same as
- // <device>. For normal users of the linux switch class driver, it will be. But it is
- // technically possible to hook the print_name method in the class driver and return a
- // different name each and every time the name sysfs entry is queried.
- //
- // Right now this is not the case for any of the switch implementations used here. I'm not
- // certain anyone would ever choose to implement such a dynamic name, or what it would mean
- // for the implementation at this level, but if it ever happens, we will need to revisit
- // this code.
for (int i = 0; i < uEventInfo.size(); ++i) {
UEventInfo uei = uEventInfo.get(i);
- if (name.equals(uei.getDevName())) {
+ if (devPath.equals(uei.getDevPath())) {
update(name, uei.computeNewHeadsetState(mHeadsetState, state));
return;
}
@@ -213,7 +204,7 @@ class WiredAccessoryObserver extends UEventObserver {
curState = Integer.valueOf((new String(buffer, 0, len)).trim());
if (curState > 0) {
- updateState(uei.getDevName(), curState);
+ updateState(uei.getDevPath(), uei.getDevName(), curState);
}
} catch (FileNotFoundException e) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 60749b3..78f17bc 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -152,6 +152,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
@@ -3038,7 +3039,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
final void logAppTooSlow(ProcessRecord app, long startTime, String msg) {
- if (IS_USER_BUILD) {
+ if (true || IS_USER_BUILD) {
return;
}
String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);
@@ -3582,9 +3583,14 @@ public final class ActivityManagerService extends ActivityManagerNative
if (doit) {
procs.add(app);
}
- } else if ((uid > 0 && uid != Process.SYSTEM_UID && app.info.uid == uid)
- || app.processName.equals(packageName)
- || app.processName.startsWith(procNamePrefix)) {
+ // If uid is specified and the uid and process name match
+ // Or, the uid is not specified and the process name matches
+ } else if (((uid > 0 && uid != Process.SYSTEM_UID && app.info.uid == uid)
+ && (app.processName.equals(packageName)
+ || app.processName.startsWith(procNamePrefix)))
+ || (uid < 0
+ && (app.processName.equals(packageName)
+ || app.processName.startsWith(procNamePrefix)))) {
if (app.setAdj >= minOomAdj) {
if (!doit) {
return true;
@@ -3635,7 +3641,8 @@ public final class ActivityManagerService extends ActivityManagerNative
for (i=0; i<mMainStack.mHistory.size(); i++) {
ActivityRecord r = (ActivityRecord)mMainStack.mHistory.get(i);
final boolean samePackage = r.packageName.equals(name);
- if ((samePackage || r.task == lastTask)
+ if (r.userId == userId
+ && (samePackage || r.task == lastTask)
&& (r.app == null || evenPersistent || !r.app.persistent)) {
if (!doit) {
if (r.finishing) {
@@ -3685,7 +3692,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
ArrayList<ContentProviderRecord> providers = new ArrayList<ContentProviderRecord>();
- for (ContentProviderRecord provider : mProviderMap.getProvidersByClass(-1).values()) {
+ for (ContentProviderRecord provider : mProviderMap.getProvidersByClass(userId).values()) {
if (provider.info.packageName.equals(name)
&& (provider.proc == null || evenPersistent || !provider.proc.persistent)) {
if (!doit) {
@@ -4118,7 +4125,16 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
}, pkgFilter);
-
+
+ IntentFilter userFilter = new IntentFilter();
+ userFilter.addAction(Intent.ACTION_USER_REMOVED);
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ onUserRemoved(intent);
+ }
+ }, userFilter);
+
synchronized (this) {
// Ensure that any processes we had put on hold are now started
// up.
@@ -12469,7 +12485,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (DEBUG_BROADCAST_LIGHT) Slog.v(
TAG, (sticky ? "Broadcast sticky: ": "Broadcast: ") + intent
- + " ordered=" + ordered);
+ + " ordered=" + ordered + " userid=" + userId);
if ((resultTo != null) && !ordered) {
Slog.w(TAG, "Broadcast " + intent + " not ordered but result callback requested!");
}
@@ -14593,25 +14609,6 @@ public final class ActivityManagerService extends ActivityManagerNative
private int mCurrentUserId;
private SparseIntArray mLoggedInUsers = new SparseIntArray(5);
- private ArrayList<UserListener> mUserListeners = new ArrayList<UserListener>(3);
-
- public interface UserListener {
- public void onUserChanged(int userId);
-
- public void onUserAdded(int userId);
-
- public void onUserRemoved(int userId);
-
- public void onUserLoggedOut(int userId);
- }
-
- public void addUserListener(UserListener listener) {
- synchronized (this) {
- if (!mUserListeners.contains(listener)) {
- mUserListeners.add(listener);
- }
- }
- }
public boolean switchUser(int userId) {
final int callingUid = Binder.getCallingUid();
@@ -14622,8 +14619,6 @@ public final class ActivityManagerService extends ActivityManagerNative
if (mCurrentUserId == userId)
return true;
- ArrayList<UserListener> listeners;
-
synchronized (this) {
// Check if user is already logged in, otherwise check if user exists first before
// adding to the list of logged in users.
@@ -14640,23 +14635,44 @@ public final class ActivityManagerService extends ActivityManagerNative
startHomeActivityLocked(userId);
}
- listeners = (ArrayList<UserListener>) mUserListeners.clone();
- }
- // Inform the listeners
- for (UserListener listener : listeners) {
- listener.onUserChanged(userId);
}
+
+ // Inform of user switch
+ Intent addedIntent = new Intent(Intent.ACTION_USER_SWITCHED);
+ addedIntent.putExtra(Intent.EXTRA_USERID, userId);
+ mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_ACCOUNTS);
+
return true;
}
- private boolean userExists(int userId) {
- try {
- List<UserInfo> users = AppGlobals.getPackageManager().getUsers();
- for (UserInfo user : users) {
- if (user.id == userId) {
- return true;
+ private void onUserRemoved(Intent intent) {
+ int extraUserId = intent.getIntExtra(Intent.EXTRA_USERID, -1);
+ if (extraUserId < 1) return;
+
+ // Kill all the processes for the user
+ ArrayList<Pair<String, Integer>> pkgAndUids = new ArrayList<Pair<String,Integer>>();
+ synchronized (this) {
+ HashMap<String,SparseArray<ProcessRecord>> map = mProcessNames.getMap();
+ for (Entry<String, SparseArray<ProcessRecord>> uidMap : map.entrySet()) {
+ SparseArray<ProcessRecord> uids = uidMap.getValue();
+ for (int i = 0; i < uids.size(); i++) {
+ if (UserId.getUserId(uids.keyAt(i)) == extraUserId) {
+ pkgAndUids.add(new Pair<String,Integer>(uidMap.getKey(), uids.keyAt(i)));
+ }
}
}
+
+ for (Pair<String,Integer> pkgAndUid : pkgAndUids) {
+ forceStopPackageLocked(pkgAndUid.first, pkgAndUid.second,
+ false, false, true, true, extraUserId);
+ }
+ }
+ }
+
+ private boolean userExists(int userId) {
+ try {
+ UserInfo user = AppGlobals.getPackageManager().getUser(userId);
+ return user != null;
} catch (RemoteException re) {
// Won't happen, in same process
}
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 067bf28..1593707 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -134,6 +134,7 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@@ -404,7 +405,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// Delay time in millisecs
static final int BROADCAST_DELAY = 10 * 1000;
- final UserManager mUserManager;
+ static UserManager sUserManager;
// Stores a list of users whose package restrictions file needs to be updated
private HashSet<Integer> mDirtyUsers = new HashSet<Integer>();
@@ -632,7 +633,7 @@ public class PackageManagerService extends IPackageManager.Stub {
packages[i] = ent.getKey();
components[i] = ent.getValue();
PackageSetting ps = mSettings.mPackages.get(ent.getKey());
- uids[i] = (ps != null) ? ps.uid : -1;
+ uids[i] = (ps != null) ? ps.appId : -1;
i++;
}
size = i;
@@ -676,14 +677,15 @@ public class PackageManagerService extends IPackageManager.Stub {
}
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
res.pkg.applicationInfo.packageName,
- extras, null, null);
+ extras, null, null, UserId.USER_ALL);
if (update) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
res.pkg.applicationInfo.packageName,
- extras, null, null);
+ extras, null, null, UserId.USER_ALL);
sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
null, null,
- res.pkg.applicationInfo.packageName, null);
+ res.pkg.applicationInfo.packageName, null,
+ UserId.USER_ALL);
}
if (res.removedInfo.args != null) {
// Remove the replaced package's older resources safely now
@@ -820,6 +822,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
void scheduleWritePackageRestrictionsLocked(int userId) {
+ if (!sUserManager.exists(userId)) return;
mDirtyUsers.add(userId);
if (!mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) {
mHandler.sendEmptyMessageDelayed(WRITE_PACKAGE_RESTRICTIONS, WRITE_SETTINGS_DELAY);
@@ -920,7 +923,7 @@ public class PackageManagerService extends IPackageManager.Stub {
mUserAppDataDir = new File(dataDir, "user");
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
- mUserManager = new UserManager(mInstaller, mUserAppDataDir);
+ sUserManager = new UserManager(mInstaller, mUserAppDataDir);
readPermissions();
@@ -1086,7 +1089,7 @@ public class PackageManagerService extends IPackageManager.Stub {
+ " no longer exists; wiping its data";
reportSettingsProblem(Log.WARN, msg);
mInstaller.remove(ps.name, 0);
- mUserManager.removePackageForAllUsers(ps.name);
+ sUserManager.removePackageForAllUsers(ps.name);
}
}
}
@@ -1242,7 +1245,7 @@ public class PackageManagerService extends IPackageManager.Stub {
Slog.w(TAG, "Couldn't remove app data directory for package: "
+ ps.name + ", retcode=" + retCode);
} else {
- mUserManager.removePackageForAllUsers(ps.name);
+ sUserManager.removePackageForAllUsers(ps.name);
}
if (ps.codePath != null) {
if (!ps.codePath.delete()) {
@@ -1506,29 +1509,39 @@ public class PackageManagerService extends IPackageManager.Stub {
return cur;
}
- PackageInfo generatePackageInfo(PackageParser.Package p, int flags) {
+ PackageInfo generatePackageInfo(PackageParser.Package p, int flags, int userId) {
+ if (!sUserManager.exists(userId)) return null;
+ PackageInfo pi;
if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
// The package has been uninstalled but has retained data and resources.
- return PackageParser.generatePackageInfo(p, null, flags, 0, 0, null);
- }
- final PackageSetting ps = (PackageSetting)p.mExtras;
- if (ps == null) {
- return null;
+ pi = PackageParser.generatePackageInfo(p, null, flags, 0, 0, null, false, 0, userId);
+ } else {
+ final PackageSetting ps = (PackageSetting) p.mExtras;
+ if (ps == null) {
+ return null;
+ }
+ final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
+ pi = PackageParser.generatePackageInfo(p, gp.gids, flags,
+ ps.firstInstallTime, ps.lastUpdateTime, gp.grantedPermissions,
+ ps.getStopped(userId), ps.getEnabled(userId), userId);
+ pi.applicationInfo.enabledSetting = ps.getEnabled(userId);
+ pi.applicationInfo.enabled =
+ pi.applicationInfo.enabledSetting == COMPONENT_ENABLED_STATE_DEFAULT
+ || pi.applicationInfo.enabledSetting == COMPONENT_ENABLED_STATE_ENABLED;
}
- final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
- return PackageParser.generatePackageInfo(p, gp.gids, flags,
- ps.firstInstallTime, ps.lastUpdateTime, gp.grantedPermissions);
+ return pi;
}
@Override
public PackageInfo getPackageInfo(String packageName, int flags, int userId) {
+ if (!sUserManager.exists(userId)) return null;
// reader
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
if (DEBUG_PACKAGE_INFO)
Log.v(TAG, "getPackageInfo " + packageName + ": " + p);
if (p != null) {
- return generatePackageInfo(p, flags);
+ return generatePackageInfo(p, flags, userId);
}
if((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) {
return generatePackageInfoFromSettingsLPw(packageName, flags, userId);
@@ -1563,6 +1576,7 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public int getPackageUid(String packageName, int userId) {
+ if (!sUserManager.exists(userId)) return -1;
// reader
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
@@ -1673,6 +1687,7 @@ public class PackageManagerService extends IPackageManager.Stub {
private ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags,
int userId) {
+ if (!sUserManager.exists(userId)) return null;
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
if (ps.pkg == null) {
@@ -1682,15 +1697,18 @@ public class PackageManagerService extends IPackageManager.Stub {
}
return null;
}
- return PackageParser.generateApplicationInfo(ps.pkg, flags);
+ return PackageParser.generateApplicationInfo(ps.pkg, flags, ps.getStopped(userId),
+ ps.getEnabled(userId), userId);
}
return null;
}
private PackageInfo generatePackageInfoFromSettingsLPw(String packageName, int flags,
int userId) {
+ if (!sUserManager.exists(userId)) return null;
PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
+ PackageParser.Package pkg = new PackageParser.Package(packageName);
if (ps.pkg == null) {
ps.pkg = new PackageParser.Package(packageName);
ps.pkg.applicationInfo.packageName = packageName;
@@ -1701,15 +1719,16 @@ public class PackageManagerService extends IPackageManager.Stub {
getDataPathForPackage(ps.pkg.packageName, 0).getPath();
ps.pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString;
}
- ps.pkg.mSetEnabled = ps.getEnabled(userId);
- ps.pkg.mSetStopped = ps.getStopped(userId);
- return generatePackageInfo(ps.pkg, flags);
+ // ps.pkg.mSetEnabled = ps.getEnabled(userId);
+ // ps.pkg.mSetStopped = ps.getStopped(userId);
+ return generatePackageInfo(ps.pkg, flags, userId);
}
return null;
}
@Override
public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) {
+ if (!sUserManager.exists(userId)) return null;
// writer
synchronized (mPackages) {
PackageParser.Package p = mPackages.get(packageName);
@@ -1717,8 +1736,11 @@ public class PackageManagerService extends IPackageManager.Stub {
TAG, "getApplicationInfo " + packageName
+ ": " + p);
if (p != null) {
+ PackageSetting ps = mSettings.mPackages.get(packageName);
+ if (ps == null) return null;
// Note: isEnabledLP() does not apply here - always return info
- return PackageParser.generateApplicationInfo(p, flags);
+ return PackageParser.generateApplicationInfo(p, flags, ps.getStopped(userId),
+ ps.getEnabled(userId));
}
if ("android".equals(packageName)||"system".equals(packageName)) {
return mAndroidApplication;
@@ -1782,12 +1804,16 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
+ if (!sUserManager.exists(userId)) return null;
synchronized (mPackages) {
PackageParser.Activity a = mActivities.mActivities.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);
if (a != null && mSettings.isEnabledLPr(a.info, flags, userId)) {
- return PackageParser.generateActivityInfo(a, flags, userId);
+ PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
+ if (ps == null) return null;
+ return PackageParser.generateActivityInfo(a, flags, ps.getStopped(userId),
+ ps.getEnabled(userId), userId);
}
if (mResolveComponentName.equals(component)) {
return mResolveActivity;
@@ -1798,12 +1824,16 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public ActivityInfo getReceiverInfo(ComponentName component, int flags, int userId) {
+ if (!sUserManager.exists(userId)) return null;
synchronized (mPackages) {
PackageParser.Activity a = mReceivers.mActivities.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getReceiverInfo " + component + ": " + a);
if (a != null && mSettings.isEnabledLPr(a.info, flags, userId)) {
- return PackageParser.generateActivityInfo(a, flags, userId);
+ PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
+ if (ps == null) return null;
+ return PackageParser.generateActivityInfo(a, flags, ps.getStopped(userId),
+ ps.getEnabled(userId), userId);
}
}
return null;
@@ -1811,12 +1841,16 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) {
+ if (!sUserManager.exists(userId)) return null;
synchronized (mPackages) {
PackageParser.Service s = mServices.mServices.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getServiceInfo " + component + ": " + s);
if (s != null && mSettings.isEnabledLPr(s.info, flags, userId)) {
- return PackageParser.generateServiceInfo(s, flags, userId);
+ PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
+ if (ps == null) return null;
+ return PackageParser.generateServiceInfo(s, flags, ps.getStopped(userId),
+ ps.getEnabled(userId), userId);
}
}
return null;
@@ -1824,12 +1858,16 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public ProviderInfo getProviderInfo(ComponentName component, int flags, int userId) {
+ if (!sUserManager.exists(userId)) return null;
synchronized (mPackages) {
PackageParser.Provider p = mProvidersByComponent.get(component);
if (DEBUG_PACKAGE_INFO) Log.v(
TAG, "getProviderInfo " + component + ": " + p);
if (p != null && mSettings.isEnabledLPr(p.info, flags, userId)) {
- return PackageParser.generateProviderInfo(p, flags, userId);
+ PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
+ if (ps == null) return null;
+ return PackageParser.generateProviderInfo(p, flags, ps.getStopped(userId),
+ ps.getEnabled(userId), userId);
}
}
return null;
@@ -2253,6 +2291,7 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
int flags, int userId) {
+ if (!sUserManager.exists(userId)) return null;
List<ResolveInfo> query = queryIntentActivities(intent, resolvedType, flags, userId);
return chooseBestActivity(intent, resolvedType, flags, query, userId);
}
@@ -2294,6 +2333,7 @@ public class PackageManagerService extends IPackageManager.Stub {
ResolveInfo findPreferredActivity(Intent intent, String resolvedType,
int flags, List<ResolveInfo> query, int priority, int userId) {
+ if (!sUserManager.exists(userId)) return null;
// writer
synchronized (mPackages) {
if (intent.getSelector() != null) {
@@ -2389,6 +2429,7 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public List<ResolveInfo> queryIntentActivities(Intent intent,
String resolvedType, int flags, int userId) {
+ if (!sUserManager.exists(userId)) return null;
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -2427,6 +2468,7 @@ public class PackageManagerService extends IPackageManager.Stub {
public List<ResolveInfo> queryIntentActivityOptions(ComponentName caller,
Intent[] specifics, String[] specificTypes, Intent intent,
String resolvedType, int flags, int userId) {
+ if (!sUserManager.exists(userId)) return null;
final String resultsAction = intent.getAction();
List<ResolveInfo> results = queryIntentActivities(intent, resolvedType, flags
@@ -2596,6 +2638,7 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags,
int userId) {
+ if (!sUserManager.exists(userId)) return null;
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -2632,6 +2675,7 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) {
List<ResolveInfo> query = queryIntentServices(intent, resolvedType, flags, userId);
+ if (!sUserManager.exists(userId)) return null;
if (query != null) {
if (query.size() >= 1) {
// If there is more than one service with the same priority,
@@ -2645,6 +2689,7 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public List<ResolveInfo> queryIntentServices(Intent intent, String resolvedType, int flags,
int userId) {
+ if (!sUserManager.exists(userId)) return null;
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -2723,7 +2768,7 @@ public class PackageManagerService extends IPackageManager.Stub {
} else {
final PackageParser.Package p = mPackages.get(packageName);
if (p != null) {
- pi = generatePackageInfo(p, flags);
+ pi = generatePackageInfo(p, flags, userId);
}
}
@@ -2743,6 +2788,7 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public ParceledListSlice<ApplicationInfo> getInstalledApplications(int flags,
String lastRead, int userId) {
+ if (!sUserManager.exists(userId)) return null;
final ParceledListSlice<ApplicationInfo> list = new ParceledListSlice<ApplicationInfo>();
final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0;
final String[] keys;
@@ -2763,15 +2809,16 @@ public class PackageManagerService extends IPackageManager.Stub {
final String packageName = keys[i++];
ApplicationInfo ai = null;
+ final PackageSetting ps = mSettings.mPackages.get(packageName);
if (listUninstalled) {
- final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
ai = generateApplicationInfoFromSettingsLPw(ps.name, flags, userId);
}
} else {
final PackageParser.Package p = mPackages.get(packageName);
- if (p != null) {
- ai = PackageParser.generateApplicationInfo(p, flags, userId);
+ if (p != null && ps != null) {
+ ai = PackageParser.generateApplicationInfo(p, flags, ps.getStopped(userId),
+ ps.getEnabled(userId), userId);
}
}
@@ -2794,13 +2841,17 @@ public class PackageManagerService extends IPackageManager.Stub {
// reader
synchronized (mPackages) {
final Iterator<PackageParser.Package> i = mPackages.values().iterator();
- final int userId = UserId.getUserId(Binder.getCallingUid());
+ final int userId = UserId.getCallingUserId();
while (i.hasNext()) {
final PackageParser.Package p = i.next();
if (p.applicationInfo != null
&& (p.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) != 0
&& (!mSafeMode || isSystemApp(p))) {
- finalList.add(PackageParser.generateApplicationInfo(p, flags, userId));
+ PackageSetting ps = mSettings.mPackages.get(p.packageName);
+ finalList.add(PackageParser.generateApplicationInfo(p, flags,
+ ps != null ? ps.getStopped(userId) : false,
+ ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
+ userId));
}
}
}
@@ -2810,14 +2861,21 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public ProviderInfo resolveContentProvider(String name, int flags, int userId) {
+ if (!sUserManager.exists(userId)) return null;
// reader
synchronized (mPackages) {
final PackageParser.Provider provider = mProviders.get(name);
+ PackageSetting ps = provider != null
+ ? mSettings.mPackages.get(provider.owner.packageName)
+ : null;
return provider != null
&& mSettings.isEnabledLPr(provider.info, flags, userId)
&& (!mSafeMode || (provider.info.applicationInfo.flags
&ApplicationInfo.FLAG_SYSTEM) != 0)
- ? PackageParser.generateProviderInfo(provider, flags, userId)
+ ? PackageParser.generateProviderInfo(provider, flags,
+ ps != null ? ps.getStopped(userId) : false,
+ ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
+ userId)
: null;
}
}
@@ -2831,16 +2889,20 @@ public class PackageManagerService extends IPackageManager.Stub {
synchronized (mPackages) {
final Iterator<Map.Entry<String, PackageParser.Provider>> i = mProviders.entrySet()
.iterator();
- final int userId = UserId.getUserId(Binder.getCallingUid());
+ final int userId = UserId.getCallingUserId();
while (i.hasNext()) {
Map.Entry<String, PackageParser.Provider> entry = i.next();
PackageParser.Provider p = entry.getValue();
+ PackageSetting ps = mSettings.mPackages.get(p.owner.packageName);
if (p.syncable
&& (!mSafeMode || (p.info.applicationInfo.flags
&ApplicationInfo.FLAG_SYSTEM) != 0)) {
outNames.add(entry.getKey());
- outInfo.add(PackageParser.generateProviderInfo(p, 0, userId));
+ outInfo.add(PackageParser.generateProviderInfo(p, 0,
+ ps != null ? ps.getStopped(userId) : false,
+ ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
+ userId));
}
}
}
@@ -2857,6 +2919,7 @@ public class PackageManagerService extends IPackageManager.Stub {
UserId.getUserId(uid) : UserId.getCallingUserId();
while (i.hasNext()) {
final PackageParser.Provider p = i.next();
+ PackageSetting ps = mSettings.mPackages.get(p.owner.packageName);
if (p.info.authority != null
&& (processName == null
|| (p.info.processName.equals(processName)
@@ -2867,7 +2930,10 @@ public class PackageManagerService extends IPackageManager.Stub {
if (finalList == null) {
finalList = new ArrayList<ProviderInfo>(3);
}
- finalList.add(PackageParser.generateProviderInfo(p, flags, userId));
+ finalList.add(PackageParser.generateProviderInfo(p, flags,
+ ps != null ? ps.getStopped(userId) : false,
+ ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
+ userId));
}
}
}
@@ -3511,7 +3577,7 @@ public class PackageManagerService extends IPackageManager.Stub {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
}
- pkg.applicationInfo.uid = pkgSetting.uid;
+ pkg.applicationInfo.uid = pkgSetting.appId;
pkg.mExtras = pkgSetting;
if (!verifySignaturesLP(pkgSetting, pkg)) {
@@ -3618,7 +3684,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if (ret >= 0) {
// TODO: Kill the processes first
// Remove the data directories for all users
- mUserManager.removePackageForAllUsers(pkgName);
+ sUserManager.removePackageForAllUsers(pkgName);
// Old data gone!
String msg = "System package " + pkg.packageName
+ " has changed from uid: "
@@ -3639,7 +3705,7 @@ public class PackageManagerService extends IPackageManager.Stub {
return null;
}
// Create data directories for all users
- mUserManager.installPackageForAllUsers(pkgName,
+ sUserManager.installPackageForAllUsers(pkgName,
pkg.applicationInfo.uid);
}
if (!recovered) {
@@ -3681,7 +3747,7 @@ public class PackageManagerService extends IPackageManager.Stub {
return null;
}
// Create data directories for all users
- mUserManager.installPackageForAllUsers(pkgName, pkg.applicationInfo.uid);
+ sUserManager.installPackageForAllUsers(pkgName, pkg.applicationInfo.uid);
if (dataPath.exists()) {
pkg.applicationInfo.dataDir = dataPath.getPath();
@@ -4510,12 +4576,14 @@ public class PackageManagerService extends IPackageManager.Stub {
extends IntentResolver<PackageParser.ActivityIntentInfo, ResolveInfo> {
public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
boolean defaultOnly, int userId) {
+ if (!sUserManager.exists(userId)) return null;
mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
return super.queryIntent(intent, resolvedType, defaultOnly, userId);
}
public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags,
int userId) {
+ if (!sUserManager.exists(userId)) return null;
mFlags = flags;
return super.queryIntent(intent, resolvedType,
(flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId);
@@ -4523,6 +4591,7 @@ public class PackageManagerService extends IPackageManager.Stub {
public List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
int flags, ArrayList<PackageParser.Activity> packageActivities, int userId) {
+ if (!sUserManager.exists(userId)) return null;
if (packageActivities == null) {
return null;
}
@@ -4605,6 +4674,7 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
protected boolean isFilterStopped(PackageParser.ActivityIntentInfo filter, int userId) {
+ if (!sUserManager.exists(userId)) return true;
PackageParser.Package p = filter.activity.owner;
if (p != null) {
PackageSetting ps = (PackageSetting)p.mExtras;
@@ -4626,6 +4696,7 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
protected ResolveInfo newResult(PackageParser.ActivityIntentInfo info,
int match, int userId) {
+ if (!sUserManager.exists(userId)) return null;
if (!mSettings.isEnabledLPr(info.activity.info, mFlags, userId)) {
return null;
}
@@ -4635,7 +4706,11 @@ public class PackageManagerService extends IPackageManager.Stub {
return null;
}
final ResolveInfo res = new ResolveInfo();
- res.activityInfo = PackageParser.generateActivityInfo(activity, mFlags, userId);
+ PackageSetting ps = (PackageSetting) activity.owner.mExtras;
+ res.activityInfo = PackageParser.generateActivityInfo(activity, mFlags,
+ ps != null ? ps.getStopped(userId) : false,
+ ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
+ userId);
if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
res.filter = info;
}
@@ -4696,6 +4771,7 @@ public class PackageManagerService extends IPackageManager.Stub {
public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags,
int userId) {
+ if (!sUserManager.exists(userId)) return null;
mFlags = flags;
return super.queryIntent(intent, resolvedType,
(flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId);
@@ -4703,6 +4779,7 @@ public class PackageManagerService extends IPackageManager.Stub {
public List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
int flags, ArrayList<PackageParser.Service> packageServices, int userId) {
+ if (!sUserManager.exists(userId)) return null;
if (packageServices == null) {
return null;
}
@@ -4780,6 +4857,7 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
protected boolean isFilterStopped(PackageParser.ServiceIntentInfo filter, int userId) {
+ if (!sUserManager.exists(userId)) return true;
PackageParser.Package p = filter.service.owner;
if (p != null) {
PackageSetting ps = (PackageSetting)p.mExtras;
@@ -4802,6 +4880,7 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
protected ResolveInfo newResult(PackageParser.ServiceIntentInfo filter,
int match, int userId) {
+ if (!sUserManager.exists(userId)) return null;
final PackageParser.ServiceIntentInfo info = (PackageParser.ServiceIntentInfo)filter;
if (!mSettings.isEnabledLPr(info.service.info, mFlags, userId)) {
return null;
@@ -4812,7 +4891,11 @@ public class PackageManagerService extends IPackageManager.Stub {
return null;
}
final ResolveInfo res = new ResolveInfo();
- res.serviceInfo = PackageParser.generateServiceInfo(service, mFlags, userId);
+ PackageSetting ps = (PackageSetting) service.owner.mExtras;
+ res.serviceInfo = PackageParser.generateServiceInfo(service, mFlags,
+ ps != null ? ps.getStopped(userId) : false,
+ ps != null ? ps.getEnabled(userId) : COMPONENT_ENABLED_STATE_DEFAULT,
+ userId);
if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
res.filter = filter;
}
@@ -4903,23 +4986,32 @@ public class PackageManagerService extends IPackageManager.Stub {
};
static final void sendPackageBroadcast(String action, String pkg,
- Bundle extras, String targetPkg, IIntentReceiver finishedReceiver) {
+ Bundle extras, String targetPkg, IIntentReceiver finishedReceiver, int userId) {
IActivityManager am = ActivityManagerNative.getDefault();
if (am != null) {
try {
- final Intent intent = new Intent(action,
- pkg != null ? Uri.fromParts("package", pkg, null) : null);
- if (extras != null) {
- intent.putExtras(extras);
- }
- if (targetPkg != null) {
- intent.setPackage(targetPkg);
- }
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- // TODO: Fix the userId argument
- am.broadcastIntent(null, intent, null, finishedReceiver,
- 0, null, null, null, finishedReceiver != null, false,
- Binder.getOrigCallingUser());
+ int[] userIds = userId == UserId.USER_ALL
+ ? sUserManager.getUserIds()
+ : new int[] {userId};
+ for (int id : userIds) {
+ final Intent intent = new Intent(action,
+ pkg != null ? Uri.fromParts("package", pkg, null) : null);
+ if (extras != null) {
+ intent.putExtras(extras);
+ }
+ if (targetPkg != null) {
+ intent.setPackage(targetPkg);
+ }
+ // Modify the UID when posting to other users
+ int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ if (uid > 0 && id > 0) {
+ uid = UserId.getUid(id, UserId.getAppId(uid));
+ intent.putExtra(Intent.EXTRA_UID, uid);
+ }
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ am.broadcastIntent(null, intent, null, finishedReceiver,
+ 0, null, null, null, finishedReceiver != null, false, id);
+ }
} catch (RemoteException ex) {
}
}
@@ -5062,13 +5154,13 @@ public class PackageManagerService extends IPackageManager.Stub {
extras.putInt(Intent.EXTRA_UID, removedUid);
extras.putBoolean(Intent.EXTRA_DATA_REMOVED, false);
sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage,
- extras, null, null);
+ extras, null, null, UserId.USER_ALL);
}
if (addedPackage != null) {
Bundle extras = new Bundle(1);
extras.putInt(Intent.EXTRA_UID, addedUid);
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, addedPackage,
- extras, null, null);
+ extras, null, null, UserId.USER_ALL);
}
}
@@ -7092,11 +7184,11 @@ public class PackageManagerService extends IPackageManager.Stub {
extras.putBoolean(Intent.EXTRA_REPLACING, true);
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
- extras, null, null);
+ extras, null, null, UserId.USER_ALL);
sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
- extras, null, null);
+ extras, null, null, UserId.USER_ALL);
sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null,
- null, packageName, null);
+ null, packageName, null, UserId.USER_ALL);
}
}
// Force a gc here.
@@ -7129,14 +7221,15 @@ public class PackageManagerService extends IPackageManager.Stub {
}
if (removedPackage != null) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage,
- extras, null, null);
+ extras, null, null, UserId.USER_ALL);
if (fullRemove && !replacing) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED, removedPackage,
- extras, null, null);
+ extras, null, null, UserId.USER_ALL);
}
}
if (removedUid >= 0) {
- sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras, null, null);
+ sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras, null, null,
+ UserId.getUserId(removedUid));
}
}
}
@@ -7168,7 +7261,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// we don't consider this to be a failure of the core package deletion
} else {
// TODO: Kill the processes first
- mUserManager.removePackageForAllUsers(packageName);
+ sUserManager.removePackageForAllUsers(packageName);
}
schedulePackageCleaning(packageName);
}
@@ -7732,12 +7825,14 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public void setApplicationEnabledSetting(String appPackageName,
int newState, int flags, int userId) {
+ if (!sUserManager.exists(userId)) return;
setEnabledSetting(appPackageName, null, newState, flags, userId);
}
@Override
public void setComponentEnabledSetting(ComponentName componentName,
int newState, int flags, int userId) {
+ if (!sUserManager.exists(userId)) return;
setEnabledSetting(componentName.getPackageName(),
componentName.getClassName(), newState, flags, userId);
}
@@ -7776,11 +7871,11 @@ public class PackageManagerService extends IPackageManager.Stub {
+ "/" + className);
}
// Allow root and verify that userId is not being specified by a different user
- if (!allowedByPermission && !UserId.isSameApp(uid, pkgSetting.uid)) {
+ if (!allowedByPermission && !UserId.isSameApp(uid, pkgSetting.appId)) {
throw new SecurityException(
"Permission Denial: attempt to change component state from pid="
+ Binder.getCallingPid()
- + ", uid=" + uid + ", package uid=" + pkgSetting.uid);
+ + ", uid=" + uid + ", package uid=" + pkgSetting.appId);
}
if (className == null) {
// We're dealing with an application/package level state change
@@ -7789,7 +7884,7 @@ public class PackageManagerService extends IPackageManager.Stub {
return;
}
pkgSetting.setEnabled(newState, userId);
- pkgSetting.pkg.mSetEnabled = newState;
+ // pkgSetting.pkg.mSetEnabled = newState;
} else {
// We're dealing with a component level state change
// First, verify that this is a valid class name.
@@ -7825,7 +7920,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
mSettings.writePackageRestrictionsLPr(userId);
- packageUid = pkgSetting.uid;
+ packageUid = UserId.getUid(userId, pkgSetting.appId);
components = mPendingBroadcasts.get(packageName);
final boolean newPackage = components == null;
if (newPackage) {
@@ -7873,10 +7968,12 @@ public class PackageManagerService extends IPackageManager.Stub {
extras.putStringArray(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, nameList);
extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, killFlag);
extras.putInt(Intent.EXTRA_UID, packageUid);
- sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, null, null);
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, null, null,
+ UserId.getUserId(packageUid));
}
public void setPackageStoppedState(String packageName, boolean stopped, int userId) {
+ if (!sUserManager.exists(userId)) return;
final int uid = Binder.getCallingUid();
final int permission = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
@@ -7900,6 +7997,7 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public int getApplicationEnabledSetting(String packageName, int userId) {
+ if (!sUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED;
int uid = Binder.getCallingUid();
checkValidCaller(uid, userId);
// reader
@@ -7910,6 +8008,7 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public int getComponentEnabledSetting(ComponentName componentName, int userId) {
+ if (!sUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED;
int uid = Binder.getCallingUid();
checkValidCaller(uid, userId);
// reader
@@ -8383,7 +8482,7 @@ public class PackageManagerService extends IPackageManager.Stub {
+ " at code path: " + ps.codePathString);
// We do have a valid package installed on sdcard
processCids.put(args, ps.codePathString);
- int uid = ps.uid;
+ int uid = ps.appId;
if (uid != -1) {
uidList[num++] = uid;
}
@@ -8436,7 +8535,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
: Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
- sendPackageBroadcast(action, null, extras, null, finishedReceiver);
+ sendPackageBroadcast(action, null, extras, null, finishedReceiver, UserId.USER_ALL);
}
}
@@ -8860,8 +8959,12 @@ public class PackageManagerService extends IPackageManager.Stub {
// TODO(kroot): Add a real permission for creating users
enforceSystemOrRoot("Only the system can create users");
- // TODO(kroot): fix this API
- UserInfo userInfo = mUserManager.createUser(name, flags, new ArrayList<ApplicationInfo>());
+ UserInfo userInfo = sUserManager.createUser(name, flags);
+ if (userInfo != null) {
+ Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
+ addedIntent.putExtra(Intent.EXTRA_USERID, userInfo.id);
+ mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_ACCOUNTS);
+ }
return userInfo;
}
@@ -8869,13 +8972,34 @@ public class PackageManagerService extends IPackageManager.Stub {
// TODO(kroot): Add a real permission for removing users
enforceSystemOrRoot("Only the system can remove users");
- if (userId == 0) {
+ if (userId == 0 || !sUserManager.exists(userId)) {
return false;
}
- mUserManager.removeUser(userId);
+
+ cleanUpUser(userId);
+
+ if (sUserManager.removeUser(userId)) {
+ // Let other services shutdown any activity
+ Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED);
+ addedIntent.putExtra(Intent.EXTRA_USERID, userId);
+ mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_ACCOUNTS);
+ }
+ sUserManager.removePackageFolders(userId);
return true;
}
+ private void cleanUpUser(int userId) {
+ // Disable all the packages for the user first
+ synchronized (mPackages) {
+ Set<Entry<String, PackageSetting>> entries = mSettings.mPackages.entrySet();
+ for (Entry<String, PackageSetting> entry : entries) {
+ entry.getValue().removeUser(userId);
+ }
+ if (mDirtyUsers.remove(userId));
+ mSettings.removeUserLPr(userId);
+ }
+ }
+
@Override
public VerifierDeviceIdentity getVerifierDeviceIdentity() throws RemoteException {
mContext.enforceCallingOrSelfPermission(
@@ -8887,8 +9011,22 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
+ @Override
public List<UserInfo> getUsers() {
- return mUserManager.getUsers();
+ enforceSystemOrRoot("Only the system can query users");
+ return sUserManager.getUsers();
+ }
+
+ @Override
+ public UserInfo getUser(int userId) {
+ enforceSystemOrRoot("Only the system can remove users");
+ return sUserManager.getUser(userId);
+ }
+
+ @Override
+ public void updateUserName(int userId, String name) {
+ enforceSystemOrRoot("Only the system can rename users");
+ sUserManager.updateUserName(userId, name);
}
@Override
diff --git a/services/java/com/android/server/pm/PackageSetting.java b/services/java/com/android/server/pm/PackageSetting.java
index 48ed9bf..f7f0870 100644
--- a/services/java/com/android/server/pm/PackageSetting.java
+++ b/services/java/com/android/server/pm/PackageSetting.java
@@ -24,7 +24,7 @@ import java.io.File;
* Settings data for a particular package we know about.
*/
final class PackageSetting extends PackageSettingBase {
- int uid;
+ int appId;
PackageParser.Package pkg;
SharedUserSetting sharedUser;
@@ -41,7 +41,7 @@ final class PackageSetting extends PackageSettingBase {
PackageSetting(PackageSetting orig) {
super(orig);
- uid = orig.uid;
+ appId = orig.appId;
pkg = orig.pkg;
sharedUser = orig.sharedUser;
}
@@ -50,6 +50,6 @@ final class PackageSetting extends PackageSettingBase {
public String toString() {
return "PackageSetting{"
+ Integer.toHexString(System.identityHashCode(this))
- + " " + name + "/" + uid + "}";
+ + " " + name + "/" + appId + "}";
}
} \ No newline at end of file
diff --git a/services/java/com/android/server/pm/PackageSettingBase.java b/services/java/com/android/server/pm/PackageSettingBase.java
index b7cf8d6..56f2166 100644
--- a/services/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/java/com/android/server/pm/PackageSettingBase.java
@@ -273,4 +273,13 @@ class PackageSettingBase extends GrantedPermissions {
return COMPONENT_ENABLED_STATE_DEFAULT;
}
}
+
+ void removeUser(int userId) {
+ enabled.delete(userId);
+ stopped.delete(userId);
+ enabledComponents.delete(userId);
+ disabledComponents.delete(userId);
+ notLaunched.delete(userId);
+ }
+
}
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index b541c8c..bb7f4fc 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -275,7 +275,7 @@ final class Settings {
p.pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
}
PackageSetting ret = addPackageLPw(name, p.realName, p.codePath, p.resourcePath,
- p.nativeLibraryPathString, p.uid, p.versionCode, p.pkgFlags);
+ p.nativeLibraryPathString, p.appId, p.versionCode, p.pkgFlags);
mDisabledSysPackages.remove(name);
return ret;
}
@@ -284,7 +284,7 @@ final class Settings {
String nativeLibraryPathString, int uid, int vc, int pkgFlags) {
PackageSetting p = mPackages.get(name);
if (p != null) {
- if (p.uid == uid) {
+ if (p.appId == uid) {
return p;
}
PackageManagerService.reportSettingsProblem(Log.ERROR,
@@ -293,7 +293,7 @@ final class Settings {
}
p = new PackageSetting(name, realName, codePath, resourcePath, nativeLibraryPathString,
vc, pkgFlags);
- p.uid = uid;
+ p.appId = uid;
if (addUserIdLPw(uid, p, name)) {
mPackages.put(name, p);
return p;
@@ -407,7 +407,7 @@ final class Settings {
p.copyFrom(origPackage);
p.signatures = s;
p.sharedUser = origPackage.sharedUser;
- p.uid = origPackage.uid;
+ p.appId = origPackage.appId;
p.origPackage = origPackage;
mRenamedPackages.put(name, origPackage.name);
name = origPackage.name;
@@ -435,7 +435,7 @@ final class Settings {
}
}
if (sharedUser != null) {
- p.uid = sharedUser.userId;
+ p.appId = sharedUser.userId;
} else {
// Clone the setting here for disabled system packages
PackageSetting dis = mDisabledSysPackages.get(name);
@@ -447,7 +447,7 @@ final class Settings {
if (dis.signatures.mSignatures != null) {
p.signatures.mSignatures = dis.signatures.mSignatures.clone();
}
- p.uid = dis.uid;
+ p.appId = dis.appId;
// Clone permissions
p.grantedPermissions = new HashSet<String>(dis.grantedPermissions);
// Clone component info
@@ -464,14 +464,14 @@ final class Settings {
}
}
// Add new setting to list of user ids
- addUserIdLPw(p.uid, p, name);
+ addUserIdLPw(p.appId, p, name);
} else {
// Assign new user id
- p.uid = newUserIdLPw(p);
+ p.appId = newUserIdLPw(p);
}
}
}
- if (p.uid < 0) {
+ if (p.appId < 0) {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Package " + name + " could not be assigned a valid uid");
return null;
@@ -539,9 +539,9 @@ final class Settings {
+ p.sharedUser + " but is now " + sharedUser
+ "; I am not changing its files so it will probably fail!");
p.sharedUser.packages.remove(p);
- } else if (p.uid != sharedUser.userId) {
+ } else if (p.appId != sharedUser.userId) {
PackageManagerService.reportSettingsProblem(Log.ERROR,
- "Package " + p.name + " was user id " + p.uid
+ "Package " + p.name + " was user id " + p.appId
+ " but is now user " + sharedUser
+ " with id " + sharedUser.userId
+ "; I am not changing its files so it will probably fail!");
@@ -549,7 +549,7 @@ final class Settings {
sharedUser.packages.add(p);
p.sharedUser = sharedUser;
- p.uid = sharedUser.userId;
+ p.appId = sharedUser.userId;
}
}
@@ -614,8 +614,8 @@ final class Settings {
return p.sharedUser.userId;
}
} else {
- removeUserIdLPw(p.uid);
- return p.uid;
+ removeUserIdLPw(p.appId);
+ return p.appId;
}
}
return -1;
@@ -628,7 +628,7 @@ final class Settings {
p.sharedUser.packages.remove(p);
p.sharedUser.packages.add(newp);
} else {
- replaceUserIdLPw(p.uid, newp);
+ replaceUserIdLPw(p.appId, newp);
}
}
mPackages.put(name, newp);
@@ -1317,9 +1317,9 @@ final class Settings {
serializer.attribute(null, "nativeLibraryPath", pkg.nativeLibraryPathString);
}
if (pkg.sharedUser == null) {
- serializer.attribute(null, "userId", Integer.toString(pkg.uid));
+ serializer.attribute(null, "userId", Integer.toString(pkg.appId));
} else {
- serializer.attribute(null, "sharedUserId", Integer.toString(pkg.uid));
+ serializer.attribute(null, "sharedUserId", Integer.toString(pkg.appId));
}
serializer.startTag(null, "perms");
if (pkg.sharedUser == null) {
@@ -1364,9 +1364,9 @@ final class Settings {
serializer.attribute(null, "ut", Long.toHexString(pkg.lastUpdateTime));
serializer.attribute(null, "version", String.valueOf(pkg.versionCode));
if (pkg.sharedUser == null) {
- serializer.attribute(null, "userId", Integer.toString(pkg.uid));
+ serializer.attribute(null, "userId", Integer.toString(pkg.appId));
} else {
- serializer.attribute(null, "sharedUserId", Integer.toString(pkg.uid));
+ serializer.attribute(null, "sharedUserId", Integer.toString(pkg.appId));
}
if (pkg.uidError) {
serializer.attribute(null, "uidError", "true");
@@ -1607,7 +1607,7 @@ final class Settings {
final Iterator<PackageSetting> disabledIt = mDisabledSysPackages.values().iterator();
while (disabledIt.hasNext()) {
final PackageSetting disabledPs = disabledIt.next();
- final Object id = getUserIdLPr(disabledPs.uid);
+ final Object id = getUserIdLPr(disabledPs.appId);
if (id != null && id instanceof SharedUserSetting) {
disabledPs.sharedUser = (SharedUserSetting) id;
}
@@ -1753,10 +1753,10 @@ final class Settings {
}
}
String idStr = parser.getAttributeValue(null, "userId");
- ps.uid = idStr != null ? Integer.parseInt(idStr) : 0;
- if (ps.uid <= 0) {
+ ps.appId = idStr != null ? Integer.parseInt(idStr) : 0;
+ if (ps.appId <= 0) {
String sharedIdStr = parser.getAttributeValue(null, "sharedUserId");
- ps.uid = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0;
+ ps.appId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0;
}
int outerDepth = parser.getDepth();
int type;
@@ -2164,6 +2164,13 @@ final class Settings {
}
}
+ void removeUserLPr(int userId) {
+ File file = getUserPackagesStateFile(userId);
+ file.delete();
+ file = getUserPackagesStateBackupFile(userId);
+ file.delete();
+ }
+
// Returns -1 if we could not find an available UserId to assign
private int newUserIdLPw(Object obj) {
// Let's be stupidly inefficient for now...
@@ -2265,11 +2272,11 @@ final class Settings {
if (pkgSetting == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
- if (!allowedByPermission && (appId != pkgSetting.uid)) {
+ if (!allowedByPermission && (appId != pkgSetting.appId)) {
throw new SecurityException(
"Permission Denial: attempt to change stopped state from pid="
+ Binder.getCallingPid()
- + ", uid=" + uid + ", package uid=" + pkgSetting.uid);
+ + ", uid=" + uid + ", package uid=" + pkgSetting.appId);
}
if (DEBUG_STOPPED) {
if (stopped) {
@@ -2285,7 +2292,7 @@ final class Settings {
if (pkgSetting.installerPackageName != null) {
PackageManagerService.sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH,
pkgSetting.name, null,
- pkgSetting.installerPackageName, null);
+ pkgSetting.installerPackageName, null, userId);
}
pkgSetting.setNotLaunched(false, userId);
}
@@ -2369,7 +2376,7 @@ final class Settings {
pw.println(ps.name);
}
- pw.print(" userId="); pw.print(ps.uid);
+ pw.print(" userId="); pw.print(ps.appId);
pw.print(" gids="); pw.println(PackageManagerService.arrayToString(ps.gids));
pw.print(" sharedUser="); pw.println(ps.sharedUser);
pw.print(" pkg="); pw.println(ps.pkg);
@@ -2513,7 +2520,7 @@ final class Settings {
pw.println(ps.name);
}
pw.print(" userId=");
- pw.println(ps.uid);
+ pw.println(ps.appId);
pw.print(" sharedUser=");
pw.println(ps.sharedUser);
pw.print(" codePath=");
diff --git a/services/java/com/android/server/pm/UserManager.java b/services/java/com/android/server/pm/UserManager.java
index 959e570..4e9e666 100644
--- a/services/java/com/android/server/pm/UserManager.java
+++ b/services/java/com/android/server/pm/UserManager.java
@@ -16,6 +16,7 @@
package com.android.server.pm;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
import android.content.pm.ApplicationInfo;
@@ -58,7 +59,7 @@ public class UserManager {
private static final String USER_INFO_DIR = "system" + File.separator + "users";
private static final String USER_LIST_FILENAME = "userlist.xml";
- private SparseArray<UserInfo> mUsers;
+ private SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>();
private final File mUsersDir;
private final File mUserListFile;
@@ -91,11 +92,36 @@ public class UserManager {
}
public List<UserInfo> getUsers() {
- ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
- for (int i = 0; i < mUsers.size(); i++) {
- users.add(mUsers.valueAt(i));
+ synchronized (mUsers) {
+ ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
+ for (int i = 0; i < mUsers.size(); i++) {
+ users.add(mUsers.valueAt(i));
+ }
+ return users;
+ }
+ }
+
+ public UserInfo getUser(int userId) {
+ synchronized (mUsers) {
+ UserInfo info = mUsers.get(userId);
+ return info;
+ }
+ }
+
+ public boolean exists(int userId) {
+ synchronized (mUsers) {
+ return ArrayUtils.contains(mUserIds, userId);
+ }
+ }
+
+ public void updateUserName(int userId, String name) {
+ synchronized (mUsers) {
+ UserInfo info = mUsers.get(userId);
+ if (name != null && !name.equals(info.name)) {
+ info.name = name;
+ writeUserLocked(info);
+ }
}
- return users;
}
/**
@@ -108,9 +134,14 @@ public class UserManager {
}
private void readUserList() {
- mUsers = new SparseArray<UserInfo>();
+ synchronized (mUsers) {
+ readUserListLocked();
+ }
+ }
+
+ private void readUserListLocked() {
if (!mUserListFile.exists()) {
- fallbackToSingleUser();
+ fallbackToSingleUserLocked();
return;
}
FileInputStream fis = null;
@@ -126,7 +157,7 @@ public class UserManager {
if (type != XmlPullParser.START_TAG) {
Slog.e(LOG_TAG, "Unable to read user list");
- fallbackToSingleUser();
+ fallbackToSingleUserLocked();
return;
}
@@ -139,11 +170,11 @@ public class UserManager {
}
}
}
- updateUserIds();
+ updateUserIdsLocked();
} catch (IOException ioe) {
- fallbackToSingleUser();
+ fallbackToSingleUserLocked();
} catch (XmlPullParserException pe) {
- fallbackToSingleUser();
+ fallbackToSingleUserLocked();
} finally {
if (fis != null) {
try {
@@ -154,15 +185,15 @@ public class UserManager {
}
}
- private void fallbackToSingleUser() {
+ private void fallbackToSingleUserLocked() {
// Create the primary user
UserInfo primary = new UserInfo(0, "Primary",
UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY);
mUsers.put(0, primary);
- updateUserIds();
+ updateUserIdsLocked();
- writeUserList();
- writeUser(primary);
+ writeUserListLocked();
+ writeUserLocked(primary);
}
/*
@@ -172,7 +203,7 @@ public class UserManager {
* <name>Primary</name>
* </user>
*/
- private void writeUser(UserInfo userInfo) {
+ private void writeUserLocked(UserInfo userInfo) {
FileOutputStream fos = null;
try {
final File mUserFile = new File(mUsersDir, userInfo.id + ".xml");
@@ -216,7 +247,7 @@ public class UserManager {
* <user id="2"></user>
* </users>
*/
- private void writeUserList() {
+ private void writeUserListLocked() {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(mUserListFile);
@@ -309,17 +340,19 @@ public class UserManager {
return null;
}
- public UserInfo createUser(String name, int flags, List<ApplicationInfo> apps) {
+ public UserInfo createUser(String name, int flags) {
int userId = getNextAvailableId();
UserInfo userInfo = new UserInfo(userId, name, flags);
File userPath = new File(mBaseUserPath, Integer.toString(userId));
- if (!createPackageFolders(userId, userPath, apps)) {
+ if (!createPackageFolders(userId, userPath)) {
return null;
}
- mUsers.put(userId, userInfo);
- writeUserList();
- writeUser(userInfo);
- updateUserIds();
+ synchronized (mUsers) {
+ mUsers.put(userId, userInfo);
+ writeUserListLocked();
+ writeUserLocked(userInfo);
+ updateUserIdsLocked();
+ }
return userInfo;
}
@@ -328,7 +361,13 @@ public class UserManager {
* after the user's processes have been terminated.
* @param id the user's id
*/
- public void removeUser(int id) {
+ public boolean removeUser(int id) {
+ synchronized (mUsers) {
+ return removeUserLocked(id);
+ }
+ }
+
+ private boolean removeUserLocked(int id) {
// Remove from the list
UserInfo userInfo = mUsers.get(id);
if (userInfo != null) {
@@ -338,11 +377,11 @@ public class UserManager {
File userFile = new File(mUsersDir, id + ".xml");
userFile.delete();
// Update the user list
- writeUserList();
- // Remove the data directories for all packages for this user
- removePackageFolders(id);
- updateUserIds();
+ writeUserListLocked();
+ updateUserIdsLocked();
+ return true;
}
+ return false;
}
public void installPackageForAllUsers(String packageName, int uid) {
@@ -376,7 +415,7 @@ public class UserManager {
/**
* Caches the list of user ids in an array, adjusting the array size when necessary.
*/
- private void updateUserIds() {
+ private void updateUserIdsLocked() {
if (mUserIds == null || mUserIds.length != mUsers.size()) {
mUserIds = new int[mUsers.size()];
}
@@ -402,11 +441,10 @@ public class UserManager {
return i;
}
- private boolean createPackageFolders(int id, File userPath, final List<ApplicationInfo> apps) {
+ private boolean createPackageFolders(int id, File userPath) {
// mInstaller may not be available for unit-tests.
- if (mInstaller == null || apps == null) return true;
+ if (mInstaller == null) return true;
- final long startTime = SystemClock.elapsedRealtime();
// Create the user path
userPath.mkdir();
FileUtils.setPermissions(userPath.toString(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
@@ -414,13 +452,10 @@ public class UserManager {
mInstaller.cloneUserData(0, id, false);
- final long stopTime = SystemClock.elapsedRealtime();
- Log.i(LOG_TAG,
- "Time to create " + apps.size() + " packages = " + (stopTime - startTime) + "ms");
return true;
}
- private boolean removePackageFolders(int id) {
+ boolean removePackageFolders(int id) {
// mInstaller may not be available for unit-tests.
if (mInstaller == null) return true;
diff --git a/services/java/com/android/server/wm/AppWindowToken.java b/services/java/com/android/server/wm/AppWindowToken.java
index 1442883..6269420 100644
--- a/services/java/com/android/server/wm/AppWindowToken.java
+++ b/services/java/com/android/server/wm/AppWindowToken.java
@@ -51,7 +51,7 @@ class AppWindowToken extends WindowToken {
int groupId = -1;
boolean appFullscreen;
int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-
+
// The input dispatching timeout for this application token in nanoseconds.
long inputDispatchingTimeoutNanos;
@@ -225,7 +225,7 @@ class AppWindowToken extends WindowToken {
if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG,
"performing show on: " + w);
w.performShowLocked();
- isAnimating |= w.isAnimating();
+ isAnimating |= w.mWinAnimator.isAnimating();
}
return isAnimating;
}
@@ -243,11 +243,11 @@ class AppWindowToken extends WindowToken {
// cache often used attributes locally
final float tmpFloats[] = service.mTmpFloats;
thumbnailTransformation.getMatrix().getValues(tmpFloats);
- if (WindowManagerService.SHOW_TRANSACTIONS) service.logSurface(thumbnail,
+ if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail,
"thumbnail", "POS " + tmpFloats[Matrix.MTRANS_X]
+ ", " + tmpFloats[Matrix.MTRANS_Y], null);
thumbnail.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]);
- if (WindowManagerService.SHOW_TRANSACTIONS) service.logSurface(thumbnail,
+ if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail,
"thumbnail", "alpha=" + thumbnailTransformation.getAlpha()
+ " layer=" + thumbnailLayer
+ " matrix=[" + tmpFloats[Matrix.MSCALE_X]
@@ -358,7 +358,7 @@ class AppWindowToken extends WindowToken {
final int N = windows.size();
for (int i=0; i<N; i++) {
- windows.get(i).finishExit();
+ windows.get(i).mWinAnimator.finishExit();
}
updateReportedVisibilityLocked();
@@ -388,7 +388,7 @@ class AppWindowToken extends WindowToken {
if (WindowManagerService.DEBUG_VISIBILITY) {
Slog.v(WindowManagerService.TAG, "Win " + win + ": isDrawn="
+ win.isDrawnLw()
- + ", isAnimating=" + win.isAnimating());
+ + ", isAnimating=" + win.mWinAnimator.isAnimating());
if (!win.isDrawnLw()) {
Slog.v(WindowManagerService.TAG, "Not displayed: s=" + win.mSurface
+ " pv=" + win.mPolicyVisibility
@@ -398,17 +398,17 @@ class AppWindowToken extends WindowToken {
+ " th="
+ (win.mAppToken != null
? win.mAppToken.hiddenRequested : false)
- + " a=" + win.mAnimating);
+ + " a=" + win.mWinAnimator.mAnimating);
}
}
numInteresting++;
if (win.isDrawnLw()) {
numDrawn++;
- if (!win.isAnimating()) {
+ if (!win.mWinAnimator.isAnimating()) {
numVisible++;
}
nowGone = false;
- } else if (win.isAnimating()) {
+ } else if (win.mWinAnimator.isAnimating()) {
nowGone = false;
}
}
diff --git a/services/java/com/android/server/wm/DimAnimator.java b/services/java/com/android/server/wm/DimAnimator.java
index 85495ea..d8f2254 100644
--- a/services/java/com/android/server/wm/DimAnimator.java
+++ b/services/java/com/android/server/wm/DimAnimator.java
@@ -82,7 +82,7 @@ class DimAnimator {
/**
* Set's the dim surface's layer and update dim parameters that will be used in
- * {@link updateSurface} after all windows are examined.
+ * {@link #updateSurface} after all windows are examined.
*/
void updateParameters(Resources res, WindowState w, long currentTime) {
mDimSurface.setLayer(w.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM);
@@ -94,15 +94,15 @@ class DimAnimator {
// If the desired dim level has changed, then
// start an animation to it.
mLastDimAnimTime = currentTime;
- long duration = (w.mAnimating && w.mAnimation != null)
- ? w.mAnimation.computeDurationHint()
+ long duration = (w.mWinAnimator.mAnimating && w.mWinAnimator.mAnimation != null)
+ ? w.mWinAnimator.mAnimation.computeDurationHint()
: WindowManagerService.DEFAULT_DIM_DURATION;
if (target > mDimTargetAlpha) {
TypedValue tv = new TypedValue();
res.getValue(com.android.internal.R.fraction.config_dimBehindFadeDuration,
tv, true);
if (tv.type == TypedValue.TYPE_FRACTION) {
- duration = (long)tv.getFraction((float)duration, (float)duration);
+ duration = (long)tv.getFraction(duration, duration);
} else if (tv.type >= TypedValue.TYPE_FIRST_INT
&& tv.type <= TypedValue.TYPE_LAST_INT) {
duration = tv.data;
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index fbfd9ec..81e0a17 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -79,7 +79,7 @@ public class WindowAnimator {
}
}
}
-
+
final int NEAT = mService.mExitingAppTokens.size();
for (i=0; i<NEAT; i++) {
final AppWindowToken appToken = mService.mExitingAppTokens.get(i);
@@ -115,6 +115,7 @@ public class WindowAnimator {
for (int i = mService.mWindows.size() - 1; i >= 0; i--) {
WindowState w = mService.mWindows.get(i);
+ WindowStateAnimator winAnimator = w.mWinAnimator;
final WindowManager.LayoutParams attrs = w.mAttrs;
@@ -143,7 +144,7 @@ public class WindowAnimator {
// let's do something.
Animation a = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.window_move_from_decor);
- w.setAnimation(a);
+ winAnimator.setAnimation(a);
w.mAnimDw = w.mLastFrame.left - w.mFrame.left;
w.mAnimDh = w.mLastFrame.top - w.mFrame.top;
} else {
@@ -151,8 +152,8 @@ public class WindowAnimator {
w.mAnimDh = mInnerDh;
}
- final boolean wasAnimating = w.mWasAnimating;
- final boolean nowAnimating = w.stepAnimationLocked(mCurrentTime);
+ final boolean wasAnimating = winAnimator.mWasAnimating;
+ final boolean nowAnimating = winAnimator.stepAnimationLocked(mCurrentTime);
if (WindowManagerService.DEBUG_WALLPAPER) {
Slog.v(TAG, w + ": wasAnimating=" + wasAnimating +
@@ -163,17 +164,17 @@ public class WindowAnimator {
// an animating window and take care of a request to run
// a detached wallpaper animation.
if (nowAnimating) {
- if (w.mAnimation != null) {
+ if (winAnimator.mAnimation != null) {
if ((w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0
- && w.mAnimation.getDetachWallpaper()) {
+ && winAnimator.mAnimation.getDetachWallpaper()) {
mService.mInnerFields.mDetachedWallpaper = w;
}
- if (w.mAnimation.getBackgroundColor() != 0) {
+ if (winAnimator.mAnimation.getBackgroundColor() != 0) {
if (mWindowAnimationBackground == null
|| (w.mAnimLayer < mWindowAnimationBackground.mAnimLayer)) {
mWindowAnimationBackground = w;
mWindowAnimationBackgroundColor =
- w.mAnimation.getBackgroundColor();
+ winAnimator.mAnimation.getBackgroundColor();
}
}
}
@@ -200,7 +201,7 @@ public class WindowAnimator {
}
}
- if (wasAnimating && !w.mAnimating && mService.mWallpaperTarget == w) {
+ if (wasAnimating && !winAnimator.mAnimating && mService.mWallpaperTarget == w) {
mService.mInnerFields.mWallpaperMayChange = true;
mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
@@ -219,7 +220,7 @@ public class WindowAnimator {
mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 3");
}
mService.mFocusMayChange = true;
- } else if (w.isReadyForDisplay() && w.mAnimation == null) {
+ } else if (w.isReadyForDisplay() && winAnimator.mAnimation == null) {
mForceHiding = true;
}
} else if (mPolicy.canBeForceHidden(w, attrs)) {
@@ -241,7 +242,7 @@ public class WindowAnimator {
// clean up later.
Animation a = mPolicy.createForceHideEnterAnimation();
if (a != null) {
- w.setAnimation(a);
+ winAnimator.setAnimation(a);
}
}
if (mCurrentFocus == null || mCurrentFocus.mLayer < w.mLayer) {
@@ -277,7 +278,7 @@ public class WindowAnimator {
WindowManagerService.DEBUG_ORIENTATION) {
Slog.v(TAG, "Eval win " + w + ": isDrawn="
+ w.isDrawnLw()
- + ", isAnimating=" + w.isAnimating());
+ + ", isAnimating=" + winAnimator.isAnimating());
if (!w.isDrawnLw()) {
Slog.v(TAG, "Not displayed: s=" + w.mSurface
+ " pv=" + w.mPolicyVisibility
@@ -285,7 +286,7 @@ public class WindowAnimator {
+ " cdp=" + w.mCommitDrawPending
+ " ah=" + w.mAttachedHidden
+ " th=" + atoken.hiddenRequested
- + " a=" + w.mAnimating);
+ + " a=" + winAnimator.mAnimating);
}
}
if (w != atoken.startingWindow) {
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 3ecbf77..9635b33 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -89,6 +89,7 @@ import android.os.TokenWatcher;
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.EventLog;
+import android.util.FloatMath;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
@@ -1237,7 +1238,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (highestTarget != null) {
if (DEBUG_INPUT_METHOD) Slog.v(TAG, "mNextAppTransition="
+ mNextAppTransition + " " + highestTarget
- + " animating=" + highestTarget.isAnimating()
+ + " animating=" + highestTarget.mWinAnimator.isAnimating()
+ " layer=" + highestTarget.mAnimLayer
+ " new layer=" + w.mAnimLayer);
@@ -1247,7 +1248,7 @@ public class WindowManagerService extends IWindowManager.Stub
mInputMethodTargetWaitingAnim = true;
mInputMethodTarget = highestTarget;
return highestPos + 1;
- } else if (highestTarget.isAnimating() &&
+ } else if (highestTarget.mWinAnimator.isAnimating() &&
highestTarget.mAnimLayer > w.mAnimLayer) {
// If the window we are currently targeting is involved
// with an animation, and it is on top of the next target
@@ -1600,7 +1601,7 @@ public class WindowManagerService extends IWindowManager.Stub
foundI = i;
if (w == mWallpaperTarget && ((w.mAppToken != null
&& w.mAppToken.animation != null)
- || w.mAnimation != null)) {
+ || w.mWinAnimator.mAnimation != null)) {
// The current wallpaper target is animating, so we'll
// look behind it for another possible target and figure
// out what is going on below.
@@ -1657,9 +1658,9 @@ public class WindowManagerService extends IWindowManager.Stub
// Now what is happening... if the current and new targets are
// animating, then we are in our super special mode!
if (foundW != null && oldW != null) {
- boolean oldAnim = oldW.mAnimation != null
+ boolean oldAnim = oldW.mWinAnimator.mAnimation != null
|| (oldW.mAppToken != null && oldW.mAppToken.animation != null);
- boolean foundAnim = foundW.mAnimation != null
+ boolean foundAnim = foundW.mWinAnimator.mAnimation != null
|| (foundW.mAppToken != null && foundW.mAppToken.animation != null);
if (DEBUG_WALLPAPER) {
Slog.v(TAG, "New animation: " + foundAnim
@@ -1711,10 +1712,10 @@ public class WindowManagerService extends IWindowManager.Stub
} else if (mLowerWallpaperTarget != null) {
// Is it time to stop animating?
- boolean lowerAnimating = mLowerWallpaperTarget.mAnimation != null
+ boolean lowerAnimating = mLowerWallpaperTarget.mWinAnimator.mAnimation != null
|| (mLowerWallpaperTarget.mAppToken != null
&& mLowerWallpaperTarget.mAppToken.animation != null);
- boolean upperAnimating = mUpperWallpaperTarget.mAnimation != null
+ boolean upperAnimating = mUpperWallpaperTarget.mWinAnimator.mAnimation != null
|| (mUpperWallpaperTarget.mAppToken != null
&& mUpperWallpaperTarget.mAppToken.animation != null);
if (!lowerAnimating || !upperAnimating) {
@@ -2302,7 +2303,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_APP_TRANSITIONS) Slog.v(
TAG, "Remove " + win + ": mSurface=" + win.mSurface
+ " mExiting=" + win.mExiting
- + " isAnimating=" + win.isAnimating()
+ + " isAnimating=" + win.mWinAnimator.isAnimating()
+ " app-animation="
+ (win.mAppToken != null ? win.mAppToken.animation : null)
+ " inPendingTransaction="
@@ -2329,7 +2330,7 @@ public class WindowManagerService extends IWindowManager.Stub
win.mExiting = true;
}
}
- if (win.mExiting || win.isAnimating()) {
+ if (win.mExiting || win.mWinAnimator.isAnimating()) {
// The exit animation is running... wait for it!
//Slog.i(TAG, "*** Running exit animation...");
win.mExiting = true;
@@ -2698,7 +2699,7 @@ public class WindowManagerService extends IWindowManager.Stub
(win.mAppToken == null || !win.mAppToken.clientHidden)) {
displayed = !win.isVisibleLw();
if (win.mExiting) {
- win.cancelExitAnimationForNextAnimationLocked();
+ win.mWinAnimator.cancelExitAnimationForNextAnimationLocked();
}
if (win.mDestroying) {
win.mDestroying = false;
@@ -2802,7 +2803,7 @@ public class WindowManagerService extends IWindowManager.Stub
applyAnimationLocked(win, transit, false)) {
focusMayChange = true;
win.mExiting = true;
- } else if (win.isAnimating()) {
+ } else if (win.mWinAnimator.isAnimating()) {
// Currently in a hide animation... turn this into
// an exit.
win.mExiting = true;
@@ -2811,7 +2812,7 @@ public class WindowManagerService extends IWindowManager.Stub
// window, we need to change both of them inside
// of a transaction to avoid artifacts.
win.mExiting = true;
- win.mAnimating = true;
+ win.mWinAnimator.mAnimating = true;
} else {
if (mInputMethodWindow == win) {
mInputMethodWindow = null;
@@ -3025,7 +3026,8 @@ public class WindowManagerService extends IWindowManager.Stub
*/
boolean applyAnimationLocked(WindowState win,
int transit, boolean isEntrance) {
- if (win.mLocalAnimating && win.mAnimationIsEntrance == isEntrance) {
+ if (win.mWinAnimator.mLocalAnimating &&
+ win.mWinAnimator.mAnimationIsEntrance == isEntrance) {
// If we are trying to apply an animation, but already running
// an animation of the same type, then just leave that one alone.
return true;
@@ -3062,7 +3064,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (DEBUG_ANIM) Slog.v(TAG, "applyAnimation: win=" + win
+ " anim=" + anim + " attr=0x" + Integer.toHexString(attr)
- + " mAnimation=" + win.mAnimation
+ + " mAnimation=" + win.mWinAnimator.mAnimation
+ " isEntrance=" + isEntrance);
if (a != null) {
if (DEBUG_ANIM) {
@@ -3073,14 +3075,14 @@ public class WindowManagerService extends IWindowManager.Stub
}
Slog.v(TAG, "Loaded animation " + a + " for " + win, e);
}
- win.setAnimation(a);
- win.mAnimationIsEntrance = isEntrance;
+ win.mWinAnimator.setAnimation(a);
+ win.mWinAnimator.mAnimationIsEntrance = isEntrance;
}
} else {
- win.clearAnimation();
+ win.mWinAnimator.clearAnimation();
}
- return win.mAnimation != null;
+ return win.mWinAnimator.mAnimation != null;
}
private Animation loadAnimation(WindowManager.LayoutParams lp, int animAttr) {
@@ -3369,7 +3371,7 @@ public class WindowManagerService extends IWindowManager.Stub
for (int i=0; i<N; i++) {
WindowState win = wtoken.windows.get(i);
- if (win.isAnimating()) {
+ if (win.mWinAnimator.isAnimating()) {
delayed = true;
}
@@ -4062,7 +4064,7 @@ public class WindowManagerService extends IWindowManager.Stub
continue;
}
- if (win.isAnimating()) {
+ if (win.mWinAnimator.isAnimating()) {
delayed = true;
}
@@ -5261,8 +5263,8 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized(mWindowMap) {
long ident = Binder.clearCallingIdentity();
- dw = mAppDisplayWidth;
- dh = mAppDisplayHeight;
+ dw = mCurDisplayWidth;
+ dh = mCurDisplayHeight;
int aboveAppLayer = mPolicy.windowTypeToLayerLw(
WindowManager.LayoutParams.TYPE_APPLICATION) * TYPE_LAYER_MULTIPLIER
@@ -5382,7 +5384,7 @@ public class WindowManagerService extends IWindowManager.Stub
Bitmap bm = Bitmap.createBitmap(width, height, rawss.getConfig());
Matrix matrix = new Matrix();
ScreenRotationAnimation.createRotationMatrix(rot, dw, dh, matrix);
- matrix.postTranslate(-(int)(frame.left*scale), -(int)(frame.top*scale));
+ matrix.postTranslate(-FloatMath.ceil(frame.left*scale), -FloatMath.ceil(frame.top*scale));
Canvas canvas = new Canvas(bm);
canvas.drawBitmap(rawss, matrix, null);
canvas.setBitmap(null);
@@ -8098,11 +8100,11 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_FOCUS) Slog.i(TAG, "win=" + w + " force hides other windows");
mAnimator.mForceHiding = true;
} else if (mPolicy.canBeForceHidden(w, attrs)) {
- if (!w.mAnimating) {
+ if (!w.mWinAnimator.mAnimating) {
// We set the animation above so it
// is not yet running.
// TODO(cmautner): We lose the enter animation when this occurs.
- w.clearAnimation();
+ w.mWinAnimator.clearAnimation();
}
}
}
@@ -9769,7 +9771,7 @@ public class WindowManagerService extends IWindowManager.Stub
public interface OnHardKeyboardStatusChangeListener {
public void onHardKeyboardStatusChange(boolean available, boolean enabled);
}
-
+
void debugLayoutRepeats(final String msg) {
if (mLayoutRepeatCount >= LAYOUT_REPEAT_THRESHOLD) {
Slog.v(TAG, "Layouts looping: " + msg);
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index 1146189..615cd80 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -45,7 +45,6 @@ import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.WindowManagerPolicy;
import android.view.WindowManager.LayoutParams;
-import android.view.animation.Animation;
import android.view.animation.Transformation;
import java.io.PrintWriter;
@@ -91,7 +90,6 @@ final class WindowState implements WindowManagerPolicy.WindowState {
boolean mAttachedHidden; // is our parent window hidden?
boolean mLastHidden; // was this window last hidden?
boolean mWallpaperVisible; // for wallpaper, what was last vis report?
- boolean mWasAnimating; // Were we animating going into the most recent animation step?
/**
* The window size that was requested by the application. These are in
@@ -110,9 +108,9 @@ final class WindowState implements WindowManagerPolicy.WindowState {
boolean mTurnOnScreen;
int mLayoutSeq = -1;
-
+
Configuration mConfiguration = null;
-
+
/**
* Actual frame shown on-screen (may be modified by animation). These
* are in the screen's coordinate space (WITH the compatibility scale
@@ -212,15 +210,6 @@ final class WindowState implements WindowManagerPolicy.WindowState {
// an enter animation.
boolean mEnterAnimationPending;
- // Currently running animation.
- boolean mAnimating;
- boolean mLocalAnimating;
- Animation mAnimation;
- boolean mAnimationIsEntrance;
- boolean mHasTransformation;
- boolean mHasLocalTransformation;
- final Transformation mTransformation = new Transformation();
-
// If a window showing a wallpaper: the requested offset for the
// wallpaper; if a wallpaper window: the currently applied offset.
float mWallpaperX = -1;
@@ -305,6 +294,8 @@ final class WindowState implements WindowManagerPolicy.WindowState {
int mAnimDw;
int mAnimDh;
+ final WindowStateAnimator mWinAnimator;
+
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState attachedWindow, int seq, WindowManager.LayoutParams a,
int viewVisibility) {
@@ -333,6 +324,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
mBaseLayer = 0;
mSubLayer = 0;
mInputWindowHandle = null;
+ mWinAnimator = null;
return;
}
mDeathRecipient = deathRecipient;
@@ -369,9 +361,11 @@ final class WindowState implements WindowManagerPolicy.WindowState {
mIsFloatingLayer = mIsImWindow || mIsWallpaper;
}
+ mWinAnimator = new WindowStateAnimator(service, this, mAttachedWindow);
+
WindowState appWin = this;
while (appWin.mAttachedWindow != null) {
- appWin = mAttachedWindow;
+ appWin = appWin.mAttachedWindow;
}
WindowToken appToken = appWin.mToken;
while (appToken.appWindowToken == null) {
@@ -405,6 +399,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
mSession.windowAddedLocked();
}
+ @Override
public void computeFrameLw(Rect pf, Rect df, Rect cf, Rect vf) {
mHaveFrame = true;
@@ -547,38 +542,47 @@ final class WindowState implements WindowManagerPolicy.WindowState {
}
}
+ @Override
public Rect getFrameLw() {
return mFrame;
}
+ @Override
public RectF getShownFrameLw() {
return mShownFrame;
}
+ @Override
public Rect getDisplayFrameLw() {
return mDisplayFrame;
}
+ @Override
public Rect getContentFrameLw() {
return mContentFrame;
}
+ @Override
public Rect getVisibleFrameLw() {
return mVisibleFrame;
}
+ @Override
public boolean getGivenInsetsPendingLw() {
return mGivenInsetsPending;
}
+ @Override
public Rect getGivenContentInsetsLw() {
return mGivenContentInsets;
}
+ @Override
public Rect getGivenVisibleInsetsLw() {
return mGivenVisibleInsets;
}
+ @Override
public WindowManager.LayoutParams getAttrs() {
return mAttrs;
}
@@ -632,41 +636,6 @@ final class WindowState implements WindowManagerPolicy.WindowState {
return mAppToken != null ? mAppToken.firstWindowDrawn : false;
}
- public void setAnimation(Animation anim) {
- if (WindowManagerService.localLOGV) Slog.v(
- WindowManagerService.TAG, "Setting animation in " + this + ": " + anim);
- mAnimating = false;
- mLocalAnimating = false;
- mAnimation = anim;
- mAnimation.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION);
- mAnimation.scaleCurrentDuration(mService.mWindowAnimationScale);
- // Start out animation gone if window is gone, or visible if window is visible.
- mTransformation.clear();
- mTransformation.setAlpha(mLastHidden ? 0 : 1);
- mHasLocalTransformation = true;
- }
-
- public void clearAnimation() {
- if (mAnimation != null) {
- mAnimating = true;
- mLocalAnimating = false;
- mAnimation.cancel();
- mAnimation = null;
- }
- }
-
- // TODO: Fix and call finishExit() instead of cancelExitAnimationForNextAnimationLocked()
- // for avoiding the code duplication.
- void cancelExitAnimationForNextAnimationLocked() {
- if (!mExiting) return;
- if (mAnimation != null) {
- mAnimation.cancel();
- mAnimation = null;
- destroySurfaceLocked();
- }
- mExiting = false;
- }
-
Surface createSurfaceLocked() {
if (mSurface == null) {
mReportDestroySurface = false;
@@ -931,7 +900,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
+ (mAppToken != null ? mAppToken.hiddenRequested : false)
+ " tok.hidden="
+ (mAppToken != null ? mAppToken.hidden : false)
- + " animating=" + mAnimating
+ + " animating=" + mWinAnimator.mAnimating
+ " tok animating="
+ (mAppToken != null ? mAppToken.animating : false));
if (!mService.showSurfaceRobustlyLocked(this)) {
@@ -978,11 +947,11 @@ final class WindowState implements WindowManagerPolicy.WindowState {
// will do an animation to reveal it from behind the
// starting window, so there is no need for it to also
// be doing its own stuff.
- if (mAnimation != null) {
- mAnimation.cancel();
- mAnimation = null;
+ if (mWinAnimator.mAnimation != null) {
+ mWinAnimator.mAnimation.cancel();
+ mWinAnimator.mAnimation = null;
// Make sure we clean up the animation.
- mAnimating = true;
+ mWinAnimator.mAnimating = true;
}
mService.mFinishedStarting.add(mAppToken);
mService.mH.sendEmptyMessage(H.FINISHED_STARTING);
@@ -995,192 +964,6 @@ final class WindowState implements WindowManagerPolicy.WindowState {
return true;
}
- private boolean stepAnimation(long currentTime) {
- if ((mAnimation == null) || !mLocalAnimating) {
- return false;
- }
- mTransformation.clear();
- final boolean more = mAnimation.getTransformation(currentTime, mTransformation);
- if (WindowManagerService.DEBUG_ANIM) Slog.v(
- WindowManagerService.TAG, "Stepped animation in " + this +
- ": more=" + more + ", xform=" + mTransformation);
- return more;
- }
-
- // This must be called while inside a transaction. Returns true if
- // there is more animation to run.
- boolean stepAnimationLocked(long currentTime) {
- // Save the animation state as it was before this step so WindowManagerService can tell if
- // we just started or just stopped animating by comparing mWasAnimating with isAnimating().
- mWasAnimating = mAnimating;
- if (mService.okToDisplay()) {
- // We will run animations as long as the display isn't frozen.
-
- if (isDrawnLw() && mAnimation != null) {
- mHasTransformation = true;
- mHasLocalTransformation = true;
- if (!mLocalAnimating) {
- if (WindowManagerService.DEBUG_ANIM) Slog.v(
- WindowManagerService.TAG, "Starting animation in " + this +
- " @ " + currentTime + ": ww=" + mFrame.width() +
- " wh=" + mFrame.height() +
- " dw=" + mAnimDw + " dh=" + mAnimDh +
- " scale=" + mService.mWindowAnimationScale);
- mAnimation.initialize(mFrame.width(), mFrame.height(), mAnimDw, mAnimDh);
- mAnimation.setStartTime(currentTime);
- mLocalAnimating = true;
- mAnimating = true;
- }
- if ((mAnimation != null) && mLocalAnimating) {
- if (stepAnimation(currentTime)) {
- return true;
- }
- }
- if (WindowManagerService.DEBUG_ANIM) Slog.v(
- WindowManagerService.TAG, "Finished animation in " + this +
- " @ " + currentTime);
- //WindowManagerService.this.dump();
- }
- mHasLocalTransformation = false;
- if ((!mLocalAnimating || mAnimationIsEntrance) && mAppToken != null
- && mAppToken.animation != null) {
- // When our app token is animating, we kind-of pretend like
- // we are as well. Note the mLocalAnimating mAnimationIsEntrance
- // part of this check means that we will only do this if
- // our window is not currently exiting, or it is not
- // locally animating itself. The idea being that one that
- // is exiting and doing a local animation should be removed
- // once that animation is done.
- mAnimating = true;
- mHasTransformation = true;
- mTransformation.clear();
- return false;
- } else if (mHasTransformation) {
- // Little trick to get through the path below to act like
- // we have finished an animation.
- mAnimating = true;
- } else if (isAnimating()) {
- mAnimating = true;
- }
- } else if (mAnimation != null) {
- // If the display is frozen, and there is a pending animation,
- // clear it and make sure we run the cleanup code.
- mAnimating = true;
- mLocalAnimating = true;
- mAnimation.cancel();
- mAnimation = null;
- }
-
- if (!mAnimating && !mLocalAnimating) {
- return false;
- }
-
- if (WindowManagerService.DEBUG_ANIM) Slog.v(
- WindowManagerService.TAG, "Animation done in " + this + ": exiting=" + mExiting
- + ", reportedVisible="
- + (mAppToken != null ? mAppToken.reportedVisible : false));
-
- mAnimating = false;
- mLocalAnimating = false;
- if (mAnimation != null) {
- mAnimation.cancel();
- mAnimation = null;
- }
- if (mService.mWindowDetachedWallpaper == this) {
- mService.mWindowDetachedWallpaper = null;
- }
- mAnimLayer = mLayer;
- if (mIsImWindow) {
- mAnimLayer += mService.mInputMethodAnimLayerAdjustment;
- } else if (mIsWallpaper) {
- mAnimLayer += mService.mWallpaperAnimLayerAdjustment;
- }
- if (WindowManagerService.DEBUG_LAYERS) Slog.v(WindowManagerService.TAG, "Stepping win " + this
- + " anim layer: " + mAnimLayer);
- mHasTransformation = false;
- mHasLocalTransformation = false;
- if (mPolicyVisibility != mPolicyVisibilityAfterAnim) {
- if (DEBUG_VISIBILITY) {
- Slog.v(WindowManagerService.TAG, "Policy visibility changing after anim in " + this + ": "
- + mPolicyVisibilityAfterAnim);
- }
- mPolicyVisibility = mPolicyVisibilityAfterAnim;
- mService.mLayoutNeeded = true;
- if (!mPolicyVisibility) {
- if (mService.mCurrentFocus == this) {
- mService.mFocusMayChange = true;
- }
- // Window is no longer visible -- make sure if we were waiting
- // for it to be displayed before enabling the display, that
- // we allow the display to be enabled now.
- mService.enableScreenIfNeededLocked();
- }
- }
- mTransformation.clear();
- if (mHasDrawn
- && mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
- && mAppToken != null
- && mAppToken.firstWindowDrawn
- && mAppToken.startingData != null) {
- if (WindowManagerService.DEBUG_STARTING_WINDOW) Slog.v(WindowManagerService.TAG, "Finish starting "
- + mToken + ": first real window done animating");
- mService.mFinishedStarting.add(mAppToken);
- mService.mH.sendEmptyMessage(H.FINISHED_STARTING);
- }
-
- finishExit();
- mService.mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) mService.debugLayoutRepeats("WindowState");
-
- if (mAppToken != null) {
- mAppToken.updateReportedVisibilityLocked();
- }
-
- return false;
- }
-
- void finishExit() {
- if (WindowManagerService.DEBUG_ANIM) Slog.v(
- WindowManagerService.TAG, "finishExit in " + this
- + ": exiting=" + mExiting
- + " remove=" + mRemoveOnExit
- + " windowAnimating=" + isWindowAnimating());
-
- final int N = mChildWindows.size();
- for (int i=0; i<N; i++) {
- mChildWindows.get(i).finishExit();
- }
-
- if (!mExiting) {
- return;
- }
-
- if (isWindowAnimating()) {
- return;
- }
-
- if (WindowManagerService.localLOGV) Slog.v(
- WindowManagerService.TAG, "Exit animation finished in " + this
- + ": remove=" + mRemoveOnExit);
- if (mSurface != null) {
- mService.mDestroySurface.add(this);
- mDestroying = true;
- if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(this, "HIDE (finishExit)", null);
- mSurfaceShown = false;
- try {
- mSurface.hide();
- } catch (RuntimeException e) {
- Slog.w(WindowManagerService.TAG, "Error hiding surface in " + this, e);
- }
- mLastHidden = true;
- }
- mExiting = false;
- if (mRemoveOnExit) {
- mService.mPendingRemove.add(this);
- mRemoveOnExit = false;
- }
- }
-
boolean isIdentityMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
if (dsdx < .99999f || dsdx > 1.00001f) return false;
if (dtdy < .99999f || dtdy > 1.00001f) return false;
@@ -1199,10 +982,10 @@ final class WindowState implements WindowManagerPolicy.WindowState {
}
void computeShownFrameLocked() {
- final boolean selfTransformation = mHasLocalTransformation;
+ final boolean selfTransformation = mWinAnimator.mHasLocalTransformation;
Transformation attachedTransformation =
- (mAttachedWindow != null && mAttachedWindow.mHasLocalTransformation)
- ? mAttachedWindow.mTransformation : null;
+ (mAttachedWindow != null && mAttachedWindow.mWinAnimator.mHasLocalTransformation)
+ ? mAttachedWindow.mWinAnimator.mTransformation : null;
Transformation appTransformation =
(mAppToken != null && mAppToken.hasTransformation)
? mAppToken.transformation : null;
@@ -1211,10 +994,10 @@ final class WindowState implements WindowManagerPolicy.WindowState {
// are currently targeting.
if (mAttrs.type == TYPE_WALLPAPER && mService.mLowerWallpaperTarget == null
&& mService.mWallpaperTarget != null) {
- if (mService.mWallpaperTarget.mHasLocalTransformation &&
- mService.mWallpaperTarget.mAnimation != null &&
- !mService.mWallpaperTarget.mAnimation.getDetachWallpaper()) {
- attachedTransformation = mService.mWallpaperTarget.mTransformation;
+ if (mService.mWallpaperTarget.mWinAnimator.mHasLocalTransformation &&
+ mService.mWallpaperTarget.mWinAnimator.mAnimation != null &&
+ !mService.mWallpaperTarget.mWinAnimator.mAnimation.getDetachWallpaper()) {
+ attachedTransformation = mService.mWallpaperTarget.mWinAnimator.mTransformation;
if (WindowManagerService.DEBUG_WALLPAPER && attachedTransformation != null) {
Slog.v(WindowManagerService.TAG, "WP target attached xform: " + attachedTransformation);
}
@@ -1260,7 +1043,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
}
tmpMatrix.postScale(mGlobalScale, mGlobalScale);
if (selfTransformation) {
- tmpMatrix.postConcat(mTransformation.getMatrix());
+ tmpMatrix.postConcat(mWinAnimator.mTransformation.getMatrix());
}
tmpMatrix.postTranslate(frame.left + mXOffset, frame.top + mYOffset);
if (attachedTransformation != null) {
@@ -1304,7 +1087,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
&& x == frame.left && y == frame.top))) {
//Slog.i(TAG, "Applying alpha transform");
if (selfTransformation) {
- mShownAlpha *= mTransformation.getAlpha();
+ mShownAlpha *= mWinAnimator.mTransformation.getAlpha();
}
if (attachedTransformation != null) {
mShownAlpha *= attachedTransformation.getAlpha();
@@ -1323,7 +1106,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
if (WindowManagerService.localLOGV) Slog.v(
WindowManagerService.TAG, "computeShownFrameLocked: Animating " + this +
": " + mShownFrame +
- ", alpha=" + mTransformation.getAlpha() + ", mShownAlpha=" + mShownAlpha);
+ ", alpha=" + mWinAnimator.mTransformation.getAlpha() + ", mShownAlpha=" + mShownAlpha);
return;
}
@@ -1374,7 +1157,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
&& (atoken == null ? mPolicyVisibility : !atoken.hiddenRequested)
&& ((!mAttachedHidden && mViewVisibility == View.VISIBLE
&& !mRootToken.hidden)
- || mAnimation != null || animating);
+ || mWinAnimator.mAnimation != null || animating);
}
/**
@@ -1431,10 +1214,10 @@ final class WindowState implements WindowManagerPolicy.WindowState {
if (atoken != null) {
return mSurface != null && mPolicyVisibility && !mDestroying
&& ((!mAttachedHidden && !atoken.hiddenRequested)
- || mAnimation != null || atoken.animation != null);
+ || mWinAnimator.mAnimation != null || atoken.animation != null);
} else {
return mSurface != null && mPolicyVisibility && !mDestroying
- && (!mAttachedHidden || mAnimation != null);
+ && (!mAttachedHidden || mWinAnimator.mAnimation != null);
}
}
@@ -1450,26 +1233,10 @@ final class WindowState implements WindowManagerPolicy.WindowState {
return mSurface != null && mPolicyVisibility && !mDestroying
&& ((!mAttachedHidden && mViewVisibility == View.VISIBLE
&& !mRootToken.hidden)
- || mAnimation != null
+ || mWinAnimator.mAnimation != null
|| ((mAppToken != null) && (mAppToken.animation != null)));
}
- /** Is the window or its container currently animating? */
- boolean isAnimating() {
- final WindowState attached = mAttachedWindow;
- final AppWindowToken atoken = mAppToken;
- return mAnimation != null
- || (attached != null && attached.mAnimation != null)
- || (atoken != null &&
- (atoken.animation != null
- || atoken.inPendingTransaction));
- }
-
- /** Is this window currently animating? */
- boolean isWindowAnimating() {
- return mAnimation != null;
- }
-
/**
* Like isOnScreen, but returns false if the surface hasn't yet
* been drawn.
@@ -1479,7 +1246,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
return isDrawnLw() && mPolicyVisibility
&& ((!mAttachedHidden &&
(atoken == null || !atoken.hiddenRequested))
- || mAnimating);
+ || mWinAnimator.mAnimating);
}
public boolean isGoneForLayoutLw() {
@@ -1508,7 +1275,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
boolean isOpaqueDrawn() {
return (mAttrs.format == PixelFormat.OPAQUE
|| mAttrs.type == TYPE_WALLPAPER)
- && isDrawnLw() && mAnimation == null
+ && isDrawnLw() && mWinAnimator.mAnimation == null
&& (mAppToken == null || mAppToken.animation == null);
}
@@ -1607,10 +1374,10 @@ final class WindowState implements WindowManagerPolicy.WindowState {
if (DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "Policy visibility true: " + this);
if (doAnimation) {
if (DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "doAnimation: mPolicyVisibility="
- + mPolicyVisibility + " mAnimation=" + mAnimation);
+ + mPolicyVisibility + " mAnimation=" + mWinAnimator.mAnimation);
if (!mService.okToDisplay()) {
doAnimation = false;
- } else if (mPolicyVisibility && mAnimation == null) {
+ } else if (mPolicyVisibility && mWinAnimator.mAnimation == null) {
// Check for the case where we are currently visible and
// not animating; we do not want to do animation at such a
// point to become visible when we already are.
@@ -1646,7 +1413,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
}
if (doAnimation) {
mService.applyAnimationLocked(this, WindowManagerPolicy.TRANSIT_EXIT, false);
- if (mAnimation == null) {
+ if (mWinAnimator.mAnimation == null) {
doAnimation = false;
}
}
@@ -1821,20 +1588,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
pw.print(" last="); mLastVisibleInsets.printShortString(pw);
pw.println();
}
- if (mAnimating || mLocalAnimating || mAnimationIsEntrance
- || mAnimation != null) {
- pw.print(prefix); pw.print("mAnimating="); pw.print(mAnimating);
- pw.print(" mLocalAnimating="); pw.print(mLocalAnimating);
- pw.print(" mAnimationIsEntrance="); pw.print(mAnimationIsEntrance);
- pw.print(" mAnimation="); pw.println(mAnimation);
- }
- if (mHasTransformation || mHasLocalTransformation) {
- pw.print(prefix); pw.print("XForm: has=");
- pw.print(mHasTransformation);
- pw.print(" hasLocal="); pw.print(mHasLocalTransformation);
- pw.print(" "); mTransformation.printShortString(pw);
- pw.println();
- }
+ mWinAnimator.dump(pw, prefix, dumpAll);
if (mShownAlpha != 1 || mAlpha != 1 || mLastAlpha != 1) {
pw.print(prefix); pw.print("mShownAlpha="); pw.print(mShownAlpha);
pw.print(" mAlpha="); pw.print(mAlpha);
diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java
new file mode 100644
index 0000000..d86d411
--- /dev/null
+++ b/services/java/com/android/server/wm/WindowStateAnimator.java
@@ -0,0 +1,298 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+
+package com.android.server.wm;
+
+import android.util.Slog;
+import android.view.WindowManager;
+import android.view.WindowManagerPolicy;
+import android.view.animation.Animation;
+import android.view.animation.Transformation;
+
+import com.android.server.wm.WindowManagerService.H;
+
+import java.io.PrintWriter;
+
+/**
+ * @author cmautner@google.com (Craig Mautner)
+ *
+ */
+class WindowStateAnimator {
+
+ final WindowManagerService mService;
+ final WindowState mWin;
+ final WindowState mAttachedWindow;
+
+ // Currently running animation.
+ boolean mAnimating;
+ boolean mLocalAnimating;
+ Animation mAnimation;
+ boolean mAnimationIsEntrance;
+ boolean mHasTransformation;
+ boolean mHasLocalTransformation;
+ final Transformation mTransformation = new Transformation();
+ boolean mWasAnimating; // Were we animating going into the most recent animation step?
+
+ public WindowStateAnimator(final WindowManagerService service, final WindowState win,
+ final WindowState attachedWindow) {
+ mService = service;
+ mWin = win;
+ mAttachedWindow = attachedWindow;
+ }
+
+ public void setAnimation(Animation anim) {
+ if (WindowManagerService.localLOGV) Slog.v(
+ WindowManagerService.TAG, "Setting animation in " + this + ": " + anim);
+ mAnimating = false;
+ mLocalAnimating = false;
+ mAnimation = anim;
+ mAnimation.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION);
+ mAnimation.scaleCurrentDuration(mService.mWindowAnimationScale);
+ // Start out animation gone if window is gone, or visible if window is visible.
+ mTransformation.clear();
+ mTransformation.setAlpha(mWin.mLastHidden ? 0 : 1);
+ mHasLocalTransformation = true;
+ }
+
+ public void clearAnimation() {
+ if (mAnimation != null) {
+ mAnimating = true;
+ mLocalAnimating = false;
+ mAnimation.cancel();
+ mAnimation = null;
+ }
+ }
+
+ /** Is the window or its container currently animating? */
+ boolean isAnimating() {
+ final WindowState attached = mAttachedWindow;
+ final AppWindowToken atoken = mWin.mAppToken;
+ return mAnimation != null
+ || (attached != null && attached.mWinAnimator.mAnimation != null)
+ || (atoken != null &&
+ (atoken.animation != null
+ || atoken.inPendingTransaction));
+ }
+
+ /** Is this window currently animating? */
+ boolean isWindowAnimating() {
+ return mAnimation != null;
+ }
+
+ // TODO: Fix and call finishExit() instead of cancelExitAnimationForNextAnimationLocked()
+ // for avoiding the code duplication.
+ void cancelExitAnimationForNextAnimationLocked() {
+ if (!mWin.mExiting) return;
+ if (mAnimation != null) {
+ mAnimation.cancel();
+ mAnimation = null;
+ mWin.destroySurfaceLocked();
+ }
+ mWin.mExiting = false;
+ }
+
+ private boolean stepAnimation(long currentTime) {
+ if ((mAnimation == null) || !mLocalAnimating) {
+ return false;
+ }
+ mTransformation.clear();
+ final boolean more = mAnimation.getTransformation(currentTime, mTransformation);
+ if (WindowManagerService.DEBUG_ANIM) Slog.v(
+ WindowManagerService.TAG, "Stepped animation in " + this +
+ ": more=" + more + ", xform=" + mTransformation);
+ return more;
+ }
+
+ // This must be called while inside a transaction. Returns true if
+ // there is more animation to run.
+ boolean stepAnimationLocked(long currentTime) {
+ // Save the animation state as it was before this step so WindowManagerService can tell if
+ // we just started or just stopped animating by comparing mWasAnimating with isAnimating().
+ mWasAnimating = mAnimating;
+ if (mService.okToDisplay()) {
+ // We will run animations as long as the display isn't frozen.
+
+ if (mWin.isDrawnLw() && mAnimation != null) {
+ mHasTransformation = true;
+ mHasLocalTransformation = true;
+ if (!mLocalAnimating) {
+ if (WindowManagerService.DEBUG_ANIM) Slog.v(
+ WindowManagerService.TAG, "Starting animation in " + this +
+ " @ " + currentTime + ": ww=" + mWin.mFrame.width() +
+ " wh=" + mWin.mFrame.height() +
+ " dw=" + mWin.mAnimDw + " dh=" + mWin.mAnimDh +
+ " scale=" + mService.mWindowAnimationScale);
+ mAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(), mWin.mAnimDw,
+ mWin.mAnimDh);
+ mAnimation.setStartTime(currentTime);
+ mLocalAnimating = true;
+ mAnimating = true;
+ }
+ if ((mAnimation != null) && mLocalAnimating) {
+ if (stepAnimation(currentTime)) {
+ return true;
+ }
+ }
+ if (WindowManagerService.DEBUG_ANIM) Slog.v(
+ WindowManagerService.TAG, "Finished animation in " + this +
+ " @ " + currentTime);
+ //WindowManagerService.this.dump();
+ }
+ mHasLocalTransformation = false;
+ if ((!mLocalAnimating || mAnimationIsEntrance) && mWin.mAppToken != null
+ && mWin.mAppToken.animation != null) {
+ // When our app token is animating, we kind-of pretend like
+ // we are as well. Note the mLocalAnimating mAnimationIsEntrance
+ // part of this check means that we will only do this if
+ // our window is not currently exiting, or it is not
+ // locally animating itself. The idea being that one that
+ // is exiting and doing a local animation should be removed
+ // once that animation is done.
+ mAnimating = true;
+ mHasTransformation = true;
+ mTransformation.clear();
+ return false;
+ } else if (mHasTransformation) {
+ // Little trick to get through the path below to act like
+ // we have finished an animation.
+ mAnimating = true;
+ } else if (isAnimating()) {
+ mAnimating = true;
+ }
+ } else if (mAnimation != null) {
+ // If the display is frozen, and there is a pending animation,
+ // clear it and make sure we run the cleanup code.
+ mAnimating = true;
+ mLocalAnimating = true;
+ mAnimation.cancel();
+ mAnimation = null;
+ }
+
+ if (!mAnimating && !mLocalAnimating) {
+ return false;
+ }
+
+ if (WindowManagerService.DEBUG_ANIM) Slog.v(
+ WindowManagerService.TAG, "Animation done in " + this + ": exiting=" + mWin.mExiting
+ + ", reportedVisible="
+ + (mWin.mAppToken != null ? mWin.mAppToken.reportedVisible : false));
+
+ mAnimating = false;
+ mLocalAnimating = false;
+ if (mAnimation != null) {
+ mAnimation.cancel();
+ mAnimation = null;
+ }
+ if (mService.mWindowDetachedWallpaper == mWin) {
+ mService.mWindowDetachedWallpaper = null;
+ }
+ mWin.mAnimLayer = mWin.mLayer;
+ if (mWin.mIsImWindow) {
+ mWin.mAnimLayer += mService.mInputMethodAnimLayerAdjustment;
+ } else if (mWin.mIsWallpaper) {
+ mWin.mAnimLayer += mService.mWallpaperAnimLayerAdjustment;
+ }
+ if (WindowManagerService.DEBUG_LAYERS) Slog.v(WindowManagerService.TAG, "Stepping win " + this
+ + " anim layer: " + mWin.mAnimLayer);
+ mHasTransformation = false;
+ mHasLocalTransformation = false;
+ if (mWin.mPolicyVisibility != mWin.mPolicyVisibilityAfterAnim) {
+ if (WindowState.DEBUG_VISIBILITY) {
+ Slog.v(WindowManagerService.TAG, "Policy visibility changing after anim in " + this + ": "
+ + mWin.mPolicyVisibilityAfterAnim);
+ }
+ mWin.mPolicyVisibility = mWin.mPolicyVisibilityAfterAnim;
+ mService.mLayoutNeeded = true;
+ if (!mWin.mPolicyVisibility) {
+ if (mService.mCurrentFocus == mWin) {
+ mService.mFocusMayChange = true;
+ }
+ // Window is no longer visible -- make sure if we were waiting
+ // for it to be displayed before enabling the display, that
+ // we allow the display to be enabled now.
+ mService.enableScreenIfNeededLocked();
+ }
+ }
+ mTransformation.clear();
+ if (mWin.mHasDrawn
+ && mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
+ && mWin.mAppToken != null
+ && mWin.mAppToken.firstWindowDrawn
+ && mWin.mAppToken.startingData != null) {
+ if (WindowManagerService.DEBUG_STARTING_WINDOW) Slog.v(WindowManagerService.TAG, "Finish starting "
+ + mWin.mToken + ": first real window done animating");
+ mService.mFinishedStarting.add(mWin.mAppToken);
+ mService.mH.sendEmptyMessage(H.FINISHED_STARTING);
+ }
+
+ finishExit();
+ mService.mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
+ if (WindowManagerService.DEBUG_LAYOUT_REPEATS) mService.debugLayoutRepeats("WindowState");
+
+ if (mWin.mAppToken != null) {
+ mWin.mAppToken.updateReportedVisibilityLocked();
+ }
+
+ return false;
+ }
+
+ void finishExit() {
+ if (WindowManagerService.DEBUG_ANIM) Slog.v(
+ WindowManagerService.TAG, "finishExit in " + this
+ + ": exiting=" + mWin.mExiting
+ + " remove=" + mWin.mRemoveOnExit
+ + " windowAnimating=" + isWindowAnimating());
+
+ final int N = mWin.mChildWindows.size();
+ for (int i=0; i<N; i++) {
+ mWin.mChildWindows.get(i).mWinAnimator.finishExit();
+ }
+
+ if (!mWin.mExiting) {
+ return;
+ }
+
+ if (isWindowAnimating()) {
+ return;
+ }
+
+ if (WindowManagerService.localLOGV) Slog.v(
+ WindowManagerService.TAG, "Exit animation finished in " + this
+ + ": remove=" + mWin.mRemoveOnExit);
+ if (mWin.mSurface != null) {
+ mService.mDestroySurface.add(mWin);
+ mWin.mDestroying = true;
+ if (WindowState.SHOW_TRANSACTIONS) WindowManagerService.logSurface(
+ mWin, "HIDE (finishExit)", null);
+ mWin.mSurfaceShown = false;
+ try {
+ mWin.mSurface.hide();
+ } catch (RuntimeException e) {
+ Slog.w(WindowManagerService.TAG, "Error hiding surface in " + this, e);
+ }
+ mWin.mLastHidden = true;
+ }
+ mWin.mExiting = false;
+ if (mWin.mRemoveOnExit) {
+ mService.mPendingRemove.add(mWin);
+ mWin.mRemoveOnExit = false;
+ }
+ }
+
+ public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
+ if (mAnimating || mLocalAnimating || mAnimationIsEntrance
+ || mAnimation != null) {
+ pw.print(prefix); pw.print("mAnimating="); pw.print(mAnimating);
+ pw.print(" mLocalAnimating="); pw.print(mLocalAnimating);
+ pw.print(" mAnimationIsEntrance="); pw.print(mAnimationIsEntrance);
+ pw.print(" mAnimation="); pw.println(mAnimation);
+ }
+ if (mHasTransformation || mHasLocalTransformation) {
+ pw.print(prefix); pw.print("XForm: has=");
+ pw.print(mHasTransformation);
+ pw.print(" hasLocal="); pw.print(mHasLocalTransformation);
+ pw.print(" "); mTransformation.printShortString(pw);
+ pw.println();
+ }
+ }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index e8188e7..d736ac1 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -54,7 +54,7 @@ public class UserManagerTest extends AndroidTestCase {
public void testAddUser() throws Exception {
final UserManager details = mUserManager;
- UserInfo userInfo = details.createUser("Guest 1", UserInfo.FLAG_GUEST, null);
+ UserInfo userInfo = details.createUser("Guest 1", UserInfo.FLAG_GUEST);
assertTrue(userInfo != null);
List<UserInfo> list = details.getUsers();
@@ -73,8 +73,8 @@ public class UserManagerTest extends AndroidTestCase {
public void testAdd2Users() throws Exception {
final UserManager details = mUserManager;
- UserInfo user1 = details.createUser("Guest 1", UserInfo.FLAG_GUEST, null);
- UserInfo user2 = details.createUser("User 2", UserInfo.FLAG_ADMIN, null);
+ UserInfo user1 = details.createUser("Guest 1", UserInfo.FLAG_GUEST);
+ UserInfo user2 = details.createUser("User 2", UserInfo.FLAG_ADMIN);
assertTrue(user1 != null);
assertTrue(user2 != null);
@@ -87,7 +87,7 @@ public class UserManagerTest extends AndroidTestCase {
public void testRemoveUser() throws Exception {
final UserManager details = mUserManager;
- UserInfo userInfo = details.createUser("Guest 1", UserInfo.FLAG_GUEST, null);
+ UserInfo userInfo = details.createUser("Guest 1", UserInfo.FLAG_GUEST);
details.removeUser(userInfo.id);
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index 6fc0134..3aa53eef 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -43,6 +43,7 @@ import com.android.internal.telephony.test.SimulatedRadioControl;
import com.android.internal.telephony.gsm.SIMRecords;
import java.util.Locale;
+import java.util.concurrent.atomic.AtomicReference;
/**
@@ -116,7 +117,6 @@ public abstract class PhoneBase extends Handler implements Phone {
/* Instance Variables */
public CommandsInterface mCM;
- protected IccFileHandler mIccFileHandler;
boolean mDnsCheckDisabled;
public DataConnectionTracker mDataConnectionTracker;
boolean mDoesRilSendMultipleCallRing;
@@ -125,7 +125,7 @@ public abstract class PhoneBase extends Handler implements Phone {
public boolean mIsTheCurrentActivePhone = true;
boolean mIsVoiceCapable = true;
public IccRecords mIccRecords;
- public IccCard mIccCard;
+ protected AtomicReference<IccCard> mIccCard = new AtomicReference<IccCard>();
public SmsStorageMonitor mSmsStorageMonitor;
public SmsUsageMonitor mSmsUsageMonitor;
public SMSDispatcher mSMS;
@@ -268,7 +268,7 @@ public abstract class PhoneBase extends Handler implements Phone {
mSmsUsageMonitor = null;
mSMS = null;
mIccRecords = null;
- mIccCard = null;
+ mIccCard.set(null);
mDataConnectionTracker = null;
}
@@ -630,7 +630,11 @@ public abstract class PhoneBase extends Handler implements Phone {
/**
* Retrieves the IccFileHandler of the Phone instance
*/
- public abstract IccFileHandler getIccFileHandler();
+ public IccFileHandler getIccFileHandler(){
+ IccCard iccCard = mIccCard.get();
+ if (iccCard == null) return null;
+ return iccCard.getIccFileHandler();
+ }
/*
* Retrieves the Handler of the Phone instance
@@ -655,7 +659,7 @@ public abstract class PhoneBase extends Handler implements Phone {
@Override
public IccCard getIccCard() {
- return mIccCard;
+ return mIccCard.get();
}
@Override
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
index f914030..110d8bf 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
@@ -85,9 +85,8 @@ public class CDMALTEPhone extends CDMAPhone {
@Override
protected void initSstIcc() {
- mIccCard = UiccController.getInstance(this).getIccCard();
- mIccRecords = mIccCard.getIccRecords();
- mIccFileHandler = mIccCard.getIccFileHandler();
+ mIccCard.set(UiccController.getInstance(this).getIccCard());
+ mIccRecords = mIccCard.get().getIccRecords();
// CdmaLteServiceStateTracker registers with IccCard to know
// when the card is ready. So create mIccCard before the ServiceStateTracker
mSST = new CdmaLteServiceStateTracker(this);
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
index ce581de..bb00d4b 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -149,9 +149,8 @@ public class CDMAPhone extends PhoneBase {
}
protected void initSstIcc() {
- mIccCard = UiccController.getInstance(this).getIccCard();
- mIccRecords = mIccCard.getIccRecords();
- mIccFileHandler = mIccCard.getIccFileHandler();
+ mIccCard.set(UiccController.getInstance(this).getIccCard());
+ mIccRecords = mIccCard.get().getIccRecords();
// CdmaServiceStateTracker registers with IccCard to know
// when the Ruim card is ready. So create mIccCard before the ServiceStateTracker
mSST = new CdmaServiceStateTracker(this);
@@ -251,7 +250,6 @@ public class CDMAPhone extends PhoneBase {
mRuimPhoneBookInterfaceManager = null;
mRuimSmsInterfaceManager = null;
mSubInfo = null;
- mIccFileHandler = null;
mCT = null;
mSST = null;
mEriManager = null;
@@ -1127,13 +1125,6 @@ public class CDMAPhone extends PhoneBase {
}
/**
- * {@inheritDoc}
- */
- public IccFileHandler getIccFileHandler() {
- return this.mIccFileHandler;
- }
-
- /**
* Activate or deactivate cell broadcast SMS.
*
* @param activate 0 = activate, 1 = deactivate
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
index cad2e22..09008cd 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
@@ -349,7 +349,7 @@ public class CdmaLteServiceStateTracker extends CdmaServiceStateTracker {
ss.setOperatorAlphaLong(eriText);
}
- if (phone.mIccCard.getState() == IccCard.State.READY) {
+ if (phone.getIccCard().getState() == IccCard.State.READY) {
// SIM is found on the device. If ERI roaming is OFF, and SID/NID matches
// one configfured in SIM, use operator name from CSIM record.
boolean showSpn =
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
index 75f5d47..9ec56fc 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
@@ -209,8 +209,7 @@ public class CdmaServiceStateTracker extends ServiceStateTracker {
// Unregister for all events.
cm.unregisterForRadioStateChanged(this);
cm.unregisterForVoiceNetworkStateChanged(this);
- phone.mIccCard.unregisterForReady(this);
-
+ phone.getIccCard().unregisterForReady(this);
cm.unregisterForCdmaOtaProvision(this);
phone.unregisterForEriFileLoaded(this);
phone.mIccRecords.unregisterForRecordsLoaded(this);
@@ -498,7 +497,7 @@ public class CdmaServiceStateTracker extends ServiceStateTracker {
// NV is ready when subscription source is NV
sendMessage(obtainMessage(EVENT_NV_READY));
} else {
- phone.mIccCard.registerForReady(this, EVENT_RUIM_READY, null);
+ phone.getIccCard().registerForReady(this, EVENT_RUIM_READY, null);
}
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
index 6e10542..be13c35 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
@@ -136,12 +136,11 @@ public class GSMPhone extends PhoneBase {
}
mCM.setPhoneType(Phone.PHONE_TYPE_GSM);
- mIccCard = UiccController.getInstance(this).getIccCard();
+ mIccCard.set(UiccController.getInstance(this).getIccCard());
+ mIccRecords = mIccCard.get().getIccRecords();
mCT = new GsmCallTracker(this);
mSST = new GsmServiceStateTracker (this);
mSMS = new GsmSMSDispatcher(this, mSmsStorageMonitor, mSmsUsageMonitor);
- mIccFileHandler = mIccCard.getIccFileHandler();
- mIccRecords = mIccCard.getIccRecords();
mDataConnectionTracker = new GsmDataConnectionTracker (this);
if (!unitTestMode) {
mSimPhoneBookIntManager = new SimPhoneBookInterfaceManager(this);
@@ -229,7 +228,6 @@ public class GSMPhone extends PhoneBase {
mSimPhoneBookIntManager = null;
mSimSmsIntManager = null;
mSubInfo = null;
- mIccFileHandler = null;
mCT = null;
mSST = null;
super.removeReferences();
@@ -1440,13 +1438,6 @@ public class GSMPhone extends PhoneBase {
}
/**
- * {@inheritDoc}
- */
- public IccFileHandler getIccFileHandler(){
- return this.mIccFileHandler;
- }
-
- /**
* Activate or deactivate cell broadcast SMS.
*
* @param activate 0 = activate, 1 = deactivate
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java b/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java
index 205c73d..9b3d5cd 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java
@@ -765,7 +765,7 @@ public final class GsmMmiCode extends Handler implements MmiCode {
// invalid length
handlePasswordError(com.android.internal.R.string.invalidPin);
} else if (sc.equals(SC_PIN) &&
- phone.mIccCard.getState() == IccCard.State.PUK_REQUIRED ) {
+ phone.getIccCard().getState() == IccCard.State.PUK_REQUIRED ) {
// Sim is puk-locked
handlePasswordError(com.android.internal.R.string.needPuk);
} else {
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
index 148b139..080d90c 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
@@ -211,7 +211,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
cm.setOnNITZTime(this, EVENT_NITZ_TIME, null);
cm.setOnSignalStrengthUpdate(this, EVENT_SIGNAL_STRENGTH_UPDATE, null);
cm.setOnRestrictedStateChanged(this, EVENT_RESTRICTED_STATE_CHANGED, null);
- phone.mIccCard.registerForReady(this, EVENT_SIM_READY, null);
+ phone.getIccCard().registerForReady(this, EVENT_SIM_READY, null);
// system setting property AIRPLANE_MODE_ON is set in Settings.
int airplaneMode = Settings.System.getInt(
@@ -244,7 +244,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
cm.unregisterForAvailable(this);
cm.unregisterForRadioStateChanged(this);
cm.unregisterForVoiceNetworkStateChanged(this);
- phone.mIccCard.unregisterForReady(this);
+ phone.getIccCard().unregisterForReady(this);
phone.mIccRecords.unregisterForRecordsLoaded(this);
cm.unSetOnSignalStrengthUpdate(this);
cm.unSetOnRestrictedStateChanged(this);
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 351c771..5610134 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -45,6 +45,7 @@ import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.RemoteException;
import java.util.List;
@@ -530,6 +531,14 @@ public class MockPackageManager extends PackageManager {
* @hide
*/
@Override
+ public UserInfo getUser(int userId) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @hide
+ */
+ @Override
public boolean removeUser(int id) {
throw new UnsupportedOperationException();
}
diff --git a/voip/java/android/net/rtp/AudioStream.java b/voip/java/android/net/rtp/AudioStream.java
index d761214..b7874f7 100644
--- a/voip/java/android/net/rtp/AudioStream.java
+++ b/voip/java/android/net/rtp/AudioStream.java
@@ -158,7 +158,7 @@ public class AudioStream extends RtpStream {
if (type < 96 || type > 127) {
throw new IllegalArgumentException("Invalid type");
}
- if (type == mCodec.type) {
+ if (mCodec != null && type == mCodec.type) {
throw new IllegalArgumentException("The type is used by codec");
}
}