summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk33
-rw-r--r--api/current.xml644
-rw-r--r--camera/libcameraservice/CameraService.cpp49
-rw-r--r--camera/libcameraservice/CameraService.h4
-rw-r--r--cmds/keystore/keystore_get.h2
-rw-r--r--cmds/stagefright/Android.mk66
-rw-r--r--cmds/stagefright/WaveWriter.h71
-rw-r--r--cmds/stagefright/play.cpp295
-rw-r--r--cmds/stagefright/record.cpp181
-rw-r--r--cmds/stagefright/stagefright.cpp200
-rw-r--r--core/java/android/app/ActivityManagerNative.java39
-rw-r--r--core/java/android/app/ActivityThread.java1
-rw-r--r--core/java/android/app/Dialog.java14
-rw-r--r--core/java/android/app/IActivityManager.java6
-rw-r--r--core/java/android/app/ISearchManager.aidl1
-rw-r--r--core/java/android/app/SearchDialog.java94
-rw-r--r--core/java/android/app/SearchManager.java50
-rw-r--r--core/java/android/app/SuggestionsAdapter.java303
-rwxr-xr-xcore/java/android/appwidget/AppWidgetProvider.java8
-rw-r--r--core/java/android/appwidget/AppWidgetProviderInfo.java3
-rw-r--r--core/java/android/bluetooth/BluetoothDevice.java27
-rw-r--r--core/java/android/bluetooth/BluetoothIntent.java4
-rw-r--r--core/java/android/bluetooth/BluetoothUuid.java2
-rw-r--r--core/java/android/bluetooth/IBluetoothDevice.aidl5
-rw-r--r--core/java/android/content/AbstractTableMerger.java49
-rw-r--r--core/java/android/content/Intent.java71
-rw-r--r--core/java/android/content/SyncManager.java2
-rw-r--r--core/java/android/content/SyncStorageEngine.java23
-rw-r--r--core/java/android/content/pm/PackageParser.java2
-rw-r--r--core/java/android/content/res/CompatibilityInfo.java38
-rw-r--r--core/java/android/content/res/Resources.java31
-rw-r--r--core/java/android/hardware/Camera.java80
-rw-r--r--core/java/android/net/http/ConnectionThread.java1
-rw-r--r--core/java/android/net/http/Request.java12
-rw-r--r--core/java/android/net/http/RequestHandle.java2
-rw-r--r--core/java/android/net/http/RequestQueue.java97
-rw-r--r--core/java/android/os/BatteryStats.java22
-rw-r--r--core/java/android/os/Process.java2
-rw-r--r--core/java/android/pim/vcard/ContactStruct.java1244
-rw-r--r--core/java/android/pim/vcard/EntryCommitter.java88
-rw-r--r--core/java/android/pim/vcard/EntryHandler.java33
-rw-r--r--core/java/android/pim/vcard/VCardBuilder.java64
-rw-r--r--core/java/android/pim/vcard/VCardBuilderCollection.java99
-rw-r--r--core/java/android/pim/vcard/VCardConfig.java59
-rw-r--r--core/java/android/pim/vcard/VCardDataBuilder.java319
-rw-r--r--core/java/android/pim/vcard/VCardEntryCounter.java60
-rw-r--r--core/java/android/pim/vcard/VCardParser.java90
-rw-r--r--core/java/android/pim/vcard/VCardParser_V21.java948
-rw-r--r--core/java/android/pim/vcard/VCardParser_V30.java306
-rw-r--r--core/java/android/pim/vcard/VCardSourceDetector.java137
-rw-r--r--core/java/android/pim/vcard/exception/VCardException.java35
-rw-r--r--core/java/android/pim/vcard/exception/VCardNestedException.java29
-rw-r--r--core/java/android/pim/vcard/exception/VCardNotSupportedException.java33
-rw-r--r--core/java/android/pim/vcard/exception/VCardVersionException.java29
-rw-r--r--core/java/android/pim/vcard/exception/package.html5
-rw-r--r--core/java/android/pim/vcard/package.html5
-rw-r--r--core/java/android/provider/ContactsContract.java129
-rw-r--r--core/java/android/provider/Settings.java41
-rw-r--r--core/java/android/server/BluetoothA2dpService.java15
-rw-r--r--core/java/android/server/BluetoothDeviceService.java47
-rw-r--r--core/java/android/server/BluetoothEventLoop.java53
-rw-r--r--core/java/android/server/search/SearchDialogWrapper.java104
-rw-r--r--core/java/android/server/search/SearchManagerService.java4
-rwxr-xr-xcore/java/android/speech/tts/TextToSpeech.java221
-rw-r--r--core/java/android/text/Html.java107
-rw-r--r--core/java/android/util/DisplayMetrics.java34
-rw-r--r--core/java/android/util/TypedValue.java12
-rw-r--r--core/java/android/view/Display.java27
-rw-r--r--core/java/android/view/MotionEvent.java8
-rw-r--r--core/java/android/view/SurfaceView.java25
-rw-r--r--core/java/android/view/ViewRoot.java9
-rw-r--r--core/java/android/view/Window.java8
-rw-r--r--core/java/android/view/WindowManager.java12
-rw-r--r--core/java/android/webkit/BrowserFrame.java18
-rw-r--r--core/java/android/webkit/FrameLoader.java9
-rw-r--r--core/java/android/webkit/LoadListener.java42
-rw-r--r--core/java/android/webkit/MimeTypeMap.java628
-rw-r--r--core/java/android/webkit/Network.java6
-rw-r--r--core/java/android/webkit/URLUtil.java2
-rw-r--r--core/java/android/webkit/WebTextView.java77
-rw-r--r--core/java/android/webkit/WebView.java8
-rw-r--r--core/java/android/webkit/WebViewCore.java72
-rw-r--r--core/java/android/widget/AbsListView.java2
-rw-r--r--core/java/android/widget/AutoCompleteTextView.java24
-rw-r--r--core/java/android/widget/DatePicker.java15
-rw-r--r--core/java/android/widget/Filter.java36
-rw-r--r--core/java/android/widget/PopupWindow.java28
-rw-r--r--core/java/android/widget/RelativeLayout.java7
-rw-r--r--core/java/android/widget/Spinner.java15
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java67
-rw-r--r--core/java/com/android/internal/os/PowerProfile.java5
-rw-r--r--core/java/com/android/internal/widget/NumberPicker.java8
-rw-r--r--core/jni/.android_server_BluetoothEventLoop.cpp.swpbin0 -> 16384 bytes
-rw-r--r--core/jni/AndroidRuntime.cpp9
-rw-r--r--core/jni/android/graphics/Bitmap.cpp6
-rw-r--r--core/jni/android/graphics/BitmapFactory.cpp10
-rw-r--r--core/jni/android/graphics/Graphics.cpp56
-rw-r--r--core/jni/android/graphics/GraphicsJNI.h6
-rw-r--r--core/jni/android_hardware_Camera.cpp5
-rw-r--r--core/jni/android_media_AudioRecord.cpp6
-rw-r--r--core/jni/android_media_AudioTrack.cpp8
-rw-r--r--core/jni/android_server_BluetoothDeviceService.cpp73
-rw-r--r--core/jni/android_server_BluetoothEventLoop.cpp38
-rw-r--r--core/jni/android_util_Process.cpp8
-rw-r--r--core/res/res/drawable/light_header.9.pngbin0 -> 183 bytes
-rw-r--r--core/res/res/layout/search_dropdown_app_selector.xml44
-rw-r--r--core/res/res/layout/search_dropdown_item_2line.xml59
-rw-r--r--core/res/res/layout/search_dropdown_item_icons_1line.xml54
-rw-r--r--core/res/res/values-he-rIL/donottranslate-cldr.xml24
-rw-r--r--core/res/res/values-pt-rBR/donottranslate-cldr.xml2
-rw-r--r--core/res/res/values-pt-rPT/donottranslate-cldr.xml2
-rw-r--r--core/res/res/values-pt/donottranslate-cldr.xml2
-rw-r--r--core/res/res/values-vi-rVN/donottranslate-cldr.xml4
-rw-r--r--core/res/res/values/styles.xml3
-rw-r--r--core/res/res/values/themes.xml26
-rw-r--r--data/etc/platform.xml2
-rw-r--r--data/sounds/AudioPackage2.mk2
-rw-r--r--data/sounds/OriginalAudio.mk2
-rw-r--r--graphics/java/android/graphics/Bitmap.java71
-rw-r--r--graphics/java/android/graphics/BitmapFactory.java31
-rw-r--r--graphics/java/android/graphics/Canvas.java4
-rw-r--r--include/media/IMediaPlayerService.h2
-rw-r--r--include/media/IOMX.h176
-rw-r--r--include/media/MediaPlayerInterface.h42
-rw-r--r--include/media/PVPlayer.h3
-rw-r--r--include/media/stagefright/AudioPlayer.h98
-rw-r--r--include/media/stagefright/AudioSource.h51
-rw-r--r--include/media/stagefright/CachingDataSource.h60
-rw-r--r--include/media/stagefright/CameraSource.h72
-rw-r--r--include/media/stagefright/DataSource.h61
-rw-r--r--include/media/stagefright/ESDS.h64
-rw-r--r--include/media/stagefright/FileSource.h49
-rw-r--r--include/media/stagefright/HTTPDataSource.h59
-rw-r--r--include/media/stagefright/HTTPStream.h74
-rw-r--r--include/media/stagefright/MP3Extractor.h53
-rw-r--r--include/media/stagefright/MPEG4Extractor.h65
-rw-r--r--include/media/stagefright/MPEG4Writer.h73
-rw-r--r--include/media/stagefright/MediaBuffer.h113
-rw-r--r--include/media/stagefright/MediaBufferGroup.h57
-rw-r--r--include/media/stagefright/MediaErrors.h43
-rw-r--r--include/media/stagefright/MediaExtractor.h49
-rw-r--r--include/media/stagefright/MediaPlayerImpl.h132
-rw-r--r--include/media/stagefright/MediaSource.h91
-rw-r--r--include/media/stagefright/MetaData.h132
-rw-r--r--include/media/stagefright/MmapSource.h52
-rw-r--r--include/media/stagefright/OMXClient.h124
-rw-r--r--include/media/stagefright/OMXDecoder.h158
-rw-r--r--include/media/stagefright/QComHardwareRenderer.h57
-rw-r--r--include/media/stagefright/SampleTable.h107
-rw-r--r--include/media/stagefright/ShoutcastSource.h59
-rw-r--r--include/media/stagefright/SoftwareRenderer.h55
-rw-r--r--include/media/stagefright/SurfaceRenderer.h51
-rw-r--r--include/media/stagefright/TimeSource.h51
-rw-r--r--include/media/stagefright/TimedEventQueue.h106
-rw-r--r--include/media/stagefright/Utils.h37
-rw-r--r--include/media/stagefright/VideoRenderer.h41
-rw-r--r--include/media/stagefright/string.h54
-rw-r--r--include/private/ui/SharedState.h2
-rw-r--r--include/ui/EventHub.h17
-rw-r--r--include/ui/Overlay.h4
-rw-r--r--include/utils/ResourceTypes.h3
-rw-r--r--include/utils/threads.h17
-rw-r--r--keystore/java/android/security/CertTool.java50
-rw-r--r--keystore/jni/cert.c114
-rw-r--r--keystore/jni/cert.h15
-rw-r--r--keystore/jni/certtool.c97
-rw-r--r--libs/audioflinger/AudioFlinger.cpp1
-rw-r--r--libs/binder/IPCThreadState.cpp9
-rw-r--r--libs/rs/Android.mk3
-rw-r--r--libs/rs/RenderScriptEnv.h60
-rw-r--r--libs/rs/java/Film/res/raw/filmstrip.c36
-rw-r--r--libs/rs/java/Film/src/com/android/film/FilmRS.java108
-rw-r--r--libs/rs/java/Fountain/AndroidManifest.xml2
-rw-r--r--libs/rs/java/Fountain/res/raw/fountain.c18
-rw-r--r--libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java37
-rw-r--r--libs/rs/java/RenderScript/android/renderscript/RenderScript.java10
-rw-r--r--libs/rs/java/Rollo/AndroidManifest.xml2
-rw-r--r--libs/rs/java/Rollo/res/raw/browser.png (renamed from libs/rs/java/Rollo/res/drawable/browser.png)bin5772 -> 5772 bytes
-rw-r--r--libs/rs/java/Rollo/res/raw/market.png (renamed from libs/rs/java/Rollo/res/drawable/market.png)bin4810 -> 4810 bytes
-rw-r--r--libs/rs/java/Rollo/res/raw/photos.png (renamed from libs/rs/java/Rollo/res/drawable/photos.png)bin4902 -> 4902 bytes
-rw-r--r--libs/rs/java/Rollo/res/raw/rollo.c109
-rw-r--r--libs/rs/java/Rollo/res/raw/rollo2.c67
-rw-r--r--libs/rs/java/Rollo/res/raw/settings.png (renamed from libs/rs/java/Rollo/res/drawable/settings.png)bin3764 -> 3764 bytes
-rw-r--r--libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java93
-rw-r--r--libs/rs/java/Rollo/src/com/android/rollo/RolloView.java137
-rw-r--r--libs/rs/jni/RenderScript_jni.cpp9
-rw-r--r--libs/rs/rs.spec4
-rw-r--r--libs/rs/rsContext.cpp7
-rw-r--r--libs/rs/rsContext.h4
-rw-r--r--libs/rs/rsFileA3D.cpp30
-rw-r--r--libs/rs/rsLight.cpp35
-rw-r--r--libs/rs/rsLight.h6
-rw-r--r--libs/rs/rsMesh.h3
-rw-r--r--libs/rs/rsProgramFragmentStore.cpp4
-rw-r--r--libs/rs/rsProgramVertex.cpp38
-rw-r--r--libs/rs/rsProgramVertex.h7
-rw-r--r--libs/rs/rsScriptC.cpp435
-rw-r--r--libs/rs/rsScriptC.h13
-rw-r--r--libs/rs/rsScriptC_Lib.cpp531
-rw-r--r--libs/rs/rsUtils.h3
-rw-r--r--libs/ui/EventHub.cpp65
-rw-r--r--libs/ui/Overlay.cpp21
-rw-r--r--libs/utils/BackupData.cpp8
-rw-r--r--libs/utils/ResourceTypes.cpp13
-rw-r--r--libs/utils/ZipUtils.cpp3
-rwxr-xr-xlocation/java/com/android/internal/location/GpsLocationProvider.java6
-rw-r--r--media/java/android/media/AudioService.java10
-rw-r--r--media/java/android/media/MediaScanner.java2
-rw-r--r--media/java/android/media/Metadata.java316
-rw-r--r--media/libmedia/Android.mk6
-rw-r--r--media/libmedia/AudioTrack.cpp2
-rw-r--r--media/libmedia/IMediaPlayerService.cpp20
-rw-r--r--media/libmedia/IOMX.cpp561
-rw-r--r--media/libmediaplayerservice/Android.mk20
-rw-r--r--media/libmediaplayerservice/MediaPlayerService.cpp143
-rw-r--r--media/libmediaplayerservice/MediaPlayerService.h20
-rw-r--r--media/libmediaplayerservice/MidiFile.h8
-rw-r--r--media/libmediaplayerservice/StagefrightPlayer.cpp213
-rw-r--r--media/libmediaplayerservice/StagefrightPlayer.h63
-rw-r--r--media/libmediaplayerservice/TestPlayerStub.cpp196
-rw-r--r--media/libmediaplayerservice/TestPlayerStub.h123
-rw-r--r--media/libmediaplayerservice/VorbisPlayer.h4
-rw-r--r--media/libstagefright/Android.mk60
-rw-r--r--media/libstagefright/AudioPlayer.cpp285
-rw-r--r--media/libstagefright/CachingDataSource.cpp162
-rw-r--r--media/libstagefright/CameraSource.cpp226
-rw-r--r--media/libstagefright/DataSource.cpp76
-rw-r--r--media/libstagefright/ESDS.cpp196
-rw-r--r--media/libstagefright/FileSource.cpp50
-rw-r--r--media/libstagefright/HTTPDataSource.cpp174
-rw-r--r--media/libstagefright/HTTPStream.cpp285
-rw-r--r--media/libstagefright/MP3Extractor.cpp527
-rw-r--r--media/libstagefright/MPEG4Extractor.cpp937
-rw-r--r--media/libstagefright/MPEG4Writer.cpp641
-rw-r--r--media/libstagefright/MediaBuffer.cpp174
-rw-r--r--media/libstagefright/MediaBufferGroup.cpp88
-rw-r--r--media/libstagefright/MediaExtractor.cpp54
-rw-r--r--media/libstagefright/MediaPlayerImpl.cpp693
-rw-r--r--media/libstagefright/MediaSource.cpp60
-rw-r--r--media/libstagefright/MetaData.cpp232
-rw-r--r--media/libstagefright/MmapSource.cpp112
-rw-r--r--media/libstagefright/OMXClient.cpp369
-rw-r--r--media/libstagefright/OMXDecoder.cpp1329
-rw-r--r--media/libstagefright/QComHardwareRenderer.cpp139
-rw-r--r--media/libstagefright/SampleTable.cpp598
-rw-r--r--media/libstagefright/ShoutcastSource.cpp154
-rw-r--r--media/libstagefright/SoftwareRenderer.cpp173
-rw-r--r--media/libstagefright/SurfaceRenderer.cpp69
-rw-r--r--media/libstagefright/TimeSource.cpp41
-rw-r--r--media/libstagefright/TimedEventQueue.cpp207
-rw-r--r--media/libstagefright/Utils.cpp45
-rw-r--r--media/libstagefright/omx/Android.mk27
-rw-r--r--media/libstagefright/omx/OMX.cpp623
-rw-r--r--media/libstagefright/omx/OMX.h132
-rw-r--r--media/libstagefright/string.cpp83
-rw-r--r--media/sdutils/sdutil.cpp4
-rwxr-xr-xmedia/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java6
-rwxr-xr-xmedia/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java13
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerInvokeTest.java70
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerMetadataParserTest.java432
-rw-r--r--media/tests/players/Android.mk29
-rw-r--r--media/tests/players/README8
-rw-r--r--media/tests/players/invoke_mock_media_player.cpp124
-rw-r--r--obex/javax/obex/Authenticator.java89
-rw-r--r--obex/javax/obex/BaseStream.java21
-rw-r--r--obex/javax/obex/ClientOperation.java173
-rw-r--r--obex/javax/obex/ClientSession.java37
-rw-r--r--obex/javax/obex/HeaderSet.java150
-rw-r--r--obex/javax/obex/ObexHelper.java159
-rw-r--r--obex/javax/obex/ObexSession.java12
-rw-r--r--obex/javax/obex/ObexTransport.java1
-rw-r--r--obex/javax/obex/Operation.java101
-rw-r--r--obex/javax/obex/PasswordAuthentication.java16
-rw-r--r--obex/javax/obex/PrivateInputStream.java26
-rw-r--r--obex/javax/obex/PrivateOutputStream.java25
-rw-r--r--obex/javax/obex/ResponseCodes.java9
-rw-r--r--obex/javax/obex/ServerOperation.java125
-rw-r--r--obex/javax/obex/ServerRequestHandler.java192
-rw-r--r--obex/javax/obex/ServerSession.java73
-rw-r--r--obex/javax/obex/SessionNotifier.java127
-rw-r--r--opengl/libagl/Android.mk1
-rw-r--r--opengl/libagl/copybit.cpp3
-rw-r--r--opengl/libagl/egl.cpp13
-rw-r--r--opengl/libagl/primitives.cpp2
-rw-r--r--opengl/libagl/texture.cpp14
-rw-r--r--packages/SettingsProvider/etc/bookmarks.xml2
-rw-r--r--packages/TtsService/jni/android_tts_SynthProxy.cpp50
-rwxr-xr-xpackages/TtsService/src/android/tts/SynthProxy.java10
-rwxr-xr-xpackages/TtsService/src/android/tts/TtsService.java192
-rw-r--r--packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java7
-rw-r--r--packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java6
-rw-r--r--services/java/com/android/server/AppWidgetService.java8
-rw-r--r--services/java/com/android/server/BackupManagerService.java219
-rw-r--r--services/java/com/android/server/BatteryService.java18
-rw-r--r--services/java/com/android/server/ConnectivityService.java199
-rw-r--r--services/java/com/android/server/InputDevice.java118
-rw-r--r--services/java/com/android/server/KeyInputQueue.java295
-rw-r--r--services/java/com/android/server/MountListener.java1
-rw-r--r--services/java/com/android/server/PackageManagerService.java16
-rw-r--r--services/java/com/android/server/WifiService.java3
-rw-r--r--services/java/com/android/server/WindowManagerService.java469
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java21
-rw-r--r--services/java/com/android/server/am/BatteryStatsService.java2
-rw-r--r--services/jni/com_android_server_KeyInputQueue.cpp38
-rw-r--r--telephony/java/com/android/internal/telephony/Phone.java50
-rw-r--r--telephony/java/com/android/internal/telephony/PhoneBase.java15
-rw-r--r--telephony/java/com/android/internal/telephony/PhoneProxy.java9
-rw-r--r--telephony/java/com/android/internal/telephony/TelephonyProperties.java6
-rwxr-xr-xtelephony/java/com/android/internal/telephony/cdma/CDMAPhone.java306
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java26
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java52
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/RuimRecords.java13
-rwxr-xr-xtelephony/java/com/android/internal/telephony/gsm/GSMPhone.java12
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmCallTracker.java8
-rw-r--r--tests/AndroidTests/AndroidManifest.xml1
-rw-r--r--tests/AndroidTests/res/raw/v21_simple_1.vcf3
-rw-r--r--tests/AndroidTests/res/raw/v21_simple_2.vcf3
-rw-r--r--tests/AndroidTests/res/raw/v21_simple_3.vcf (renamed from tests/AndroidTests/res/raw/v21_simple.vcf)0
-rw-r--r--tests/AndroidTests/src/com/android/unit_tests/HtmlTest.java62
-rw-r--r--tests/AndroidTests/src/com/android/unit_tests/SearchablesTest.java4
-rwxr-xr-xtests/AndroidTests/src/com/android/unit_tests/VpnTest.java46
-rw-r--r--tests/AndroidTests/src/com/android/unit_tests/activity/ServiceTest.java16
-rw-r--r--tests/AndroidTests/src/com/android/unit_tests/content/ConfigTest.java2
-rw-r--r--tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNode.java322
-rw-r--r--tests/AndroidTests/src/com/android/unit_tests/vcard/VCardTests.java (renamed from tests/AndroidTests/src/com/android/unit_tests/VCardTests.java)209
-rw-r--r--tests/AndroidTests/src/com/android/unit_tests/vcard/VNode.java30
-rw-r--r--tests/AndroidTests/src/com/android/unit_tests/vcard/VNodeBuilder.java313
-rw-r--r--tests/CoreTests/android/core/RequestAPITest.java24
-rw-r--r--tests/CoreTests/com/android/internal/telephony/gsm/GSMPhoneTest.java4
-rw-r--r--tests/DpiTest/AndroidManifest.xml9
-rw-r--r--tests/DpiTest/res/drawable-nodpi/logonodpi120.pngbin0 -> 5178 bytes
-rw-r--r--tests/DpiTest/res/drawable-nodpi/logonodpi160.pngbin0 -> 8114 bytes
-rw-r--r--tests/DpiTest/res/drawable-nodpi/logonodpi240.pngbin0 -> 13388 bytes
-rw-r--r--tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java48
-rw-r--r--tests/DpiTest/src/com/google/android/test/dpi/DpiTestNoCompatActivity.java23
-rw-r--r--tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java4
-rw-r--r--tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java6
-rw-r--r--tools/aapt/AaptAssets.cpp8
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java10
-rw-r--r--wifi/java/android/net/wifi/WifiStateTracker.java22
340 files changed, 25837 insertions, 3712 deletions
diff --git a/Android.mk b/Android.mk
index b11877e2cde2..9c5773fd8ef5 100644
--- a/Android.mk
+++ b/Android.mk
@@ -362,6 +362,34 @@ framework_docs_LOCAL_DROIDDOC_OPTIONS += \
-hdf sdk.rel.id $(framework_docs_SDK_REL_ID) \
-hdf sdk.current $(framework_docs_SDK_CURRENT_DIR)
+# ==== the api stubs and current.xml ===========================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES)
+LOCAL_INTERMEDIATE_SOURCES:=$(framework_docs_LOCAL_INTERMEDIATE_SOURCES)
+LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES)
+LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
+LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
+LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR)
+LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR)
+
+LOCAL_MODULE := api-stubs
+
+LOCAL_DROIDDOC_OPTIONS:=\
+ $(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
+ -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android_stubs_current_intermediates/src \
+ -apixml $(INTERNAL_PLATFORM_API_FILE) \
+ -nodocs
+
+LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk
+LOCAL_DROIDDOC_CUSTOM_ASSET_DIR:=assets-sdk
+
+include $(BUILD_DROIDDOC)
+
+$(full_target): $(framework_built)
+$(INTERNAL_PLATFORM_API_FILE): $(full_target)
+$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE))
+
# ==== static html in the sdk ==================================
include $(CLEAR_VARS)
@@ -380,10 +408,7 @@ LOCAL_DROIDDOC_OPTIONS:=\
-title "Android SDK" \
-proofread $(OUT_DOCS)/$(LOCAL_MODULE)-proofread.txt \
-todo $(OUT_DOCS)/$(LOCAL_MODULE)-docs-todo.html \
- -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android_stubs_current_intermediates/src \
- -apixml $(INTERNAL_PLATFORM_API_FILE) \
-sdkvalues $(OUT_DOCS) \
- -warning 3 \
-hdf android.whichdoc offline
LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk
@@ -399,8 +424,6 @@ $(static_doc_index_redirect): \
$(full_target): $(static_doc_index_redirect)
$(full_target): $(framework_built)
-$(INTERNAL_PLATFORM_API_FILE): $(full_target)
-$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE))
# ==== docs for the web (on the google app engine server) =======================
diff --git a/api/current.xml b/api/current.xml
index a31208ab7a8e..3a7fd2f921bb 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -25978,6 +25978,50 @@
visibility="public"
>
</field>
+<field name="EXTRA_DATA_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;intent_extra_data_key&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="INTENT_ACTION_SEARCHABLES_CHANGED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.search.action.SEARCHABLES_CHANGED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="INTENT_ACTION_SEARCH_SETTINGS_CHANGED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.search.action.SETTINGS_CHANGED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="INTENT_ACTION_WEB_SEARCH_SETTINGS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.search.action.WEB_SEARCH_SETTINGS&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="MENU_KEY"
type="char"
transient="false"
@@ -26011,6 +26055,17 @@
visibility="public"
>
</field>
+<field name="SHORTCUT_MIME_TYPE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;vnd.android.cursor.item/vnd.android.search.suggest&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="SUGGEST_COLUMN_FORMAT"
type="java.lang.String"
transient="false"
@@ -26077,6 +26132,17 @@
visibility="public"
>
</field>
+<field name="SUGGEST_COLUMN_INTENT_EXTRA_DATA"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;suggest_intent_extra_data&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="SUGGEST_COLUMN_QUERY"
type="java.lang.String"
transient="false"
@@ -26088,6 +26154,28 @@
visibility="public"
>
</field>
+<field name="SUGGEST_COLUMN_SHORTCUT_ID"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;suggest_shortcut_id&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;suggest_spinner_while_refreshing&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="SUGGEST_COLUMN_TEXT_1"
type="java.lang.String"
transient="false"
@@ -26121,6 +26209,17 @@
visibility="public"
>
</field>
+<field name="SUGGEST_NEVER_MAKE_SHORTCUT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;_-1&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="SUGGEST_URI_PATH_QUERY"
type="java.lang.String"
transient="false"
@@ -26132,6 +26231,17 @@
visibility="public"
>
</field>
+<field name="SUGGEST_URI_PATH_SHORTCUT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;search_suggest_shortcut&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="USER_QUERY"
type="java.lang.String"
transient="false"
@@ -35638,6 +35748,39 @@
visibility="public"
>
</field>
+<field name="ACTION_TTS_CHECK_TTS_DATA"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.action.CHECK_TTS_DATA&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_TTS_INSTALL_TTS_DATA"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.action.INSTALL_TTS_DATA&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_TTS_QUEUE_PROCESSING_COMPLETED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.action.TTS_QUEUE_PROCESSING_COMPLETED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="ACTION_UID_REMOVED"
type="java.lang.String"
transient="false"
@@ -52656,6 +52799,17 @@
visibility="public"
>
</method>
+<method name="getDensityScale"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getHeight"
return="int"
abstract="false"
@@ -52729,6 +52883,58 @@
visibility="public"
>
</method>
+<method name="getScaledHeight"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="canvas" type="android.graphics.Canvas">
+</parameter>
+</method>
+<method name="getScaledHeight"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="metrics" type="android.util.DisplayMetrics">
+</parameter>
+</method>
+<method name="getScaledWidth"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="canvas" type="android.graphics.Canvas">
+</parameter>
+</method>
+<method name="getScaledWidth"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="metrics" type="android.util.DisplayMetrics">
+</parameter>
+</method>
<method name="getWidth"
return="int"
abstract="false"
@@ -52751,6 +52957,17 @@
visibility="public"
>
</method>
+<method name="isAutoScalingEnabled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="isMutable"
return="boolean"
abstract="false"
@@ -52795,6 +53012,32 @@
visibility="public"
>
</method>
+<method name="setAutoScalingEnabled"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="autoScalingEnabled" type="boolean">
+</parameter>
+</method>
+<method name="setDensityScale"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="densityScale" type="float">
+</parameter>
+</method>
<method name="setPixel"
return="void"
abstract="false"
@@ -52862,6 +53105,17 @@
visibility="public"
>
</field>
+<field name="DENSITY_SCALE_UNKNOWN"
+ type="float"
+ transient="false"
+ volatile="false"
+ value="-1.0f"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
</class>
<class name="Bitmap.CompressFormat"
extends="java.lang.Enum"
@@ -53081,6 +53335,27 @@
deprecated="not deprecated"
visibility="public"
>
+<parameter name="res" type="android.content.res.Resources">
+</parameter>
+<parameter name="value" type="android.util.TypedValue">
+</parameter>
+<parameter name="is" type="java.io.InputStream">
+</parameter>
+<parameter name="pad" type="android.graphics.Rect">
+</parameter>
+<parameter name="opts" type="android.graphics.BitmapFactory.Options">
+</parameter>
+</method>
+<method name="decodeStream"
+ return="android.graphics.Bitmap"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
<parameter name="is" type="java.io.InputStream">
</parameter>
<parameter name="outPadding" type="android.graphics.Rect">
@@ -53129,6 +53404,16 @@
visibility="public"
>
</method>
+<field name="inDensity"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="inDither"
type="boolean"
transient="false"
@@ -53189,6 +53474,16 @@
visibility="public"
>
</field>
+<field name="inScaled"
+ type="boolean"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="inTempStorage"
type="byte[]"
transient="false"
@@ -54443,6 +54738,17 @@
visibility="public"
>
</method>
+<method name="getDensityScale"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getDrawFilter"
return="android.graphics.DrawFilter"
abstract="false"
@@ -54789,6 +55095,19 @@
<parameter name="bitmap" type="android.graphics.Bitmap">
</parameter>
</method>
+<method name="setDensityScale"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="densityScale" type="float">
+</parameter>
+</method>
<method name="setDrawFilter"
return="void"
abstract="false"
@@ -65953,6 +66272,19 @@
<exception name="IOException" type="java.io.IOException">
</exception>
</method>
+<method name="setZoomCallback"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cb" type="android.hardware.Camera.ZoomCallback">
+</parameter>
+</method>
<method name="startPreview"
return="void"
abstract="false"
@@ -65992,6 +66324,25 @@
<parameter name="jpeg" type="android.hardware.Camera.PictureCallback">
</parameter>
</method>
+<method name="takePicture"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="shutter" type="android.hardware.Camera.ShutterCallback">
+</parameter>
+<parameter name="raw" type="android.hardware.Camera.PictureCallback">
+</parameter>
+<parameter name="postview" type="android.hardware.Camera.PictureCallback">
+</parameter>
+<parameter name="jpeg" type="android.hardware.Camera.PictureCallback">
+</parameter>
+</method>
<field name="CAMERA_ERROR_SERVER_DIED"
type="int"
transient="false"
@@ -66393,6 +66744,29 @@
>
</field>
</class>
+<interface name="Camera.ZoomCallback"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onZoomUpdate"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="zoomLevel" type="int">
+</parameter>
+<parameter name="camera" type="android.hardware.Camera">
+</parameter>
+</method>
+</interface>
<class name="GeomagneticField"
extends="java.lang.Object"
abstract="false"
@@ -115065,6 +115439,38 @@
<parameter name="listener" type="android.speech.tts.TextToSpeech.OnInitListener">
</parameter>
</constructor>
+<method name="addEarcon"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="earcon" type="java.lang.String">
+</parameter>
+<parameter name="packagename" type="java.lang.String">
+</parameter>
+<parameter name="resourceId" type="int">
+</parameter>
+</method>
+<method name="addEarcon"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="earcon" type="java.lang.String">
+</parameter>
+<parameter name="filename" type="java.lang.String">
+</parameter>
+</method>
<method name="addSpeech"
return="int"
abstract="false"
@@ -115374,6 +115780,144 @@
>
</field>
</class>
+<class name="TextToSpeech.Engine"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="TextToSpeech.Engine"
+ type="android.speech.tts.TextToSpeech.Engine"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<field name="CHECK_VOICE_DATA_BAD_DATA"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHECK_VOICE_DATA_FAIL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHECK_VOICE_DATA_MISSING_DATA"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHECK_VOICE_DATA_MISSING_VOLUME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHECK_VOICE_DATA_PASS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TTS_DEFAULT_STREAM"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TTS_KEY_PARAM_STREAM"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;streamType&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TTS_KEY_PARAM_UTTERANCE_ID"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;utteranceId&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="VOICE_DATA_FILES"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;dataFiles&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="VOICE_DATA_FILES_INFO"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;dataFilesInfo&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="VOICE_DATA_ROOT_DIRECTORY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;dataRoot&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
<interface name="TextToSpeech.OnInitListener"
abstract="true"
static="true"
@@ -136791,6 +137335,50 @@
visibility="public"
>
</method>
+<field name="DENSITY_DEFAULT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="160"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DENSITY_HIGH"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="240"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DENSITY_LOW"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="120"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DENSITY_MEDIUM"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="160"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="density"
type="float"
transient="false"
@@ -138840,6 +139428,28 @@
visibility="public"
>
</field>
+<field name="DENSITY_DEFAULT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DENSITY_NONE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="65535"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="TYPE_ATTRIBUTE"
type="int"
transient="false"
@@ -139068,6 +139678,16 @@
visibility="public"
>
</field>
+<field name="density"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="resourceId"
type="int"
transient="false"
@@ -175418,6 +176038,17 @@
<parameter name="yOffset" type="int">
</parameter>
</method>
+<method name="getSoftInputMode"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getWidth"
return="int"
abstract="false"
@@ -175623,6 +176254,19 @@
<parameter name="touchable" type="boolean">
</parameter>
</method>
+<method name="setSoftInputMode"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="mode" type="int">
+</parameter>
+</method>
<method name="setTouchInterceptor"
return="void"
abstract="false"
diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp
index 96389dd5d22c..97b43a4fe259 100644
--- a/camera/libcameraservice/CameraService.cpp
+++ b/camera/libcameraservice/CameraService.cpp
@@ -227,6 +227,8 @@ CameraService::Client::Client(const sp<CameraService>& cameraService,
mMediaPlayerClick = newMediaPlayer("/system/media/audio/ui/camera_click.ogg");
mMediaPlayerBeep = newMediaPlayer("/system/media/audio/ui/VideoRecord.ogg");
+ mOverlayW = 0;
+ mOverlayH = 0;
// Callback is disabled by default
mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP;
@@ -399,6 +401,11 @@ void CameraService::Client::disconnect()
mHardware->cancelPicture(true, true, true);
// Release the hardware resources.
mHardware->release();
+ // Release the held overlay resources.
+ if (mUseOverlay)
+ {
+ mOverlayRef = 0;
+ }
mHardware.clear();
mCameraService->removeClient(mCameraClient);
@@ -420,11 +427,21 @@ status_t CameraService::Client::setPreviewDisplay(const sp<ISurface>& surface)
result = NO_ERROR;
// asBinder() is safe on NULL (returns NULL)
if (surface->asBinder() != mSurface->asBinder()) {
- if (mSurface != 0 && !mUseOverlay) {
+ if (mSurface != 0) {
LOGD("clearing old preview surface %p", mSurface.get());
- mSurface->unregisterBuffers();
+ if ( !mUseOverlay)
+ {
+ mSurface->unregisterBuffers();
+ }
+ else
+ {
+ // Force the destruction of any previous overlay
+ sp<Overlay> dummy;
+ mHardware->setOverlay( dummy );
+ }
}
mSurface = surface;
+ mOverlayRef = 0;
// If preview has been already started, set overlay or register preview
// buffers now.
if (mHardware->previewEnabled()) {
@@ -520,8 +537,8 @@ status_t CameraService::Client::setOverlay()
const char *format = params.getPreviewFormat();
int fmt;
- if (!strcmp(format, "yuv422i"))
- fmt = OVERLAY_FORMAT_YCbCr_422_I;
+ if (!strcmp(format, "yuv422i-yuyv"))
+ fmt = OVERLAY_FORMAT_YCbYCr_422_I;
else if (!strcmp(format, "rgb565"))
fmt = OVERLAY_FORMAT_RGB_565;
else {
@@ -529,16 +546,35 @@ status_t CameraService::Client::setOverlay()
return -EINVAL;
}
+ if ( w != mOverlayW || h != mOverlayH )
+ {
+ // Force the destruction of any previous overlay
+ sp<Overlay> dummy;
+ mHardware->setOverlay( dummy );
+ mOverlayRef = 0;
+ }
+
status_t ret = NO_ERROR;
if (mSurface != 0) {
- sp<OverlayRef> ref = mSurface->createOverlay(w, h, fmt);
- ret = mHardware->setOverlay(new Overlay(ref));
+ if (mOverlayRef.get() == NULL) {
+ mOverlayRef = mSurface->createOverlay(w, h, fmt);
+ if ( mOverlayRef.get() == NULL )
+ {
+ LOGE("Overlay Creation Failed!");
+ return -EINVAL;
+ }
+ ret = mHardware->setOverlay(new Overlay(mOverlayRef));
+ }
} else {
ret = mHardware->setOverlay(NULL);
}
if (ret != NO_ERROR) {
LOGE("mHardware->setOverlay() failed with status %d\n", ret);
}
+
+ mOverlayW = w;
+ mOverlayH = h;
+
return ret;
}
@@ -1092,6 +1128,7 @@ void CameraService::Client::postPreviewFrame(const sp<IMemory>& mem)
ssize_t offset;
size_t size;
sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
+ if ( !mUseOverlay )
{
Mutex::Autolock surfaceLock(mSurfaceLock);
if (mSurface != NULL) {
diff --git a/camera/libcameraservice/CameraService.h b/camera/libcameraservice/CameraService.h
index ea93789d14a0..8a49fa6a7d99 100644
--- a/camera/libcameraservice/CameraService.h
+++ b/camera/libcameraservice/CameraService.h
@@ -189,6 +189,10 @@ private:
sp<CameraHardwareInterface> mHardware;
pid_t mClientPid;
bool mUseOverlay;
+
+ sp<OverlayRef> mOverlayRef;
+ int mOverlayW;
+ int mOverlayH;
};
// ----------------------------------------------------------------------------
diff --git a/cmds/keystore/keystore_get.h b/cmds/keystore/keystore_get.h
index a7fd9a556af8..7665e81a314d 100644
--- a/cmds/keystore/keystore_get.h
+++ b/cmds/keystore/keystore_get.h
@@ -29,7 +29,7 @@
* is returned. Otherwise it returns the value in dynamically allocated memory
* and sets the size if the pointer is not NULL. One can release the memory by
* calling free(). */
-static char *keystore_get(char *key, int *size)
+static char *keystore_get(const char *key, int *size)
{
char buffer[MAX_KEY_VALUE_LENGTH];
char *value;
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
new file mode 100644
index 000000000000..fd681a286b7b
--- /dev/null
+++ b/cmds/stagefright/Android.mk
@@ -0,0 +1,66 @@
+ifeq ($(BUILD_WITH_STAGEFRIGHT),true)
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ stagefright.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libstagefright
+
+LOCAL_C_INCLUDES:= \
+ frameworks/base/media/libstagefright \
+ $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
+ $(TOP)/external/opencore/android
+
+LOCAL_CFLAGS += -Wno-multichar
+
+LOCAL_MODULE:= stagefright
+
+include $(BUILD_EXECUTABLE)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ record.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libstagefright
+
+LOCAL_C_INCLUDES:= \
+ frameworks/base/media/libstagefright \
+ $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
+ $(TOP)/external/opencore/android
+
+LOCAL_CFLAGS += -Wno-multichar
+
+LOCAL_MODULE:= record
+
+include $(BUILD_EXECUTABLE)
+
+################################################################################
+
+# include $(CLEAR_VARS)
+#
+# LOCAL_SRC_FILES:= \
+# play.cpp
+#
+# LOCAL_SHARED_LIBRARIES := \
+# libstagefright
+#
+# LOCAL_C_INCLUDES:= \
+# frameworks/base/media/libstagefright \
+# $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
+# $(TOP)/external/opencore/android
+#
+# LOCAL_CFLAGS += -Wno-multichar
+#
+# LOCAL_MODULE:= play
+#
+# include $(BUILD_EXECUTABLE)
+
+endif
diff --git a/cmds/stagefright/WaveWriter.h b/cmds/stagefright/WaveWriter.h
new file mode 100644
index 000000000000..a0eb66e2b9bc
--- /dev/null
+++ b/cmds/stagefright/WaveWriter.h
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_WAVEWRITER_H_
+
+#define ANDROID_WAVEWRITER_H_
+
+namespace android {
+
+class WaveWriter {
+public:
+ WaveWriter(const char *filename,
+ uint16_t num_channels, uint32_t sampling_rate)
+ : mFile(fopen(filename, "wb")),
+ mTotalBytes(0) {
+ fwrite("RIFFxxxxWAVEfmt \x10\x00\x00\x00\x01\x00", 1, 22, mFile);
+ write_u16(num_channels);
+ write_u32(sampling_rate);
+ write_u32(sampling_rate * num_channels * 2);
+ write_u16(num_channels * 2);
+ write_u16(16);
+ fwrite("dataxxxx", 1, 8, mFile);
+ }
+
+ ~WaveWriter() {
+ fseek(mFile, 40, SEEK_SET);
+ write_u32(mTotalBytes);
+
+ fseek(mFile, 4, SEEK_SET);
+ write_u32(36 + mTotalBytes);
+
+ fclose(mFile);
+ mFile = NULL;
+ }
+
+ void Append(const void *data, size_t size) {
+ fwrite(data, 1, size, mFile);
+ mTotalBytes += size;
+ }
+
+private:
+ void write_u16(uint16_t x) {
+ fputc(x & 0xff, mFile);
+ fputc(x >> 8, mFile);
+ }
+
+ void write_u32(uint32_t x) {
+ write_u16(x & 0xffff);
+ write_u16(x >> 16);
+ }
+
+ FILE *mFile;
+ size_t mTotalBytes;
+};
+
+} // namespace android
+
+#endif // ANDROID_WAVEWRITER_H_
diff --git a/cmds/stagefright/play.cpp b/cmds/stagefright/play.cpp
new file mode 100644
index 000000000000..c6e778e1361d
--- /dev/null
+++ b/cmds/stagefright/play.cpp
@@ -0,0 +1,295 @@
+/*
+ * 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.
+ */
+
+#include <binder/ProcessState.h>
+#include <media/stagefright/OMXClient.h>
+#include <media/stagefright/TimedEventQueue.h>
+#include <media/stagefright/MPEG4Extractor.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MmapSource.h>
+#include <media/stagefright/OMXDecoder.h>
+
+using namespace android;
+
+struct NewPlayer {
+ NewPlayer();
+ ~NewPlayer();
+
+ void setSource(const char *uri);
+ void start();
+ void pause();
+ void stop();
+
+private:
+ struct PlayerEvent : public TimedEventQueue::Event {
+ PlayerEvent(NewPlayer *player,
+ void (NewPlayer::*method)(int64_t realtime_us))
+ : mPlayer(player),
+ mMethod(method) {
+ }
+
+ virtual void fire(TimedEventQueue *queue, int64_t realtime_us) {
+ (mPlayer->*mMethod)(realtime_us);
+ }
+
+ private:
+ NewPlayer *mPlayer;
+ void (NewPlayer::*mMethod)(int64_t realtime_us);
+
+ PlayerEvent(const PlayerEvent &);
+ PlayerEvent &operator=(const PlayerEvent &);
+ };
+
+ struct PlayVideoFrameEvent : public TimedEventQueue::Event {
+ PlayVideoFrameEvent(NewPlayer *player, MediaBuffer *buffer)
+ : mPlayer(player),
+ mBuffer(buffer) {
+ }
+
+ virtual ~PlayVideoFrameEvent() {
+ if (mBuffer != NULL) {
+ mBuffer->release();
+ mBuffer = NULL;
+ }
+ }
+
+ virtual void fire(TimedEventQueue *queue, int64_t realtime_us) {
+ mPlayer->onPlayVideoFrame(realtime_us, mBuffer);
+ mBuffer = NULL;
+ }
+
+ private:
+ NewPlayer *mPlayer;
+ MediaBuffer *mBuffer;
+
+ PlayVideoFrameEvent(const PlayVideoFrameEvent &);
+ PlayVideoFrameEvent &operator=(const PlayVideoFrameEvent &);
+ };
+
+ OMXClient mClient;
+
+ MPEG4Extractor *mExtractor;
+ MediaSource *mAudioSource;
+ OMXDecoder *mAudioDecoder;
+ MediaSource *mVideoSource;
+ OMXDecoder *mVideoDecoder;
+
+ int32_t mVideoWidth, mVideoHeight;
+
+ TimedEventQueue mQueue;
+ wp<TimedEventQueue::Event> mPlayVideoFrameEvent;
+
+ int64_t mMediaTimeUsStart;
+ int64_t mRealTimeUsStart;
+
+ void setAudioSource(MediaSource *source);
+ void setVideoSource(MediaSource *source);
+
+ int64_t approxRealTime(int64_t mediatime_us) const;
+
+ void onStart(int64_t realtime_us);
+ void onPause(int64_t realtime_us);
+ void onFetchVideoFrame(int64_t realtime_us);
+ void onPlayVideoFrame(int64_t realtime_us, MediaBuffer *buffer);
+
+ static int64_t getMediaBufferTimeUs(MediaBuffer *buffer);
+
+ NewPlayer(const NewPlayer &);
+ NewPlayer &operator=(const NewPlayer &);
+};
+
+NewPlayer::NewPlayer()
+ : mExtractor(NULL),
+ mAudioSource(NULL),
+ mAudioDecoder(NULL),
+ mVideoSource(NULL),
+ mVideoDecoder(NULL),
+ mVideoWidth(0),
+ mVideoHeight(0) {
+ status_t err = mClient.connect();
+ assert(err == OK);
+}
+
+NewPlayer::~NewPlayer() {
+ stop();
+
+ mClient.disconnect();
+}
+
+void NewPlayer::setSource(const char *uri) {
+ stop();
+
+ mExtractor = new MPEG4Extractor(new MmapSource(uri));
+
+ int num_tracks;
+ status_t err = mExtractor->countTracks(&num_tracks);
+ assert(err == OK);
+
+ for (int i = 0; i < num_tracks; ++i) {
+ const sp<MetaData> meta = mExtractor->getTrackMetaData(i);
+ assert(meta != NULL);
+
+ const char *mime;
+ if (!meta->findCString(kKeyMIMEType, &mime)) {
+ continue;
+ }
+
+ bool is_audio = false;
+ bool is_acceptable = false;
+ if (!strncasecmp(mime, "audio/", 6)) {
+ is_audio = true;
+ is_acceptable = (mAudioSource == NULL);
+ } else if (!strncasecmp(mime, "video/", 6)) {
+ is_acceptable = (mVideoSource == NULL);
+ }
+
+ if (!is_acceptable) {
+ continue;
+ }
+
+ MediaSource *source;
+ if (mExtractor->getTrack(i, &source) != OK) {
+ continue;
+ }
+
+ if (is_audio) {
+ setAudioSource(source);
+ } else {
+ setVideoSource(source);
+ }
+ }
+}
+
+void NewPlayer::setAudioSource(MediaSource *source) {
+ mAudioSource = source;
+
+ sp<MetaData> meta = source->getFormat();
+
+ mAudioDecoder = OMXDecoder::Create(&mClient, meta);
+ mAudioDecoder->setSource(source);
+}
+
+void NewPlayer::setVideoSource(MediaSource *source) {
+ mVideoSource = source;
+
+ sp<MetaData> meta = source->getFormat();
+
+ bool success = meta->findInt32(kKeyWidth, &mVideoWidth);
+ assert(success);
+
+ success = meta->findInt32(kKeyHeight, &mVideoHeight);
+ assert(success);
+
+ mVideoDecoder = OMXDecoder::Create(&mClient, meta);
+ mVideoDecoder->setSource(source);
+}
+
+void NewPlayer::start() {
+ mQueue.start();
+ mQueue.postEvent(new PlayerEvent(this, &NewPlayer::onStart));
+}
+
+void NewPlayer::pause() {
+ mQueue.postEvent(new PlayerEvent(this, &NewPlayer::onPause));
+}
+
+void NewPlayer::stop() {
+ mQueue.stop();
+
+ delete mVideoDecoder;
+ mVideoDecoder = NULL;
+ delete mVideoSource;
+ mVideoSource = NULL;
+ mVideoWidth = mVideoHeight = 0;
+
+ delete mAudioDecoder;
+ mAudioDecoder = NULL;
+ delete mAudioSource;
+ mAudioSource = NULL;
+
+ delete mExtractor;
+ mExtractor = NULL;
+}
+
+int64_t NewPlayer::approxRealTime(int64_t mediatime_us) const {
+ return mRealTimeUsStart + (mediatime_us - mMediaTimeUsStart);
+}
+
+void NewPlayer::onStart(int64_t realtime_us) {
+ mRealTimeUsStart = TimedEventQueue::getRealTimeUs();
+
+ if (mVideoDecoder != NULL) {
+ mQueue.postEvent(new PlayerEvent(this, &NewPlayer::onFetchVideoFrame));
+ }
+}
+
+void NewPlayer::onFetchVideoFrame(int64_t realtime_us) {
+ MediaBuffer *buffer;
+ status_t err = mVideoDecoder->read(&buffer);
+ assert(err == OK);
+
+ int64_t mediatime_us = getMediaBufferTimeUs(buffer);
+
+ sp<TimedEventQueue::Event> event = new PlayVideoFrameEvent(this, buffer);
+ mPlayVideoFrameEvent = event;
+
+ mQueue.postTimedEvent(event, approxRealTime(mediatime_us));
+}
+
+// static
+int64_t NewPlayer::getMediaBufferTimeUs(MediaBuffer *buffer) {
+ int32_t units, scale;
+ bool success =
+ buffer->meta_data()->findInt32(kKeyTimeUnits, &units);
+ assert(success);
+ success =
+ buffer->meta_data()->findInt32(kKeyTimeScale, &scale);
+ assert(success);
+
+ return (int64_t)units * 1000000 / scale;
+}
+
+void NewPlayer::onPlayVideoFrame(int64_t realtime_us, MediaBuffer *buffer) {
+ LOGI("playing video frame (mediatime: %.2f sec)\n",
+ getMediaBufferTimeUs(buffer) / 1E6);
+ fflush(stdout);
+
+ buffer->release();
+ buffer = NULL;
+
+ mQueue.postEvent(new PlayerEvent(this, &NewPlayer::onFetchVideoFrame));
+}
+
+void NewPlayer::onPause(int64_t realtime_us) {
+}
+
+int main(int argc, char **argv) {
+ android::ProcessState::self()->startThreadPool();
+
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s filename\n", argv[0]);
+ return 1;
+ }
+
+ NewPlayer player;
+ player.setSource(argv[1]);
+ player.start();
+ sleep(10);
+ player.stop();
+
+ return 0;
+}
diff --git a/cmds/stagefright/record.cpp b/cmds/stagefright/record.cpp
new file mode 100644
index 000000000000..12bdead5c37c
--- /dev/null
+++ b/cmds/stagefright/record.cpp
@@ -0,0 +1,181 @@
+/*
+ * 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.
+ */
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <binder/ProcessState.h>
+#include <media/stagefright/CameraSource.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MPEG4Extractor.h>
+#include <media/stagefright/MPEG4Writer.h>
+#include <media/stagefright/MmapSource.h>
+#include <media/stagefright/OMXClient.h>
+#include <media/stagefright/OMXDecoder.h>
+
+using namespace android;
+
+class DummySource : public MediaSource {
+public:
+ DummySource(int width, int height)
+ : mSize((width * height * 3) / 2) {
+ mGroup.add_buffer(new MediaBuffer(mSize));
+ }
+
+ virtual ::status_t getMaxSampleSize(size_t *max_size) {
+ *max_size = mSize;
+ return ::OK;
+ }
+
+ virtual ::status_t read(MediaBuffer **buffer) {
+ ::status_t err = mGroup.acquire_buffer(buffer);
+ if (err != ::OK) {
+ return err;
+ }
+
+ char x = (char)((double)rand() / RAND_MAX * 255);
+ memset((*buffer)->data(), x, mSize);
+ (*buffer)->set_range(0, mSize);
+
+ return ::OK;
+ }
+
+private:
+ MediaBufferGroup mGroup;
+ size_t mSize;
+
+ DummySource(const DummySource &);
+ DummySource &operator=(const DummySource &);
+};
+
+int main(int argc, char **argv) {
+ android::ProcessState::self()->startThreadPool();
+
+#if 1
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s filename\n", argv[0]);
+ return 1;
+ }
+
+ MPEG4Extractor extractor(new MmapSource(argv[1]));
+ int num_tracks;
+ assert(extractor.countTracks(&num_tracks) == ::OK);
+
+ MediaSource *source = NULL;
+ sp<MetaData> meta;
+ for (int i = 0; i < num_tracks; ++i) {
+ meta = extractor.getTrackMetaData(i);
+ assert(meta.get() != NULL);
+
+ const char *mime;
+ if (!meta->findCString(kKeyMIMEType, &mime)) {
+ continue;
+ }
+
+ if (strncasecmp(mime, "video/", 6)) {
+ continue;
+ }
+
+ if (extractor.getTrack(i, &source) != ::OK) {
+ source = NULL;
+ continue;
+ }
+ break;
+ }
+
+ if (source == NULL) {
+ fprintf(stderr, "Unable to find a suitable video track.\n");
+ return 1;
+ }
+
+ OMXClient client;
+ assert(client.connect() == android::OK);
+
+ OMXDecoder *decoder = OMXDecoder::Create(&client, meta);
+ decoder->setSource(source);
+
+ int width, height;
+ bool success = meta->findInt32(kKeyWidth, &width);
+ success = success && meta->findInt32(kKeyHeight, &height);
+ assert(success);
+
+ sp<MetaData> enc_meta = new MetaData;
+ // enc_meta->setCString(kKeyMIMEType, "video/3gpp");
+ enc_meta->setCString(kKeyMIMEType, "video/mp4v-es");
+ enc_meta->setInt32(kKeyWidth, width);
+ enc_meta->setInt32(kKeyHeight, height);
+
+ OMXDecoder *encoder = OMXDecoder::CreateEncoder(&client, enc_meta);
+
+ encoder->setSource(decoder);
+ // encoder->setSource(meta, new DummySource(width, height));
+
+#if 1
+ MPEG4Writer writer("/sdcard/output.mp4");
+ writer.addSource(enc_meta, encoder);
+ writer.start();
+ sleep(120);
+ writer.stop();
+#else
+ encoder->start();
+
+ MediaBuffer *buffer;
+ while (encoder->read(&buffer) == ::OK) {
+ printf("got an output frame of size %d\n", buffer->range_length());
+
+ buffer->release();
+ buffer = NULL;
+ }
+
+ encoder->stop();
+#endif
+
+ delete encoder;
+ encoder = NULL;
+
+ delete decoder;
+ decoder = NULL;
+
+ client.disconnect();
+
+ delete source;
+ source = NULL;
+#endif
+
+#if 0
+ CameraSource *source = CameraSource::Create();
+ printf("source = %p\n", source);
+
+ for (int i = 0; i < 100; ++i) {
+ MediaBuffer *buffer;
+ status_t err = source->read(&buffer);
+ assert(err == OK);
+
+ printf("got a frame, data=%p, size=%d\n",
+ buffer->data(), buffer->range_length());
+
+ buffer->release();
+ buffer = NULL;
+ }
+
+ delete source;
+ source = NULL;
+#endif
+
+ return 0;
+}
+
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
new file mode 100644
index 000000000000..961942ad7ca2
--- /dev/null
+++ b/cmds/stagefright/stagefright.cpp
@@ -0,0 +1,200 @@
+/*
+ * 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.
+ */
+
+#include <sys/time.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <pthread.h>
+#include <stdlib.h>
+
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <media/IMediaPlayerService.h>
+#include <media/stagefright/AudioPlayer.h>
+#include <media/stagefright/CachingDataSource.h>
+#include <media/stagefright/ESDS.h>
+#include <media/stagefright/FileSource.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaPlayerImpl.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MmapSource.h>
+#include <media/stagefright/OMXClient.h>
+#include <media/stagefright/OMXDecoder.h>
+
+#include "WaveWriter.h"
+
+using namespace android;
+
+////////////////////////////////////////////////////////////////////////////////
+
+static bool convertToWav(
+ OMXClient *client, const sp<MetaData> &meta, MediaSource *source) {
+ printf("convertToWav\n");
+
+ OMXDecoder *decoder = OMXDecoder::Create(client, meta);
+
+ int32_t sampleRate;
+ bool success = meta->findInt32(kKeySampleRate, &sampleRate);
+ assert(success);
+
+ int32_t numChannels;
+ success = meta->findInt32(kKeyChannelCount, &numChannels);
+ assert(success);
+
+ const char *mime;
+ success = meta->findCString(kKeyMIMEType, &mime);
+ assert(success);
+
+ if (!strcasecmp("audio/3gpp", mime)) {
+ numChannels = 1; // XXX
+ }
+
+ WaveWriter writer("/sdcard/Music/shoutcast.wav", numChannels, sampleRate);
+
+ decoder->setSource(source);
+ for (int i = 0; i < 100; ++i) {
+ MediaBuffer *buffer;
+
+ ::status_t err = decoder->read(&buffer);
+ if (err != ::OK) {
+ break;
+ }
+
+ writer.Append((const char *)buffer->data() + buffer->range_offset(),
+ buffer->range_length());
+
+ buffer->release();
+ buffer = NULL;
+ }
+
+ delete decoder;
+ decoder = NULL;
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+static int64_t getNowUs() {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+
+ return (int64_t)tv.tv_usec + tv.tv_sec * 1000000;
+}
+
+int main(int argc, char **argv) {
+ android::ProcessState::self()->startThreadPool();
+
+ if (argc > 1 && !strcmp(argv[1], "--list")) {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("media.player"));
+ sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
+
+ assert(service.get() != NULL);
+
+ sp<IOMX> omx = service->createOMX();
+ assert(omx.get() != NULL);
+
+ List<String8> list;
+ omx->list_nodes(&list);
+
+ for (List<String8>::iterator it = list.begin();
+ it != list.end(); ++it) {
+ printf("%s\n", (*it).string());
+ }
+
+ return 0;
+ }
+
+#if 0
+ MediaPlayerImpl player(argv[1]);
+ player.play();
+
+ sleep(10000);
+#else
+ DataSource::RegisterDefaultSniffers();
+
+ OMXClient client;
+ status_t err = client.connect();
+
+ MmapSource *dataSource = new MmapSource(argv[1]);
+ MediaExtractor *extractor = MediaExtractor::Create(dataSource);
+ dataSource = NULL;
+
+ int numTracks;
+ err = extractor->countTracks(&numTracks);
+
+ sp<MetaData> meta;
+ int i;
+ for (i = 0; i < numTracks; ++i) {
+ meta = extractor->getTrackMetaData(i);
+
+ const char *mime;
+ meta->findCString(kKeyMIMEType, &mime);
+
+ if (!strncasecmp(mime, "video/", 6)) {
+ break;
+ }
+ }
+
+ OMXDecoder *decoder = OMXDecoder::Create(&client, meta);
+
+ if (decoder != NULL) {
+ MediaSource *source;
+ err = extractor->getTrack(i, &source);
+
+ decoder->setSource(source);
+
+ decoder->start();
+
+ int64_t startTime = getNowUs();
+
+ int n = 0;
+ MediaBuffer *buffer;
+ while ((err = decoder->read(&buffer)) == OK) {
+ if ((++n % 16) == 0) {
+ printf(".");
+ fflush(stdout);
+ }
+
+ buffer->release();
+ buffer = NULL;
+ }
+ decoder->stop();
+ printf("\n");
+
+ int64_t delay = getNowUs() - startTime;
+ printf("avg. %.2f fps\n", n * 1E6 / delay);
+
+ delete decoder;
+ decoder = NULL;
+
+ delete source;
+ source = NULL;
+ }
+
+ delete extractor;
+ extractor = NULL;
+
+ client.disconnect();
+#endif
+
+ return 0;
+}
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index ec7714d61481..ba6cc32808d4 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1068,6 +1068,23 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
unregisterActivityWatcher(watcher);
return true;
}
+
+ case START_ACTIVITY_IN_PACKAGE_TRANSACTION:
+ {
+ data.enforceInterface(IActivityManager.descriptor);
+ int uid = data.readInt();
+ Intent intent = Intent.CREATOR.createFromParcel(data);
+ String resolvedType = data.readString();
+ IBinder resultTo = data.readStrongBinder();
+ String resultWho = data.readString();
+ int requestCode = data.readInt();
+ boolean onlyIfNeeded = data.readInt() != 0;
+ int result = startActivityInPackage(uid, intent, resolvedType,
+ resultTo, resultWho, requestCode, onlyIfNeeded);
+ reply.writeNoException();
+ reply.writeInt(result);
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
@@ -2330,5 +2347,27 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle();
}
+ public int startActivityInPackage(int uid,
+ Intent intent, String resolvedType, IBinder resultTo,
+ String resultWho, int requestCode, boolean onlyIfNeeded)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(uid);
+ intent.writeToParcel(data, 0);
+ data.writeString(resolvedType);
+ data.writeStrongBinder(resultTo);
+ data.writeString(resultWho);
+ data.writeInt(requestCode);
+ data.writeInt(onlyIfNeeded ? 1 : 0);
+ mRemote.transact(START_ACTIVITY_IN_PACKAGE_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int result = reply.readInt();
+ reply.recycle();
+ data.recycle();
+ return result;
+ }
+
private IBinder mRemote;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index f2814f2acee6..76b47f1140c3 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -32,7 +32,6 @@ import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.pm.ServiceInfo;
-import android.content.pm.PackageParser.Component;
import android.content.res.AssetManager;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 133091255cd9..444f222e5bde 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -20,6 +20,7 @@ import com.android.internal.policy.PolicyManager;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.ComponentName;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
@@ -784,8 +785,17 @@ public class Dialog implements DialogInterface, Window.Callback,
* This hook is called when the user signals the desire to start a search.
*/
public boolean onSearchRequested() {
- // not during dialogs, no.
- return false;
+ final SearchManager searchManager = (SearchManager) mContext
+ .getSystemService(Context.SEARCH_SERVICE);
+
+ // associate search with owner activity if possible (otherwise it will default to
+ // global search).
+ final ComponentName appName = mOwnerActivity == null ? null
+ : mOwnerActivity.getComponentName();
+ final boolean globalSearch = (appName == null);
+ searchManager.startSearch(null, false, appName, null, globalSearch);
+ dismiss();
+ return true;
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index ee1b69b9af59..95b376cce673 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -262,6 +262,11 @@ public interface IActivityManager extends IInterface {
public void unregisterActivityWatcher(IActivityWatcher watcher)
throws RemoteException;
+ public int startActivityInPackage(int uid,
+ Intent intent, String resolvedType, IBinder resultTo,
+ String resultWho, int requestCode, boolean onlyIfNeeded)
+ throws RemoteException;
+
/*
* Private non-Binder interfaces
*/
@@ -415,4 +420,5 @@ public interface IActivityManager extends IInterface {
int UNBIND_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+91;
int REGISTER_ACTIVITY_WATCHER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+92;
int UNREGISTER_ACTIVITY_WATCHER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+93;
+ int START_ACTIVITY_IN_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+94;
}
diff --git a/core/java/android/app/ISearchManager.aidl b/core/java/android/app/ISearchManager.aidl
index 84a6085c4b75..bd725443e13a 100644
--- a/core/java/android/app/ISearchManager.aidl
+++ b/core/java/android/app/ISearchManager.aidl
@@ -37,4 +37,5 @@ interface ISearchManager {
ISearchManagerCallback searchManagerCallback,
int ident);
void stopSearch();
+ boolean isVisible();
}
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index 13eb0347e5a5..27c637652d5e 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -28,7 +28,6 @@ import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.drawable.Animatable;
@@ -90,6 +89,9 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
private static final String INSTANCE_KEY_STORED_APPDATA = "sData";
private static final String INSTANCE_KEY_PREVIOUS_COMPONENTS = "sPrev";
private static final String INSTANCE_KEY_USER_QUERY = "uQry";
+
+ // The extra key used in an intent to the speech recognizer for in-app voice search.
+ private static final String EXTRA_CALLING_PACKAGE = "calling_package";
private static final int SEARCH_PLATE_LEFT_PADDING_GLOBAL = 12;
private static final int SEARCH_PLATE_LEFT_PADDING_NON_GLOBAL = 7;
@@ -137,8 +139,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
// A weak map of drawables we've gotten from other packages, so we don't load them
// more than once.
- private final WeakHashMap<String, Drawable> mOutsideDrawablesCache =
- new WeakHashMap<String, Drawable>();
+ private final WeakHashMap<String, Drawable.ConstantState> mOutsideDrawablesCache =
+ new WeakHashMap<String, Drawable.ConstantState>();
// Last known IME options value for the search edit text.
private int mSearchAutoCompleteImeOptions;
@@ -319,16 +321,14 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
if (!globalSearch && mSearchable == null) {
globalSearch = true;
mSearchable = searchManager.getSearchableInfo(componentName, globalSearch);
-
- // If we still get back null (i.e., there's not even a searchable info available
- // for global search), then really give up.
- if (mSearchable == null) {
- // Unfortunately, we can't log here. it would be logspam every time the user
- // clicks the "search" key on a non-search app.
- return false;
- }
}
-
+
+ // If there's not even a searchable info available for global search, then really give up.
+ if (mSearchable == null) {
+ Log.w(LOG_TAG, "No global search provider.");
+ return false;
+ }
+
mLaunchComponent = componentName;
mAppSearchData = appSearchData;
// Using globalSearch here is just an optimization, just calling
@@ -337,16 +337,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
mActivityContext = mSearchable.getActivityContext(getContext());
// show the dialog. this will call onStart().
- if (!isShowing()) {
- // First make sure the keyboard is showing (if needed), so that we get the right height
- // for the dropdown to respect the IME.
- if (getContext().getResources().getConfiguration().hardKeyboardHidden ==
- Configuration.HARDKEYBOARDHIDDEN_YES) {
- InputMethodManager inputManager = (InputMethodManager)
- getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
- inputManager.showSoftInputUnchecked(0, null);
- }
-
+ if (!isShowing()) {
// The Dialog uses a ContextThemeWrapper for the context; use this to change the
// theme out from underneath us, between the global search theme and the in-app
// search theme. They are identical except that the global search theme does not
@@ -363,7 +354,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
}
show();
}
-
updateUI();
return true;
@@ -499,6 +489,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
*/
private void updateUI() {
if (mSearchable != null) {
+ mDecor.setVisibility(View.VISIBLE);
updateSearchAutoComplete();
updateSearchButton();
updateSearchAppIcon();
@@ -708,7 +699,10 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (DBG) Log.d(LOG_TAG, "onKeyDown(" + keyCode + "," + event + ")");
-
+ if (mSearchable == null) {
+ return false;
+ }
+
// handle back key to go back to previous searchable, etc.
if (handleBackKey(keyCode, event)) {
return true;
@@ -744,6 +738,9 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
if (DBG_LOG_TIMING) {
dbgLogTiming("onTextChanged()");
}
+ if (mSearchable == null) {
+ return;
+ }
updateWidgetState();
if (!mSearchAutoComplete.isPerformingCompletion()) {
// The user changed the query, remember it.
@@ -856,11 +853,13 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
* @return A completely-configured intent ready to send to the voice search activity
*/
private Intent createVoiceAppSearchIntent(Intent baseIntent) {
+ ComponentName searchActivity = mSearchable.getSearchActivity();
+
// create the necessary intent to set up a search-and-forward operation
// in the voice search system. We have to keep the bundle separate,
// because it becomes immutable once it enters the PendingIntent
Intent queryIntent = new Intent(Intent.ACTION_SEARCH);
- queryIntent.setComponent(mSearchable.getSearchActivity());
+ queryIntent.setComponent(searchActivity);
PendingIntent pending = PendingIntent.getActivity(
getContext(), 0, queryIntent, PendingIntent.FLAG_ONE_SHOT);
@@ -900,6 +899,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
voiceIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt);
voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, language);
voiceIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, maxResults);
+ voiceIntent.putExtra(EXTRA_CALLING_PACKAGE,
+ searchActivity == null ? null : searchActivity.toShortString());
// Add the values that configure forwarding the results
voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT, pending);
@@ -993,7 +994,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
};
@Override
- public void dismiss() {
+ public void hide() {
if (!isShowing()) return;
// We made sure the IME was displayed, so also make sure it is closed
@@ -1004,10 +1005,10 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
imm.hideSoftInputFromWindow(
getWindow().getDecorView().getWindowToken(), 0);
}
-
- super.dismiss();
+
+ super.hide();
}
-
+
/**
* React to the user typing while in the suggestions list. First, check for action
* keys. If not handled, try refocusing regular characters into the EditText.
@@ -1041,6 +1042,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
mSearchAutoComplete.setSelection(selPoint);
mSearchAutoComplete.setListSelection(0);
mSearchAutoComplete.clearListSelection();
+ mSearchAutoComplete.ensureImeVisible();
+
return true;
}
@@ -1231,8 +1234,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
}
/**
- * Launches an intent and dismisses the search dialog (unless the intent
- * is one of the special intents that modifies the state of the search dialog).
+ * Launches an intent, including any special intent handling. Doesn't dismiss the dialog
+ * since that will be handled in {@link SearchDialogWrapper#performActivityResuming}
*/
private void launchIntent(Intent intent) {
if (intent == null) {
@@ -1241,8 +1244,15 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
if (handleSpecialIntent(intent)){
return;
}
- dismiss();
+ Log.d(LOG_TAG, "launching " + intent);
getContext().startActivity(intent);
+
+ // in global search mode, SearchDialogWrapper#performActivityResuming will handle hiding
+ // the dialog when the next activity starts, but for in-app search, we still need to
+ // dismiss the dialog.
+ if (!mGlobalSearchMode) {
+ dismiss();
+ }
}
/**
@@ -1535,7 +1545,22 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
@Override
public void performCompletion() {
}
-
+
+ /**
+ * We override this method to be sure and show the soft keyboard if appropriate when
+ * the TextView has focus.
+ */
+ @Override
+ public void onWindowFocusChanged(boolean hasWindowFocus) {
+ super.onWindowFocusChanged(hasWindowFocus);
+
+ if (hasWindowFocus) {
+ InputMethodManager inputManager = (InputMethodManager)
+ getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+ inputManager.showSoftInput(this, 0);
+ }
+ }
+
/**
* We override this method so that we can allow a threshold of zero, which ACTV does not.
*/
@@ -1550,6 +1575,9 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
*/
@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
+ if (mSearchDialog.mSearchable == null) {
+ return false;
+ }
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) {
if (mSearchDialog.backToPreviousComponent()) {
return true;
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index b795a5431ede..c98d966ce7bd 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -1188,8 +1188,6 @@ public class SearchManager
/**
* Intent extra data key: This key will be used for the extra populated by the
* {@link #SUGGEST_COLUMN_INTENT_EXTRA_DATA} column.
- *
- * {@hide}
*/
public final static String EXTRA_DATA_KEY = "intent_extra_data_key";
@@ -1269,16 +1267,12 @@ public class SearchManager
* result indicates the shortcut refers to a no longer valid sugggestion.
*
* @see #SUGGEST_COLUMN_SHORTCUT_ID
- *
- * @hide pending API council approval
*/
public final static String SUGGEST_URI_PATH_SHORTCUT = "search_suggest_shortcut";
/**
* MIME type for shortcut validation. You'll use this in your suggestions content provider
* in the getType() function.
- *
- * @hide pending API council approval
*/
public final static String SHORTCUT_MIME_TYPE =
"vnd.android.cursor.item/vnd.android.search.suggest";
@@ -1389,9 +1383,7 @@ public class SearchManager
* this element exists at the given row, this is the data that will be used when
* forming the suggestion's intent. If not provided, the Intent's extra data field will be null.
* This column allows suggestions to provide additional arbitrary data which will be included as
- * an extra under the key EXTRA_DATA_KEY.
- *
- * @hide Pending API council approval.
+ * an extra under the key {@link #EXTRA_DATA_KEY}.
*/
public final static String SUGGEST_COLUMN_INTENT_EXTRA_DATA = "suggest_intent_extra_data";
/**
@@ -1425,8 +1417,6 @@ public class SearchManager
* {@link #SUGGEST_NEVER_MAKE_SHORTCUT}, the result will not be stored as a shortcut.
* Otherwise, the shortcut id will be used to check back for validation via
* {@link #SUGGEST_URI_PATH_SHORTCUT}.
- *
- * @hide Pending API council approval.
*/
public final static String SUGGEST_COLUMN_SHORTCUT_ID = "suggest_shortcut_id";
@@ -1443,8 +1433,6 @@ public class SearchManager
* Column name for suggestions cursor. <i>Optional.</i> This column is used to specify
* that a spinner should be shown in lieu of an icon2 while the shortcut of this suggestion
* is being refreshed.
- *
- * @hide Pending API council approval.
*/
public final static String SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING =
"suggest_spinner_while_refreshing";
@@ -1452,8 +1440,6 @@ public class SearchManager
/**
* Column value for suggestion column {@link #SUGGEST_COLUMN_SHORTCUT_ID} when a suggestion
* should not be stored as a shortcut in global search.
- *
- * @hide Pending API council approval.
*/
public final static String SUGGEST_NEVER_MAKE_SHORTCUT = "_-1";
@@ -1500,8 +1486,6 @@ public class SearchManager
* Intent action for starting a web search provider's settings activity.
* Web search providers should handle this intent if they have provider-specific
* settings to implement.
- *
- * @hide Pending API council approval.
*/
public final static String INTENT_ACTION_WEB_SEARCH_SETTINGS
= "android.search.action.WEB_SEARCH_SETTINGS";
@@ -1510,11 +1494,17 @@ public class SearchManager
* Intent action broadcasted to inform that the searchables list or default have changed.
* Components should handle this intent if they cache any searchable data and wish to stay
* up to date on changes.
- *
- * @hide Pending API council approval.
*/
public final static String INTENT_ACTION_SEARCHABLES_CHANGED
= "android.search.action.SEARCHABLES_CHANGED";
+
+ /**
+ * Intent action broadcasted to inform that the search settings have changed in some way.
+ * Either searchables have been enabled or disabled, or a different web search provider
+ * has been chosen.
+ */
+ public final static String INTENT_ACTION_SEARCH_SETTINGS_CHANGED
+ = "android.search.action.SETTINGS_CHANGED";
/**
* If a suggestion has this value in {@link #SUGGEST_COLUMN_INTENT_ACTION},
@@ -1534,7 +1524,6 @@ public class SearchManager
private int mIdent;
// package private since they are used by the inner class SearchManagerCallback
- /* package */ boolean mIsShowing = false;
/* package */ final Handler mHandler;
/* package */ OnDismissListener mDismissListener = null;
/* package */ OnCancelListener mCancelListener = null;
@@ -1600,12 +1589,9 @@ public class SearchManager
ComponentName launchActivity,
Bundle appSearchData,
boolean globalSearch) {
- if (DBG) debug("startSearch(), mIsShowing=" + mIsShowing);
- if (mIsShowing) return;
if (mIdent == 0) throw new IllegalArgumentException(
"Called from outside of an Activity context");
try {
- mIsShowing = true;
// activate the search manager and start it up!
mService.startSearch(initialQuery, selectInitialQuery, launchActivity, appSearchData,
globalSearch, mSearchManagerCallback, mIdent);
@@ -1626,15 +1612,10 @@ public class SearchManager
* @see #startSearch
*/
public void stopSearch() {
- if (DBG) debug("stopSearch(), mIsShowing=" + mIsShowing);
- if (!mIsShowing) return;
+ if (DBG) debug("stopSearch()");
try {
mService.stopSearch();
- // onDismiss will also clear this, but we do it here too since onDismiss() is
- // called asynchronously.
- mIsShowing = false;
} catch (RemoteException ex) {
- Log.e(TAG, "stopSearch() failed: " + ex);
}
}
@@ -1648,8 +1629,13 @@ public class SearchManager
* @hide
*/
public boolean isVisible() {
- if (DBG) debug("isVisible(), mIsShowing=" + mIsShowing);
- return mIsShowing;
+ if (DBG) debug("isVisible()");
+ try {
+ return mService.isVisible();
+ } catch (RemoteException e) {
+ Log.e(TAG, "isVisible() failed: " + e);
+ return false;
+ }
}
/**
@@ -1701,7 +1687,6 @@ public class SearchManager
private final Runnable mFireOnDismiss = new Runnable() {
public void run() {
if (DBG) debug("mFireOnDismiss");
- mIsShowing = false;
if (mDismissListener != null) {
mDismissListener.onDismiss();
}
@@ -1711,7 +1696,6 @@ public class SearchManager
private final Runnable mFireOnCancel = new Runnable() {
public void run() {
if (DBG) debug("mFireOnCancel");
- // doesn't need to clear mIsShowing since onDismiss() always gets called too
if (mCancelListener != null) {
mCancelListener.onCancel();
}
diff --git a/core/java/android/app/SuggestionsAdapter.java b/core/java/android/app/SuggestionsAdapter.java
index 58e66b634ba3..593b7b736cfa 100644
--- a/core/java/android/app/SuggestionsAdapter.java
+++ b/core/java/android/app/SuggestionsAdapter.java
@@ -23,25 +23,24 @@ import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.database.Cursor;
-import android.graphics.Canvas;
+import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.StateListDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.server.search.SearchableInfo;
import android.text.Html;
import android.text.TextUtils;
-import android.util.DisplayMetrics;
import android.util.Log;
-import android.util.TypedValue;
+import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.AbsListView;
import android.widget.ImageView;
import android.widget.ResourceCursorAdapter;
import android.widget.TextView;
+import android.widget.Filter;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -62,7 +61,8 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
private SearchDialog mSearchDialog;
private SearchableInfo mSearchable;
private Context mProviderContext;
- private WeakHashMap<String, Drawable> mOutsideDrawablesCache;
+ private WeakHashMap<String, Drawable.ConstantState> mOutsideDrawablesCache;
+ private SparseArray<Drawable.ConstantState> mBackgroundsCache;
private boolean mGlobalSearchMode;
// Cached column indexes, updated when the cursor changes.
@@ -91,8 +91,16 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
private final Runnable mStartSpinnerRunnable;
private final Runnable mStopSpinnerRunnable;
- public SuggestionsAdapter(Context context, SearchDialog searchDialog, SearchableInfo searchable,
- WeakHashMap<String, Drawable> outsideDrawablesCache, boolean globalSearchMode) {
+ /**
+ * The amount of time we delay in the filter when the user presses the delete key.
+ * @see Filter#setDelayer(android.widget.Filter.Delayer).
+ */
+ private static final long DELETE_KEY_POST_DELAY = 500L;
+
+ public SuggestionsAdapter(Context context, SearchDialog searchDialog,
+ SearchableInfo searchable,
+ WeakHashMap<String, Drawable.ConstantState> outsideDrawablesCache,
+ boolean globalSearchMode) {
super(context,
com.android.internal.R.layout.search_dropdown_item_icons_2line,
null, // no initial cursor
@@ -106,6 +114,7 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
mProviderContext = mSearchable.getProviderContext(mContext, activityContext);
mOutsideDrawablesCache = outsideDrawablesCache;
+ mBackgroundsCache = new SparseArray<Drawable.ConstantState>();
mGlobalSearchMode = globalSearchMode;
mStartSpinnerRunnable = new Runnable() {
@@ -119,6 +128,18 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
mSearchDialog.setWorking(false);
}
};
+
+ // delay 500ms when deleting
+ getFilter().setDelayer(new Filter.Delayer() {
+
+ private int mPreviousLength = 0;
+
+ public long getPostingDelay(CharSequence constraint) {
+ long delay = constraint.length() < mPreviousLength ? DELETE_KEY_POST_DELAY : 0;
+ mPreviousLength = constraint.length();
+ return delay;
+ }
+ });
}
/**
@@ -256,7 +277,7 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
*/
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
- View v = new SuggestionItemView(context, cursor);
+ View v = super.newView(context, cursor, parent);
v.setTag(new ChildViewCache(v));
return v;
}
@@ -301,18 +322,13 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
if (mBackgroundColorCol != -1) {
backgroundColor = cursor.getInt(mBackgroundColorCol);
}
- ((SuggestionItemView)view).setColor(backgroundColor);
+ Drawable background = getItemBackground(backgroundColor);
+ view.setBackgroundDrawable(background);
final boolean isHtml = mFormatCol > 0 && "html".equals(cursor.getString(mFormatCol));
- String text1 = null;
- if (mText1Col >= 0) {
- text1 = cursor.getString(mText1Col);
- }
- String text2 = null;
- if (mText2Col >= 0) {
- text2 = cursor.getString(mText2Col);
- }
- ((SuggestionItemView)view).setTextStrings(text1, text2, isHtml, mProviderContext);
+ setViewText(cursor, views.mText1, mText1Col, isHtml);
+ setViewText(cursor, views.mText2, mText2Col, isHtml);
+
if (views.mIcon1 != null) {
setViewDrawable(views.mIcon1, getIcon1(cursor));
}
@@ -321,6 +337,65 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
}
}
+ /**
+ * Gets a drawable with no color when selected or pressed, and the given color when
+ * neither selected nor pressed.
+ *
+ * @return A drawable, or {@code null} if the given color is transparent.
+ */
+ private Drawable getItemBackground(int backgroundColor) {
+ if (backgroundColor == 0) {
+ return null;
+ } else {
+ Drawable.ConstantState cachedBg = mBackgroundsCache.get(backgroundColor);
+ if (cachedBg != null) {
+ if (DBG) Log.d(LOG_TAG, "Background cache hit for color " + backgroundColor);
+ return cachedBg.newDrawable();
+ }
+ if (DBG) Log.d(LOG_TAG, "Creating new background for color " + backgroundColor);
+ ColorDrawable transparent = new ColorDrawable(0);
+ ColorDrawable background = new ColorDrawable(backgroundColor);
+ StateListDrawable newBg = new StateListDrawable();
+ newBg.addState(new int[]{android.R.attr.state_selected}, transparent);
+ newBg.addState(new int[]{android.R.attr.state_pressed}, transparent);
+ newBg.addState(new int[]{}, background);
+ mBackgroundsCache.put(backgroundColor, newBg.getConstantState());
+ return newBg;
+ }
+ }
+
+ private void setViewText(Cursor cursor, TextView v, int textCol, boolean isHtml) {
+ if (v == null) {
+ return;
+ }
+ CharSequence text = null;
+ if (textCol >= 0) {
+ String str = cursor.getString(textCol);
+ if (isHtml && looksLikeHtml(str)) {
+ text = Html.fromHtml(str);
+ } else {
+ text = str;
+ }
+ }
+ // Set the text even if it's null, since we need to clear any previous text.
+ v.setText(text);
+
+ if (TextUtils.isEmpty(text)) {
+ v.setVisibility(View.GONE);
+ } else {
+ v.setVisibility(View.VISIBLE);
+ }
+ }
+
+ private static boolean looksLikeHtml(String str) {
+ if (TextUtils.isEmpty(str)) return false;
+ for (int i = str.length() - 1; i >= 0; i--) {
+ char c = str.charAt(i);
+ if (c == '<' || c == '&') return true;
+ }
+ return false;
+ }
+
private Drawable getIcon1(Cursor cursor) {
if (mIconName1Col < 0) {
return null;
@@ -449,12 +524,13 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
}
// First, check the cache.
- Drawable drawable = mOutsideDrawablesCache.get(drawableId);
- if (drawable != null) {
+ Drawable.ConstantState cached = mOutsideDrawablesCache.get(drawableId);
+ if (cached != null) {
if (DBG) Log.d(LOG_TAG, "Found icon in cache: " + drawableId);
- return drawable;
+ return cached.newDrawable();
}
+ Drawable drawable = null;
try {
// Not cached, try using it as a plain resource ID in the provider's context.
int resourceId = Integer.parseInt(drawableId);
@@ -486,7 +562,7 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
// If we got a drawable for this resource id, then stick it in the
// map so we don't do this lookup again.
if (drawable != null) {
- mOutsideDrawablesCache.put(drawableId, drawable);
+ mOutsideDrawablesCache.put(drawableId, drawable.getConstantState());
}
} catch (Resources.NotFoundException nfe) {
if (DBG) Log.d(LOG_TAG, "Icon resource not found: " + drawableId);
@@ -541,12 +617,14 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
String componentIconKey = component.flattenToShortString();
// Using containsKey() since we also store null values.
if (mOutsideDrawablesCache.containsKey(componentIconKey)) {
- return mOutsideDrawablesCache.get(componentIconKey);
+ Drawable.ConstantState cached = mOutsideDrawablesCache.get(componentIconKey);
+ return cached == null ? null : cached.newDrawable();
}
// Then try the activity or application icon
Drawable drawable = getActivityIcon(component);
// Stick it in the cache so we don't do this lookup again.
- mOutsideDrawablesCache.put(componentIconKey, drawable);
+ Drawable.ConstantState toCache = drawable == null ? null : drawable.getConstantState();
+ mOutsideDrawablesCache.put(componentIconKey, toCache);
return drawable;
}
@@ -594,179 +672,4 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
return cursor.getString(col);
}
- /**
- * A parent viewgroup class which holds the actual suggestion item as a child.
- *
- * The sole purpose of this class is to draw the given background color when the item is in
- * normal state and not draw the background color when it is pressed, so that when pressed the
- * list view's selection highlight will be displayed properly (if we draw our background it
- * draws on top of the list view selection highlight).
- */
- private class SuggestionItemView extends ViewGroup {
- /**
- * Parses a given HTMl string and manages Spannable variants of the string for different
- * states of the suggestion item (selected, pressed and normal). Colors for these different
- * states are specified in the html font tag color attribute in the format '@<RESOURCEID>'
- * where RESOURCEID is the ID of a ColorStateList or Color resource.
- */
- private class MultiStateText {
- private CharSequence mNormal = null; // text to display in normal state.
- private CharSequence mSelected = null; // text to display in selected state.
- private CharSequence mPressed = null; // text to display in pressed state.
- private String mPlainText = null; // valid if the text is stateless plain text.
-
- public MultiStateText(boolean isHtml, String text, Context context) {
- if (!isHtml || text == null) {
- mPlainText = text;
- return;
- }
-
- String textNormal = text;
- String textSelected = text;
- String textPressed = text;
- int textLength = text.length();
- int start = text.indexOf("\"@");
-
- // For each font color attribute which has the value in the form '@<RESOURCEID>',
- // try to load the resource and create the display strings for the 3 states.
- while (start >= 0) {
- start++;
- int end = text.indexOf("\"", start);
- if (end == -1) break;
-
- String colorIdString = text.substring(start, end);
- int colorId = Integer.parseInt(colorIdString.substring(1));
- try {
- // The following call works both for color lists and colors.
- ColorStateList csl = context.getResources().getColorStateList(colorId);
- int normalColor = csl.getColorForState(
- View.EMPTY_STATE_SET, csl.getDefaultColor());
- int selectedColor = csl.getColorForState(
- View.SELECTED_STATE_SET, csl.getDefaultColor());
- int pressedColor = csl.getColorForState(
- View.PRESSED_STATE_SET, csl.getDefaultColor());
-
- // Convert the int color values into a hex string, and strip the first 2
- // characters which will be the alpha (html doesn't want this).
- textNormal = textNormal.replace(colorIdString,
- "#" + Integer.toHexString(normalColor).substring(2));
- textSelected = textSelected.replace(colorIdString,
- "#" + Integer.toHexString(selectedColor).substring(2));
- textPressed = textPressed.replace(colorIdString,
- "#" + Integer.toHexString(pressedColor).substring(2));
- } catch (Resources.NotFoundException e) {
- // Nothing to do.
- }
-
- start = text.indexOf("\"@", end);
- }
- mNormal = Html.fromHtml(textNormal);
- mSelected = Html.fromHtml(textSelected);
- mPressed = Html.fromHtml(textPressed);
- }
- public CharSequence normal() {
- return (mPlainText != null) ? mPlainText : mNormal;
- }
- public CharSequence selected() {
- return (mPlainText != null) ? mPlainText : mSelected;
- }
- public CharSequence pressed() {
- return (mPlainText != null) ? mPlainText : mPressed;
- }
- }
-
- private int mBackgroundColor; // the background color to draw in normal state.
- private View mView; // the suggestion item's view.
- private MultiStateText mText1Strings = null;
- private MultiStateText mText2Strings = null;
-
- protected SuggestionItemView(Context context, Cursor cursor) {
- // Initialize ourselves
- super(context);
- mBackgroundColor = 0; // transparent by default.
-
- // For our layout use the default list item height from the current theme.
- TypedValue lineHeight = new TypedValue();
- context.getTheme().resolveAttribute(
- com.android.internal.R.attr.searchResultListItemHeight, lineHeight, true);
- DisplayMetrics metrics = new DisplayMetrics();
- metrics.setToDefaults();
- AbsListView.LayoutParams layout = new AbsListView.LayoutParams(
- AbsListView.LayoutParams.FILL_PARENT,
- (int)lineHeight.getDimension(metrics));
-
- setLayoutParams(layout);
-
- // Initialize the child view
- mView = SuggestionsAdapter.super.newView(context, cursor, this);
- if (mView != null) {
- addView(mView, layout.width, layout.height);
- mView.setVisibility(View.VISIBLE);
- }
- }
-
- private void setInitialTextForView(TextView view, MultiStateText multiState,
- String plainText) {
- // Set the text even if it's null, since we need to clear any previous text.
- CharSequence text = (multiState != null) ? multiState.normal() : plainText;
- view.setText(text);
-
- if (TextUtils.isEmpty(text)) {
- view.setVisibility(View.GONE);
- } else {
- view.setVisibility(View.VISIBLE);
- }
- }
-
- public void setTextStrings(String text1, String text2, boolean isHtml, Context context) {
- mText1Strings = new MultiStateText(isHtml, text1, context);
- mText2Strings = new MultiStateText(isHtml, text2, context);
-
- ChildViewCache views = (ChildViewCache) getTag();
- setInitialTextForView(views.mText1, mText1Strings, text1);
- setInitialTextForView(views.mText2, mText2Strings, text2);
- }
-
- public void updateTextViewContentIfRequired() {
- // Check if the pressed or selected state has changed since the last call.
- boolean isPressedNow = isPressed();
- boolean isSelectedNow = isSelected();
-
- ChildViewCache views = (ChildViewCache) getTag();
- views.mText1.setText((isPressedNow ? mText1Strings.pressed() :
- (isSelectedNow ? mText1Strings.selected() : mText1Strings.normal())));
- views.mText2.setText((isPressedNow ? mText2Strings.pressed() :
- (isSelectedNow ? mText2Strings.selected() : mText2Strings.normal())));
- }
-
- public void setColor(int backgroundColor) {
- mBackgroundColor = backgroundColor;
- }
-
- @Override
- public void dispatchDraw(Canvas canvas) {
- updateTextViewContentIfRequired();
-
- if (mBackgroundColor != 0 && !isPressed() && !isSelected()) {
- canvas.drawColor(mBackgroundColor);
- }
- super.dispatchDraw(canvas);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- if (mView != null) {
- mView.measure(widthMeasureSpec, heightMeasureSpec);
- }
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- if (mView != null) {
- mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
- }
- }
- }
-
}
diff --git a/core/java/android/appwidget/AppWidgetProvider.java b/core/java/android/appwidget/AppWidgetProvider.java
index 26712a10f0ce..f1bbedef9024 100755
--- a/core/java/android/appwidget/AppWidgetProvider.java
+++ b/core/java/android/appwidget/AppWidgetProvider.java
@@ -64,11 +64,9 @@ public class AppWidgetProvider extends BroadcastReceiver {
}
else if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
Bundle extras = intent.getExtras();
- if (extras != null) {
- int[] appWidgetIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
- if (appWidgetIds != null && appWidgetIds.length > 0) {
- this.onDeleted(context, appWidgetIds);
- }
+ if (extras != null && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)) {
+ final int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
+ this.onDeleted(context, new int[] { appWidgetId });
}
}
else if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)) {
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index 8530c355b935..a2e0ba0a4c26 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -57,6 +57,9 @@ public class AppWidgetProviderInfo implements Parcelable {
*
* <p>This field corresponds to the <code>android:updatePeriodMillis</code> attribute in
* the AppWidget meta-data file.
+ *
+ * <p class="note"><b>Note:</b> Updates requested with <code>updatePeriodMillis</code>
+ * will not be delivered more than once every 30 minutes.</p>
*/
public int updatePeriodMillis;
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index c942a27e8cd1..a64c6d72d43c 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -74,6 +74,14 @@ public class BluetoothDevice {
/** An existing bond was explicitly revoked */
public static final int UNBOND_REASON_REMOVED = 6;
+ /* The user will be prompted to enter a pin */
+ public static final int PAIRING_VARIANT_PIN = 0;
+ /* The user will be prompted to enter a passkey */
+ public static final int PAIRING_VARIANT_PASSKEY = 1;
+ /* The user will be prompted to confirm the passkey displayed on the screen */
+ public static final int PAIRING_VARIANT_CONFIRMATION = 2;
+
+
private static final String TAG = "BluetoothDevice";
private final IBluetoothDevice mService;
@@ -358,9 +366,24 @@ public class BluetoothDevice {
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
- public boolean cancelPin(String address) {
+
+ public boolean setPasskey(String address, int passkey) {
+ try {
+ return mService.setPasskey(address, passkey);
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return false;
+ }
+
+ public boolean setPairingConfirmation(String address, boolean confirm) {
+ try {
+ return mService.setPairingConfirmation(address, confirm);
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return false;
+ }
+
+ public boolean cancelPairingUserInput(String address) {
try {
- return mService.cancelPin(address);
+ return mService.cancelPairingUserInput(address);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothIntent.java b/core/java/android/bluetooth/BluetoothIntent.java
index 344601b0b89a..d6c79b404909 100644
--- a/core/java/android/bluetooth/BluetoothIntent.java
+++ b/core/java/android/bluetooth/BluetoothIntent.java
@@ -57,6 +57,10 @@ public interface BluetoothIntent {
"android.bluetooth.intent.BOND_PREVIOUS_STATE";
public static final String REASON =
"android.bluetooth.intent.REASON";
+ public static final String PAIRING_VARIANT =
+ "android.bluetooth.intent.PAIRING_VARIANT";
+ public static final String PASSKEY =
+ "android.bluetooth.intent.PASSKEY";
/** Broadcast when the local Bluetooth device state changes, for example
* when Bluetooth is enabled. Will contain int extra's BLUETOOTH_STATE and
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index 96b93f9b18ee..f8316a5bac9a 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -52,7 +52,7 @@ public final class BluetoothUuid {
}
public static boolean isHandsfree(UUID uuid) {
- return uuid.equals(Handsfree) || uuid.equals(HandsfreeAudioGateway);
+ return uuid.equals(Handsfree);
}
public static boolean isHeadset(UUID uuid) {
diff --git a/core/java/android/bluetooth/IBluetoothDevice.aidl b/core/java/android/bluetooth/IBluetoothDevice.aidl
index c249c817a464..a78752bff5e5 100644
--- a/core/java/android/bluetooth/IBluetoothDevice.aidl
+++ b/core/java/android/bluetooth/IBluetoothDevice.aidl
@@ -54,5 +54,8 @@ interface IBluetoothDevice
int getRemoteServiceChannel(in String address, String uuid);
boolean setPin(in String address, in byte[] pin);
- boolean cancelPin(in String address);
+ boolean setPasskey(in String address, int passkey);
+ boolean setPairingConfirmation(in String address, boolean confirm);
+ boolean cancelPairingUserInput(in String address);
+
}
diff --git a/core/java/android/content/AbstractTableMerger.java b/core/java/android/content/AbstractTableMerger.java
index 3266c07a7ff3..a3daa01e8317 100644
--- a/core/java/android/content/AbstractTableMerger.java
+++ b/core/java/android/content/AbstractTableMerger.java
@@ -369,30 +369,33 @@ public abstract class AbstractTableMerger
// An existing server item has changed
// If serverSyncVersion is null, there is no edit URL;
// server won't let this change be written.
- // Just hold onto it, I guess, in case the server permissions
- // change later.
- if (serverSyncVersion != null) {
- boolean recordChanged = (localSyncVersion == null) ||
- !serverSyncVersion.equals(localSyncVersion);
- if (recordChanged) {
- if (localSyncDirty) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "remote record " + serverSyncId
- + " conflicts with local _sync_id " + localSyncID
- + ", local _id " + localRowId);
- }
- conflict = true;
- } else {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG,
- "remote record " +
- serverSyncId +
- " updates local _sync_id " +
- localSyncID + ", local _id " +
- localRowId);
- }
- update = true;
+ boolean recordChanged = (localSyncVersion == null) ||
+ (serverSyncVersion == null) ||
+ !serverSyncVersion.equals(localSyncVersion);
+ if (recordChanged) {
+ if (localSyncDirty) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "remote record " + serverSyncId
+ + " conflicts with local _sync_id " + localSyncID
+ + ", local _id " + localRowId);
}
+ conflict = true;
+ } else {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG,
+ "remote record " +
+ serverSyncId +
+ " updates local _sync_id " +
+ localSyncID + ", local _id " +
+ localRowId);
+ }
+ update = true;
+ }
+ } else {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG,
+ "Skipping update: localSyncVersion: " + localSyncVersion +
+ ", serverSyncVersion: " + serverSyncVersion);
}
}
} else {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 54d2e76a86b6..5be8100d3e24 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -75,10 +75,10 @@ import java.util.Set;
* <p>Some examples of action/data pairs are:</p>
*
* <ul>
- * <li> <p><b>{@link #ACTION_VIEW} <i>content://contacts/1</i></b> -- Display
+ * <li> <p><b>{@link #ACTION_VIEW} <i>content://contacts/people/1</i></b> -- Display
* information about the person whose identifier is "1".</p>
* </li>
- * <li> <p><b>{@link #ACTION_DIAL} <i>content://contacts/1</i></b> -- Display
+ * <li> <p><b>{@link #ACTION_DIAL} <i>content://contacts/people/1</i></b> -- Display
* the phone dialer with the person filled in.</p>
* </li>
* <li> <p><b>{@link #ACTION_VIEW} <i>tel:123</i></b> -- Display
@@ -89,10 +89,10 @@ import java.util.Set;
* <li> <p><b>{@link #ACTION_DIAL} <i>tel:123</i></b> -- Display
* the phone dialer with the given number filled in.</p>
* </li>
- * <li> <p><b>{@link #ACTION_EDIT} <i>content://contacts/1</i></b> -- Edit
+ * <li> <p><b>{@link #ACTION_EDIT} <i>content://contacts/people/1</i></b> -- Edit
* information about the person whose identifier is "1".</p>
* </li>
- * <li> <p><b>{@link #ACTION_VIEW} <i>content://contacts/</i></b> -- Display
+ * <li> <p><b>{@link #ACTION_VIEW} <i>content://contacts/people/</i></b> -- Display
* a list of people, which the user can browse through. This example is a
* typical top-level entry into the Contacts application, showing you the
* list of people. Selecting a particular person to view would result in a
@@ -156,7 +156,7 @@ import java.util.Set;
* defined in the Intent class, but applications can also define their own.
* These strings use java style scoping, to ensure they are unique -- for
* example, the standard {@link #ACTION_VIEW} is called
- * "android.app.action.VIEW".</p>
+ * "android.intent.action.VIEW".</p>
*
* <p>Put together, the set of actions, data types, categories, and extra data
* defines a language for the system allowing for the expression of phrases
@@ -347,7 +347,7 @@ import java.util.Set;
* <li> <p><b>{ action=android.app.action.MAIN,
* category=android.app.category.LAUNCHER }</b> is the actual intent
* used by the Launcher to populate its top-level list.</p>
- * <li> <p><b>{ action=android.app.action.VIEW
+ * <li> <p><b>{ action=android.intent.action.VIEW
* data=content://com.google.provider.NotePad/notes }</b>
* displays a list of all the notes under
* "content://com.google.provider.NotePad/notes", which
@@ -399,7 +399,7 @@ import java.util.Set;
* NoteEditor activity:</p>
*
* <ul>
- * <li> <p><b>{ action=android.app.action.VIEW
+ * <li> <p><b>{ action=android.intent.action.VIEW
* data=content://com.google.provider.NotePad/notes/<var>{ID}</var> }</b>
* shows the user the content of note <var>{ID}</var>.</p>
* <li> <p><b>{ action=android.app.action.EDIT
@@ -1684,6 +1684,53 @@ public class Intent implements Parcelable {
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_REBOOT =
"android.intent.action.REBOOT";
+ /**
+ * Broadcast Action: Triggers the platform Text-To-Speech engine to
+ * start the activity that installs the resource files on the device
+ * that are required for TTS to be operational. Since the installation
+ * of the data can be interrupted or declined by the user, the application
+ * shouldn't expect successful installation upon return from that intent,
+ * and if need be, should check installation status with
+ * {@link #ACTION_TTS_CHECK_TTS_DATA}.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_TTS_INSTALL_TTS_DATA =
+ "android.intent.action.INSTALL_TTS_DATA";
+
+ /**
+ * Broadcast Action: Starts the activity from the platform Text-To-Speech
+ * engine to verify the proper installation and availability of the
+ * resource files on the system. Upon completion, the activity will
+ * return one of the following codes:
+ * {@link android.speech.tts.TextToSpeech.Engine#CHECK_VOICE_DATA_PASS},
+ * {@link android.speech.tts.TextToSpeech.Engine#CHECK_VOICE_DATA_FAIL},
+ * {@link android.speech.tts.TextToSpeech.Engine#CHECK_VOICE_DATA_BAD_DATA},
+ * {@link android.speech.tts.TextToSpeech.Engine#CHECK_VOICE_DATA_MISSING_DATA}, or
+ * {@link android.speech.tts.TextToSpeech.Engine#CHECK_VOICE_DATA_MISSING_VOLUME}.
+ * <p> Moreover, the data received in the activity result will contain the following
+ * fields:
+ * <ul>
+ * <li>{@link android.speech.tts.TextToSpeech.Engine#VOICE_DATA_ROOT_DIRECTORY} which
+ * indicates the path to the location of the resource files</li>,
+ * <li>{@link android.speech.tts.TextToSpeech.Engine#VOICE_DATA_FILES} which contains
+ * the list of all the resource files</li>,
+ * <li>and {@link android.speech.tts.TextToSpeech.Engine#VOICE_DATA_FILES_INFO} which
+ * contains, for each resource file, the description of the language covered by
+ * the file in the xxx-YYY format, where xxx is the 3-letter ISO language code,
+ * and YYY is the 3-letter ISO country code.</li>
+ * </ul>
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_TTS_CHECK_TTS_DATA =
+ "android.intent.action.CHECK_TTS_DATA";
+
+ /**
+ * Broadcast Action: The TextToSpeech synthesizer has completed processing
+ * all of the text in the speech queue.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_TTS_QUEUE_PROCESSING_COMPLETED =
+ "android.intent.action.TTS_QUEUE_PROCESSING_COMPLETED";
/**
* Broadcast Action: a remote intent is to be broadcasted.
@@ -1699,16 +1746,6 @@ public class Intent implements Parcelable {
public static final String ACTION_REMOTE_INTENT =
"android.intent.action.REMOTE_INTENT";
- /**
- * @hide
- * TODO: This will be unhidden in a later CL.
- * Broadcast Action: The TextToSpeech synthesizer has completed processing
- * all of the text in the speech queue.
- */
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String ACTION_TTS_QUEUE_PROCESSING_COMPLETED =
- "android.intent.action.TTS_QUEUE_PROCESSING_COMPLETED";
-
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Standard intent categories (see addCategory()).
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index f73b39438307..d54e26091d24 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -1588,7 +1588,7 @@ class SyncManager implements OnAccountsUpdatedListener {
ContentResolver.SYNC_EXTRAS_MANUAL, false);
final boolean syncAutomatically =
mSyncStorageEngine.getSyncAutomatically(op.account, op.authority)
- || mSyncStorageEngine.getMasterSyncAutomatically();
+ && mSyncStorageEngine.getMasterSyncAutomatically();
boolean syncAllowed =
manualSync || (backgroundDataUsageAllowed && syncAutomatically);
if (!syncAllowed) {
diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java
index 9d2efb54647d..8cc06426c599 100644
--- a/core/java/android/content/SyncStorageEngine.java
+++ b/core/java/android/content/SyncStorageEngine.java
@@ -110,6 +110,8 @@ public class SyncStorageEngine extends Handler {
private static final int MSG_WRITE_STATISTICS = 2;
private static final long WRITE_STATISTICS_DELAY = 1000*60*30; // 1/2 hour
+
+ private static final boolean SYNC_ENABLED_DEFAULT = false;
public static class PendingOperation {
final Account account;
@@ -158,7 +160,7 @@ public class SyncStorageEngine extends Handler {
this.account = account;
this.authority = authority;
this.ident = ident;
- enabled = true;
+ enabled = SYNC_ENABLED_DEFAULT;
}
}
@@ -376,23 +378,30 @@ public class SyncStorageEngine extends Handler {
}
public void setSyncAutomatically(Account account, String providerName, boolean sync) {
+ boolean wasEnabled;
synchronized (mAuthorities) {
- AuthorityInfo authority = getAuthorityLocked(account, providerName,
- "setSyncAutomatically");
- if (authority != null) {
- authority.enabled = sync;
- }
+ AuthorityInfo authority = getOrCreateAuthorityLocked(account, providerName, -1, false);
+ wasEnabled = authority.enabled;
+ authority.enabled = sync;
writeAccountInfoLocked();
}
-
+
+ if (!wasEnabled && sync) {
+ mContext.getContentResolver().requestSync(account, providerName, new Bundle());
+ }
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
}
public void setMasterSyncAutomatically(boolean flag) {
+ boolean old;
synchronized (mAuthorities) {
+ old = mMasterSyncAutomatically;
mMasterSyncAutomatically = flag;
writeAccountInfoLocked();
}
+ if (!old && flag) {
+ mContext.getContentResolver().requestSync(null, null, new Bundle());
+ }
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
mContext.sendBroadcast(SYNC_CONNECTION_SETTING_CHANGED_INTENT);
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 903f4820b101..cebb696867b0 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -979,12 +979,12 @@ public class PackageParser {
/**
* TODO: enable this before code freeze. b/1967935
* *
- */
if ((densities == null || densities.length == 0)
&& (pkg.applicationInfo.targetSdkVersion
>= android.os.Build.VERSION_CODES.CUR_DEVELOPMENT)) {
pkg.supportsDensities = ApplicationInfo.ANY_DENSITIES_ARRAY;
}
+ */
return pkg;
}
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index ebe556e3cd65..6e34cc8a24dc 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -38,7 +38,12 @@ public class CompatibilityInfo {
private static final String TAG = "CompatibilityInfo";
/** default compatibility info object for compatible applications */
- public static final CompatibilityInfo DEFAULT_COMPATIBILITY_INFO = new CompatibilityInfo();
+ public static final CompatibilityInfo DEFAULT_COMPATIBILITY_INFO = new CompatibilityInfo() {
+ @Override
+ public void setExpandable(boolean expandable) {
+ throw new UnsupportedOperationException("trying to change default compatibility info");
+ }
+ };
/**
* The default width of the screen in portrait mode.
@@ -80,6 +85,11 @@ public class CompatibilityInfo {
private static final int SCALING_EXPANDABLE_MASK = SCALING_REQUIRED | EXPANDABLE;
/**
+ * The effective screen density we have selected for this application.
+ */
+ public final int applicationDensity;
+
+ /**
* Application's scale.
*/
public final float applicationScale;
@@ -102,30 +112,36 @@ public class CompatibilityInfo {
}
float packageDensityScale = -1.0f;
+ int packageDensity = 0;
if (appInfo.supportsDensities != null) {
int minDiff = Integer.MAX_VALUE;
for (int density : appInfo.supportsDensities) {
- if (density == ApplicationInfo.ANY_DENSITY) {
+ if (density == ApplicationInfo.ANY_DENSITY) {
+ packageDensity = DisplayMetrics.DENSITY_DEVICE;
packageDensityScale = 1.0f;
break;
}
- int tmpDiff = Math.abs(DisplayMetrics.DEVICE_DENSITY - density);
+ int tmpDiff = Math.abs(DisplayMetrics.DENSITY_DEVICE - density);
if (tmpDiff == 0) {
+ packageDensity = DisplayMetrics.DENSITY_DEVICE;
packageDensityScale = 1.0f;
break;
}
// prefer higher density (appScale>1.0), unless that's only option.
if (tmpDiff < minDiff && packageDensityScale < 1.0f) {
- packageDensityScale = DisplayMetrics.DEVICE_DENSITY / (float) density;
+ packageDensity = density;
+ packageDensityScale = DisplayMetrics.DENSITY_DEVICE / (float) density;
minDiff = tmpDiff;
}
}
}
if (packageDensityScale > 0.0f) {
+ applicationDensity = packageDensity;
applicationScale = packageDensityScale;
} else {
+ applicationDensity = DisplayMetrics.DENSITY_DEFAULT;
applicationScale =
- DisplayMetrics.DEVICE_DENSITY / (float) DisplayMetrics.DEFAULT_DENSITY;
+ DisplayMetrics.DENSITY_DEVICE / (float) DisplayMetrics.DENSITY_DEFAULT;
}
applicationInvertedScale = 1.0f / applicationScale;
@@ -134,9 +150,11 @@ public class CompatibilityInfo {
}
}
- private CompatibilityInfo(int appFlags, int compFlags, float scale, float invertedScale) {
+ private CompatibilityInfo(int appFlags, int compFlags,
+ int dens, float scale, float invertedScale) {
this.appFlags = appFlags;
mCompatibilityFlags = compFlags;
+ applicationDensity = dens;
applicationScale = scale;
applicationInvertedScale = invertedScale;
}
@@ -146,6 +164,7 @@ public class CompatibilityInfo {
| ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS
| ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS,
EXPANDABLE | CONFIGURED_EXPANDABLE,
+ DisplayMetrics.DENSITY_DEVICE,
1.0f,
1.0f);
}
@@ -155,7 +174,7 @@ public class CompatibilityInfo {
*/
public CompatibilityInfo copy() {
CompatibilityInfo info = new CompatibilityInfo(appFlags, mCompatibilityFlags,
- applicationScale, applicationInvertedScale);
+ applicationDensity, applicationScale, applicationInvertedScale);
return info;
}
@@ -191,7 +210,7 @@ public class CompatibilityInfo {
@Override
public String toString() {
return "CompatibilityInfo{scale=" + applicationScale +
- ", compatibility flag=" + mCompatibilityFlags + "}";
+ ", supports screen=" + supportsScreen() + "}";
}
/**
@@ -205,8 +224,7 @@ public class CompatibilityInfo {
if (DBG) Log.d(TAG, "no translation required");
return null;
}
- if (!isScalingRequired() ||
- (params.flags & WindowManager.LayoutParams.FLAG_NO_COMPATIBILITY_SCALING) != 0) {
+ if (!isScalingRequired()) {
return null;
}
return new Translator();
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 8de938d58df2..2354519d6ad6 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -34,6 +34,7 @@ import android.util.Log;
import android.util.SparseArray;
import android.util.TypedValue;
import android.util.LongSparseArray;
+import android.view.Display;
import java.io.IOException;
import java.io.InputStream;
@@ -86,7 +87,8 @@ public class Resources {
/*package*/ final DisplayMetrics mMetrics = new DisplayMetrics();
PluralRules mPluralRule;
- private final CompatibilityInfo mCompatibilityInfo;
+ private CompatibilityInfo mCompatibilityInfo;
+ private Display mDefaultDisplay;
private static final LongSparseArray<Object> EMPTY_ARRAY = new LongSparseArray<Object>() {
@Override
@@ -1384,6 +1386,15 @@ public class Resources {
}
/**
+ * This is just for testing.
+ * @hide
+ */
+ public void setCompatibilityInfo(CompatibilityInfo ci) {
+ mCompatibilityInfo = ci;
+ updateConfiguration(mConfiguration, mMetrics);
+ }
+
+ /**
* Return a resource identifier for the given resource name. A fully
* qualified resource name is of the form "package:type/entry". The first
* two components (package and type) are optional if defType and
@@ -1915,6 +1926,24 @@ public class Resources {
+ Integer.toHexString(id));
}
+ /**
+ * Returns the display adjusted for the Resources' metrics.
+ * @hide
+ */
+ public Display getDefaultDisplay(Display defaultDisplay) {
+ if (mDefaultDisplay == null) {
+ if (!mCompatibilityInfo.isScalingRequired() && mCompatibilityInfo.supportsScreen()) {
+ // the app supports the display. just use the default one.
+ mDefaultDisplay = defaultDisplay;
+ } else {
+ // display needs adjustment.
+ mDefaultDisplay = Display.createMetricsBasedDisplay(
+ defaultDisplay.getDisplayId(), mMetrics);
+ }
+ }
+ return mDefaultDisplay;
+ }
+
private TypedArray getCachedStyledAttributes(int len) {
synchronized (mTmpValue) {
TypedArray attrs = mCachedStyledAttributes;
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 091bc1700988..40d2c869d30a 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -56,7 +56,9 @@ public class Camera {
private PictureCallback mRawImageCallback;
private PictureCallback mJpegCallback;
private PreviewCallback mPreviewCallback;
+ private PictureCallback mPostviewCallback;
private AutoFocusCallback mAutoFocusCallback;
+ private ZoomCallback mZoomCallback;
private ErrorCallback mErrorCallback;
private boolean mOneShot;
@@ -72,6 +74,8 @@ public class Camera {
mRawImageCallback = null;
mJpegCallback = null;
mPreviewCallback = null;
+ mPostviewCallback = null;
+ mZoomCallback = null;
Looper looper;
if ((looper = Looper.myLooper()) != null) {
@@ -245,13 +249,15 @@ public class Camera {
return;
case CAMERA_MSG_RAW_IMAGE:
- if (mRawImageCallback != null)
+ if (mRawImageCallback != null) {
mRawImageCallback.onPictureTaken((byte[])msg.obj, mCamera);
+ }
return;
case CAMERA_MSG_COMPRESSED_IMAGE:
- if (mJpegCallback != null)
+ if (mJpegCallback != null) {
mJpegCallback.onPictureTaken((byte[])msg.obj, mCamera);
+ }
return;
case CAMERA_MSG_PREVIEW_FRAME:
@@ -263,15 +269,29 @@ public class Camera {
}
return;
+ case CAMERA_MSG_POSTVIEW_FRAME:
+ if (mPostviewCallback != null) {
+ mPostviewCallback.onPictureTaken((byte[])msg.obj, mCamera);
+ }
+ return;
+
case CAMERA_MSG_FOCUS:
- if (mAutoFocusCallback != null)
+ if (mAutoFocusCallback != null) {
mAutoFocusCallback.onAutoFocus(msg.arg1 == 0 ? false : true, mCamera);
+ }
+ return;
+
+ case CAMERA_MSG_ZOOM:
+ if (mZoomCallback != null) {
+ mZoomCallback.onZoomUpdate(msg.arg1, mCamera);
+ }
return;
case CAMERA_MSG_ERROR :
Log.e(TAG, "Error " + msg.arg1);
- if (mErrorCallback != null)
+ if (mErrorCallback != null) {
mErrorCallback.onError(msg.arg1, mCamera);
+ }
return;
default:
@@ -364,13 +384,63 @@ public class Camera {
*/
public final void takePicture(ShutterCallback shutter, PictureCallback raw,
PictureCallback jpeg) {
+ takePicture(shutter, raw, null, jpeg);
+ }
+ private native final void native_takePicture();
+
+ /**
+ * Triggers an asynchronous image capture. The camera service
+ * will initiate a series of callbacks to the application as the
+ * image capture progresses. The shutter callback occurs after
+ * the image is captured. This can be used to trigger a sound
+ * to let the user know that image has been captured. The raw
+ * callback occurs when the raw image data is available. The
+ * postview callback occurs when a scaled, fully processed
+ * postview image is available (NOTE: not all hardware supports
+ * this). The jpeg callback occurs when the compressed image is
+ * available. If the application does not need a particular
+ * callback, a null can be passed instead of a callback method.
+ *
+ * @param shutter callback after the image is captured, may be null
+ * @param raw callback with raw image data, may be null
+ * @param postview callback with postview image data, may be null
+ * @param jpeg callback with jpeg image data, may be null
+ */
+ public final void takePicture(ShutterCallback shutter, PictureCallback raw,
+ PictureCallback postview, PictureCallback jpeg) {
mShutterCallback = shutter;
mRawImageCallback = raw;
+ mPostviewCallback = postview;
mJpegCallback = jpeg;
native_takePicture();
}
- private native final void native_takePicture();
+ /**
+ * Handles the zoom callback.
+ */
+ public interface ZoomCallback
+ {
+ /**
+ * Callback for zoom updates
+ * @param zoomLevel new zoom level in 1/1000 increments,
+ * e.g. a zoom of 3.2x is stored as 3200. Accuracy of the
+ * value is dependent on the hardware implementation. Not
+ * all devices will generate this callback.
+ * @param camera the Camera service object
+ */
+ void onZoomUpdate(int zoomLevel, Camera camera);
+ };
+
+ /**
+ * Registers a callback to be invoked when the zoom
+ * level is updated by the camera driver.
+ * @param cb the callback to run
+ */
+ public final void setZoomCallback(ZoomCallback cb)
+ {
+ mZoomCallback = cb;
+ }
+
// These match the enum in include/ui/Camera.h
/** Unspecified camerar error. @see #ErrorCallback */
public static final int CAMERA_ERROR_UNKNOWN = 1;
diff --git a/core/java/android/net/http/ConnectionThread.java b/core/java/android/net/http/ConnectionThread.java
index 1d0db2bc70d6..0b30e589c4a7 100644
--- a/core/java/android/net/http/ConnectionThread.java
+++ b/core/java/android/net/http/ConnectionThread.java
@@ -69,6 +69,7 @@ class ConnectionThread extends Thread {
*/
public void run() {
android.os.Process.setThreadPriority(
+ android.os.Process.THREAD_PRIORITY_DEFAULT +
android.os.Process.THREAD_PRIORITY_LESS_FAVORABLE);
// these are used to get performance data. When it is not in the timing,
diff --git a/core/java/android/net/http/Request.java b/core/java/android/net/http/Request.java
index 3fb3d3f0e8a9..1b6568e7f118 100644
--- a/core/java/android/net/http/Request.java
+++ b/core/java/android/net/http/Request.java
@@ -67,9 +67,6 @@ class Request {
/** Set if I'm using a proxy server */
HttpHost mProxyHost;
- /** True if request is .html, .js, .css */
- boolean mHighPriority;
-
/** True if request has been cancelled */
volatile boolean mCancelled = false;
@@ -102,17 +99,15 @@ class Request {
* @param eventHandler request will make progress callbacks on
* this interface
* @param headers reqeust headers
- * @param highPriority true for .html, css, .cs
*/
Request(String method, HttpHost host, HttpHost proxyHost, String path,
InputStream bodyProvider, int bodyLength,
EventHandler eventHandler,
- Map<String, String> headers, boolean highPriority) {
+ Map<String, String> headers) {
mEventHandler = eventHandler;
mHost = host;
mProxyHost = proxyHost;
mPath = path;
- mHighPriority = highPriority;
mBodyProvider = bodyProvider;
mBodyLength = bodyLength;
@@ -356,7 +351,7 @@ class Request {
* for debugging
*/
public String toString() {
- return (mHighPriority ? "P*" : "") + mPath;
+ return mPath;
}
@@ -422,8 +417,7 @@ class Request {
}
return status >= HttpStatus.SC_OK
&& status != HttpStatus.SC_NO_CONTENT
- && status != HttpStatus.SC_NOT_MODIFIED
- && status != HttpStatus.SC_RESET_CONTENT;
+ && status != HttpStatus.SC_NOT_MODIFIED;
}
/**
diff --git a/core/java/android/net/http/RequestHandle.java b/core/java/android/net/http/RequestHandle.java
index 6a97951fecbd..190ae7ab6378 100644
--- a/core/java/android/net/http/RequestHandle.java
+++ b/core/java/android/net/http/RequestHandle.java
@@ -419,6 +419,6 @@ public class RequestHandle {
mRequest = mRequestQueue.queueRequest(
mUrl, mUri, mMethod, mHeaders, mRequest.mEventHandler,
mBodyProvider,
- mBodyLength, mRequest.mHighPriority).mRequest;
+ mBodyLength).mRequest;
}
}
diff --git a/core/java/android/net/http/RequestQueue.java b/core/java/android/net/http/RequestQueue.java
index 4d3e7c3804cb..b6f295edf834 100644
--- a/core/java/android/net/http/RequestQueue.java
+++ b/core/java/android/net/http/RequestQueue.java
@@ -52,47 +52,10 @@ public class RequestQueue implements RequestFeeder {
private Context mContext;
- private static class RequestSet {
- private final LinkedList<Request> mHighPriority;
- private final LinkedList<Request> mLowPriority;
-
- RequestSet() {
- mHighPriority = new LinkedList<Request>();
- mLowPriority = new LinkedList<Request>();
- }
-
- void add(Request req, boolean head) {
- LinkedList l = mLowPriority;
- if (req.mHighPriority) {
- l = mHighPriority;
- }
- if (head) {
- l.addFirst(req);
- } else {
- l.add(req);
- }
- }
-
- Request removeFirst() {
- if (!mHighPriority.isEmpty()) {
- return mHighPriority.removeFirst();
- } else if (!mLowPriority.isEmpty()) {
- return mLowPriority.removeFirst();
- }
- return null;
- }
-
- boolean isEmpty() {
- return mHighPriority.isEmpty() && mLowPriority.isEmpty();
- }
- };
/**
* Requests, indexed by HttpHost (scheme, host, port)
*/
- private LinkedHashMap<HttpHost, RequestSet> mPending;
-
- /* Support for notifying a client when queue is empty */
- private boolean mClientWaiting = false;
+ private LinkedHashMap<HttpHost, LinkedList<Request>> mPending;
/** true if connected */
boolean mNetworkConnected = true;
@@ -382,7 +345,7 @@ public class RequestQueue implements RequestFeeder {
public RequestQueue(Context context, int connectionCount) {
mContext = context;
- mPending = new LinkedHashMap<HttpHost, RequestSet>(32);
+ mPending = new LinkedHashMap<HttpHost, LinkedList<Request>>(32);
mActivePool = new ActivePool(connectionCount);
mActivePool.startup();
@@ -472,16 +435,14 @@ public class RequestQueue implements RequestFeeder {
* data. Callbacks will be made on the supplied instance.
* @param bodyProvider InputStream providing HTTP body, null if none
* @param bodyLength length of body, must be 0 if bodyProvider is null
- * @param highPriority If true, queues before low priority
- * requests if possible
*/
public RequestHandle queueRequest(
String url, String method,
Map<String, String> headers, EventHandler eventHandler,
- InputStream bodyProvider, int bodyLength, boolean highPriority) {
+ InputStream bodyProvider, int bodyLength) {
WebAddress uri = new WebAddress(url);
return queueRequest(url, uri, method, headers, eventHandler,
- bodyProvider, bodyLength, highPriority);
+ bodyProvider, bodyLength);
}
/**
@@ -494,14 +455,11 @@ public class RequestQueue implements RequestFeeder {
* data. Callbacks will be made on the supplied instance.
* @param bodyProvider InputStream providing HTTP body, null if none
* @param bodyLength length of body, must be 0 if bodyProvider is null
- * @param highPriority If true, queues before low priority
- * requests if possible
*/
public RequestHandle queueRequest(
String url, WebAddress uri, String method, Map<String, String> headers,
EventHandler eventHandler,
- InputStream bodyProvider, int bodyLength,
- boolean highPriority) {
+ InputStream bodyProvider, int bodyLength) {
if (HttpLog.LOGV) HttpLog.v("RequestQueue.queueRequest " + uri);
@@ -516,7 +474,7 @@ public class RequestQueue implements RequestFeeder {
// set up request
req = new Request(method, httpHost, mProxyHost, uri.mPath, bodyProvider,
- bodyLength, eventHandler, headers, highPriority);
+ bodyLength, eventHandler, headers);
queueRequest(req, false);
@@ -558,24 +516,19 @@ public class RequestQueue implements RequestFeeder {
HttpLog.v("dump()");
StringBuilder dump = new StringBuilder();
int count = 0;
- Iterator<Map.Entry<HttpHost, RequestSet>> iter;
+ Iterator<Map.Entry<HttpHost, LinkedList<Request>>> iter;
// mActivePool.log(dump);
if (!mPending.isEmpty()) {
iter = mPending.entrySet().iterator();
while (iter.hasNext()) {
- Map.Entry<HttpHost, RequestSet> entry = iter.next();
+ Map.Entry<HttpHost, LinkedList<Request>> entry = iter.next();
String hostName = entry.getKey().getHostName();
StringBuilder line = new StringBuilder("p" + count++ + " " + hostName + " ");
- RequestSet reqList = entry.getValue();
- ListIterator reqIter = reqList.mHighPriority.listIterator(0);
- while (iter.hasNext()) {
- Request request = (Request)iter.next();
- line.append(request + " ");
- }
- reqIter = reqList.mLowPriority.listIterator(0);
+ LinkedList<Request> reqList = entry.getValue();
+ ListIterator reqIter = reqList.listIterator(0);
while (iter.hasNext()) {
Request request = (Request)iter.next();
line.append(request + " ");
@@ -607,9 +560,10 @@ public class RequestQueue implements RequestFeeder {
Request ret = null;
if (mNetworkConnected && mPending.containsKey(host)) {
- RequestSet reqList = mPending.get(host);
- ret = reqList.removeFirst();
- if (reqList.isEmpty()) {
+ LinkedList<Request> reqList = mPending.get(host);
+ if (!reqList.isEmpty()) {
+ ret = reqList.removeFirst();
+ } else {
mPending.remove(host);
}
}
@@ -640,14 +594,18 @@ public class RequestQueue implements RequestFeeder {
protected synchronized void queueRequest(Request request, boolean head) {
HttpHost host = request.mProxyHost == null ? request.mHost : request.mProxyHost;
- RequestSet reqList;
+ LinkedList<Request> reqList;
if (mPending.containsKey(host)) {
reqList = mPending.get(host);
} else {
- reqList = new RequestSet();
+ reqList = new LinkedList<Request>();
mPending.put(host, reqList);
}
- reqList.add(request, head);
+ if (head) {
+ reqList.addFirst(request);
+ } else {
+ reqList.add(request);
+ }
}
@@ -660,14 +618,15 @@ public class RequestQueue implements RequestFeeder {
}
/* helper */
- private Request removeFirst(LinkedHashMap<HttpHost, RequestSet> requestQueue) {
+ private Request removeFirst(LinkedHashMap<HttpHost, LinkedList<Request>> requestQueue) {
Request ret = null;
- Iterator<Map.Entry<HttpHost, RequestSet>> iter = requestQueue.entrySet().iterator();
+ Iterator<Map.Entry<HttpHost, LinkedList<Request>>> iter = requestQueue.entrySet().iterator();
if (iter.hasNext()) {
- Map.Entry<HttpHost, RequestSet> entry = iter.next();
- RequestSet reqList = entry.getValue();
- ret = reqList.removeFirst();
- if (reqList.isEmpty()) {
+ Map.Entry<HttpHost, LinkedList<Request>> entry = iter.next();
+ LinkedList<Request> reqList = entry.getValue();
+ if (!reqList.isEmpty()) {
+ ret = reqList.removeFirst();
+ } else {
requestQueue.remove(entry.getKey());
}
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 528def5c4011..e203fd590258 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -508,6 +508,19 @@ public abstract class BatteryStats implements Parcelable {
public abstract long getBatteryUptime(long curTime);
/**
+ * @deprecated use getRadioDataUptime
+ */
+ public long getRadioDataUptimeMs() {
+ return getRadioDataUptime() / 1000;
+ }
+
+ /**
+ * Returns the time that the radio was on for data transfers.
+ * @return the uptime in microseconds while unplugged
+ */
+ public abstract long getRadioDataUptime();
+
+ /**
* Returns the current battery realtime in microseconds.
*
* @param curTime the amount of elapsed realtime in microseconds.
@@ -1128,7 +1141,14 @@ public abstract class BatteryStats implements Parcelable {
}
if (!didOne) sb.append("No activity");
pw.println(sb.toString());
-
+
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Radio data uptime when unplugged: ");
+ sb.append(getRadioDataUptime() / 1000);
+ sb.append(" ms");
+ pw.println(sb.toString());
+
sb.setLength(0);
sb.append(prefix);
sb.append(" Wifi on: "); formatTimeMs(sb, wifiOnTime / 1000);
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 480519386e48..980cff39801f 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -739,7 +739,7 @@ public class Process {
public static final native void sendSignal(int pid, int signal);
/** @hide */
- public static final native int getFreeMemory();
+ public static final native long getFreeMemory();
/** @hide */
public static final native void readProcLines(String path,
diff --git a/core/java/android/pim/vcard/ContactStruct.java b/core/java/android/pim/vcard/ContactStruct.java
new file mode 100644
index 000000000000..46725d31e04d
--- /dev/null
+++ b/core/java/android/pim/vcard/ContactStruct.java
@@ -0,0 +1,1244 @@
+/*
+ * 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.
+ */
+package android.pim.vcard;
+
+import android.content.AbstractSyncableContentProvider;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.net.Uri;
+import android.provider.Contacts;
+import android.provider.Contacts.ContactMethods;
+import android.provider.Contacts.Extensions;
+import android.provider.Contacts.GroupMembership;
+import android.provider.Contacts.Organizations;
+import android.provider.Contacts.People;
+import android.provider.Contacts.Phones;
+import android.provider.Contacts.Photos;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * This class bridges between data structure of Contact app and VCard data.
+ */
+public class ContactStruct {
+ private static final String LOG_TAG = "ContactStruct";
+
+ /**
+ * @hide only for testing
+ */
+ static public class PhoneData {
+ public final int type;
+ public final String data;
+ public final String label;
+ // isPrimary is changable only when there's no appropriate one existing in
+ // the original VCard.
+ public boolean isPrimary;
+ public PhoneData(int type, String data, String label, boolean isPrimary) {
+ this.type = type;
+ this.data = data;
+ this.label = label;
+ this.isPrimary = isPrimary;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof PhoneData) {
+ return false;
+ }
+ PhoneData phoneData = (PhoneData)obj;
+ return (type == phoneData.type && data.equals(phoneData.data) &&
+ label.equals(phoneData.label) && isPrimary == phoneData.isPrimary);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("type: %d, data: %s, label: %s, isPrimary: %s",
+ type, data, label, isPrimary);
+ }
+ }
+
+ /**
+ * @hide only for testing
+ */
+ static public class ContactMethod {
+ // Contacts.KIND_EMAIL, Contacts.KIND_POSTAL
+ public final int kind;
+ // e.g. Contacts.ContactMethods.TYPE_HOME, Contacts.PhoneColumns.TYPE_HOME
+ // If type == Contacts.PhoneColumns.TYPE_CUSTOM, label is used.
+ public final int type;
+ public final String data;
+ // Used only when TYPE is TYPE_CUSTOM.
+ public final String label;
+ // isPrimary is changable only when there's no appropriate one existing in
+ // the original VCard.
+ public boolean isPrimary;
+ public ContactMethod(int kind, int type, String data, String label,
+ boolean isPrimary) {
+ this.kind = kind;
+ this.type = type;
+ this.data = data;
+ this.label = data;
+ this.isPrimary = isPrimary;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof ContactMethod) {
+ return false;
+ }
+ ContactMethod contactMethod = (ContactMethod)obj;
+ return (kind == contactMethod.kind && type == contactMethod.type &&
+ data.equals(contactMethod.data) && label.equals(contactMethod.label) &&
+ isPrimary == contactMethod.isPrimary);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("kind: %d, type: %d, data: %s, label: %s, isPrimary: %s",
+ kind, type, data, label, isPrimary);
+ }
+ }
+
+ /**
+ * @hide only for testing
+ */
+ static public class OrganizationData {
+ public final int type;
+ public final String companyName;
+ // can be changed in some VCard format.
+ public String positionName;
+ // isPrimary is changable only when there's no appropriate one existing in
+ // the original VCard.
+ public boolean isPrimary;
+ public OrganizationData(int type, String companyName, String positionName,
+ boolean isPrimary) {
+ this.type = type;
+ this.companyName = companyName;
+ this.positionName = positionName;
+ this.isPrimary = isPrimary;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof OrganizationData) {
+ return false;
+ }
+ OrganizationData organization = (OrganizationData)obj;
+ return (type == organization.type && companyName.equals(organization.companyName) &&
+ positionName.equals(organization.positionName) &&
+ isPrimary == organization.isPrimary);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("type: %d, company: %s, position: %s, isPrimary: %s",
+ type, companyName, positionName, isPrimary);
+ }
+ }
+
+ static class Property {
+ private String mPropertyName;
+ private Map<String, Collection<String>> mParameterMap =
+ new HashMap<String, Collection<String>>();
+ private List<String> mPropertyValueList = new ArrayList<String>();
+ private byte[] mPropertyBytes;
+
+ public Property() {
+ clear();
+ }
+
+ public void setPropertyName(final String propertyName) {
+ mPropertyName = propertyName;
+ }
+
+ public void addParameter(final String paramName, final String paramValue) {
+ Collection<String> values;
+ if (mParameterMap.containsKey(paramName)) {
+ if (paramName.equals("TYPE")) {
+ values = new HashSet<String>();
+ } else {
+ values = new ArrayList<String>();
+ }
+ mParameterMap.put(paramName, values);
+ } else {
+ values = mParameterMap.get(paramName);
+ }
+ }
+
+ public void addToPropertyValueList(final String propertyValue) {
+ mPropertyValueList.add(propertyValue);
+ }
+
+ public void setPropertyBytes(final byte[] propertyBytes) {
+ mPropertyBytes = propertyBytes;
+ }
+
+ public final Collection<String> getParameters(String type) {
+ return mParameterMap.get(type);
+ }
+
+ public final List<String> getPropertyValueList() {
+ return mPropertyValueList;
+ }
+
+ public void clear() {
+ mPropertyName = null;
+ mParameterMap.clear();
+ mPropertyValueList.clear();
+ }
+ }
+
+ private String mName;
+ private String mPhoneticName;
+ // private String mPhotoType;
+ private byte[] mPhotoBytes;
+ private List<String> mNotes;
+ private List<PhoneData> mPhoneList;
+ private List<ContactMethod> mContactMethodList;
+ private List<OrganizationData> mOrganizationList;
+ private Map<String, List<String>> mExtensionMap;
+
+ private int mNameOrderType;
+
+ /* private variables bellow is for temporary use. */
+
+ // For name, there are three fields in vCard: FN, N, NAME.
+ // We prefer FN, which is a required field in vCard 3.0 , but not in vCard 2.1.
+ // Next, we prefer NAME, which is defined only in vCard 3.0.
+ // Finally, we use N, which is a little difficult to parse.
+ private String mTmpFullName;
+ private String mTmpNameFromNProperty;
+
+ // Some vCard has "X-PHONETIC-FIRST-NAME", "X-PHONETIC-MIDDLE-NAME", and
+ // "X-PHONETIC-LAST-NAME"
+ private String mTmpXPhoneticFirstName;
+ private String mTmpXPhoneticMiddleName;
+ private String mTmpXPhoneticLastName;
+
+ // Each Column of four properties has ISPRIMARY field
+ // (See android.provider.Contacts)
+ // If false even after the following loop, we choose the first
+ // entry as a "primary" entry.
+ private boolean mPrefIsSet_Address;
+ private boolean mPrefIsSet_Phone;
+ private boolean mPrefIsSet_Email;
+ private boolean mPrefIsSet_Organization;
+
+ public ContactStruct() {
+ mNameOrderType = VCardConfig.NAME_ORDER_TYPE_DEFAULT;
+ }
+
+ public ContactStruct(int nameOrderType) {
+ mNameOrderType = nameOrderType;
+ }
+
+ /**
+ * @hide only for test
+ */
+ public ContactStruct(String name,
+ String phoneticName,
+ byte[] photoBytes,
+ List<String> notes,
+ List<PhoneData> phoneList,
+ List<ContactMethod> contactMethodList,
+ List<OrganizationData> organizationList,
+ Map<String, List<String>> extensionMap) {
+ mName = name;
+ mPhoneticName = phoneticName;
+ mPhotoBytes = photoBytes;
+ mContactMethodList = contactMethodList;
+ mOrganizationList = organizationList;
+ mExtensionMap = extensionMap;
+ }
+
+ /**
+ * @hide only for test
+ */
+ public String getName() {
+ return mName;
+ }
+
+ /**
+ * @hide only for test
+ */
+ public String getPhoneticName() {
+ return mPhoneticName;
+ }
+
+ /**
+ * @hide only for test
+ */
+ public final byte[] getPhotoBytes() {
+ return mPhotoBytes;
+ }
+
+ /**
+ * @hide only for test
+ */
+ public final List<String> getNotes() {
+ return mNotes;
+ }
+
+ /**
+ * @hide only for test
+ */
+ public final List<PhoneData> getPhoneList() {
+ return mPhoneList;
+ }
+
+ /**
+ * @hide only for test
+ */
+ public final List<ContactMethod> getContactMethodList() {
+ return mContactMethodList;
+ }
+
+ /**
+ * @hide only for test
+ */
+ public final List<OrganizationData> getOrganizationList() {
+ return mOrganizationList;
+ }
+
+ /**
+ * @hide only for test
+ */
+ public final Map<String, List<String>> getExtensionMap() {
+ return mExtensionMap;
+ }
+
+ /**
+ * Add a phone info to phoneList.
+ * @param data phone number
+ * @param type type col of content://contacts/phones
+ * @param label lable col of content://contacts/phones
+ */
+ private void addPhone(int type, String data, String label, boolean isPrimary){
+ if (mPhoneList == null) {
+ mPhoneList = new ArrayList<PhoneData>();
+ }
+ StringBuilder builder = new StringBuilder();
+ String trimed = data.trim();
+ int length = trimed.length();
+ for (int i = 0; i < length; i++) {
+ char ch = trimed.charAt(i);
+ if (('0' <= ch && ch <= '9') || (i == 0 && ch == '+')) {
+ builder.append(ch);
+ }
+ }
+
+ PhoneData phoneData = new PhoneData(type,
+ PhoneNumberUtils.formatNumber(builder.toString()),
+ label, isPrimary);
+
+ mPhoneList.add(phoneData);
+ }
+
+ /**
+ * Add a contactmethod info to contactmethodList.
+ * @param kind integer value defined in Contacts.java
+ * (e.g. Contacts.KIND_EMAIL)
+ * @param type type col of content://contacts/contact_methods
+ * @param data contact data
+ * @param label extra string used only when kind is Contacts.KIND_CUSTOM.
+ */
+ private void addContactmethod(int kind, int type, String data,
+ String label, boolean isPrimary){
+ if (mContactMethodList == null) {
+ mContactMethodList = new ArrayList<ContactMethod>();
+ }
+ mContactMethodList.add(new ContactMethod(kind, type, data, label, isPrimary));
+ }
+
+ /**
+ * Add a Organization info to organizationList.
+ */
+ private void addOrganization(int type, String companyName, String positionName,
+ boolean isPrimary) {
+ if (mOrganizationList == null) {
+ mOrganizationList = new ArrayList<OrganizationData>();
+ }
+ mOrganizationList.add(new OrganizationData(type, companyName, positionName, isPrimary));
+ }
+
+ /**
+ * Set "position" value to the appropriate data. If there's more than one
+ * OrganizationData objects, the value is set to the last one. If there's no
+ * OrganizationData object, a new OrganizationData is created, whose company name is
+ * empty.
+ *
+ * TODO: incomplete logic. fix this:
+ *
+ * e.g. This assumes ORG comes earlier, but TITLE may come earlier like this, though we do not
+ * know how to handle it in general cases...
+ * ----
+ * TITLE:Software Engineer
+ * ORG:Google
+ * ----
+ */
+ private void setPosition(String positionValue) {
+ if (mOrganizationList == null) {
+ mOrganizationList = new ArrayList<OrganizationData>();
+ }
+ int size = mOrganizationList.size();
+ if (size == 0) {
+ addOrganization(Contacts.OrganizationColumns.TYPE_OTHER, "", null, false);
+ size = 1;
+ }
+ OrganizationData lastData = mOrganizationList.get(size - 1);
+ lastData.positionName = positionValue;
+ }
+
+ private void addExtension(String propName, Map<String, Collection<String>> paramMap,
+ List<String> propValueList) {
+ if (propValueList.size() == 0) {
+ return;
+ }
+ // Now store the string into extensionMap.
+ List<String> list;
+ if (mExtensionMap == null) {
+ mExtensionMap = new HashMap<String, List<String>>();
+ }
+ if (!mExtensionMap.containsKey(propName)){
+ list = new ArrayList<String>();
+ mExtensionMap.put(propName, list);
+ } else {
+ list = mExtensionMap.get(propName);
+ }
+
+ list.add(encodeProperty(propName, paramMap, propValueList));
+ }
+
+ private String encodeProperty(String propName, Map<String, Collection<String>> paramMap,
+ List<String> propValueList) {
+ // PropertyNode#toString() is for reading, not for parsing in the future.
+ // We construct appropriate String here.
+ StringBuilder builder = new StringBuilder();
+ if (propName.length() > 0) {
+ builder.append("propName:[");
+ builder.append(propName);
+ builder.append("],");
+ }
+
+ if (paramMap.size() > 0) {
+ builder.append("paramMap:[");
+ int size = paramMap.size();
+ int i = 0;
+ for (Map.Entry<String, Collection<String>> entry : paramMap.entrySet()) {
+ String key = entry.getKey();
+ for (String value : entry.getValue()) {
+ // Assuming param-key does not contain NON-ASCII nor symbols.
+ // TODO: check it.
+ //
+ // According to vCard 3.0:
+ // param-name = iana-token / x-name
+ builder.append(key);
+
+ // param-value may contain any value including NON-ASCIIs.
+ // We use the following replacing rule.
+ // \ -> \\
+ // , -> \,
+ // In String#replaceAll(), "\\\\" means a single backslash.
+ builder.append("=");
+
+ // TODO: fix this.
+ builder.append(value.replaceAll("\\\\", "\\\\\\\\").replaceAll(",", "\\\\,"));
+ if (i < size -1) {
+ builder.append(",");
+ }
+ i++;
+ }
+ }
+
+ builder.append("],");
+ }
+
+ int size = propValueList.size();
+ if (size > 0) {
+ builder.append("propValue:[");
+ List<String> list = propValueList;
+ for (int i = 0; i < size; i++) {
+ // TODO: fix this.
+ builder.append(list.get(i).replaceAll("\\\\", "\\\\\\\\").replaceAll(",", "\\\\,"));
+ if (i < size -1) {
+ builder.append(",");
+ }
+ }
+ builder.append("],");
+ }
+
+ return builder.toString();
+ }
+
+ private static String getNameFromNProperty(List<String> elems, int nameOrderType) {
+ // Family, Given, Middle, Prefix, Suffix. (1 - 5)
+ int size = elems.size();
+ if (size > 1) {
+ StringBuilder builder = new StringBuilder();
+ boolean builderIsEmpty = true;
+ // Prefix
+ if (size > 3 && elems.get(3).length() > 0) {
+ builder.append(elems.get(3));
+ builderIsEmpty = false;
+ }
+ String first, second;
+ if (nameOrderType == VCardConfig.NAME_ORDER_TYPE_JAPANESE) {
+ first = elems.get(0);
+ second = elems.get(1);
+ } else {
+ first = elems.get(1);
+ second = elems.get(0);
+ }
+ if (first.length() > 0) {
+ if (!builderIsEmpty) {
+ builder.append(' ');
+ }
+ builder.append(first);
+ builderIsEmpty = false;
+ }
+ // Middle name
+ if (size > 2 && elems.get(2).length() > 0) {
+ if (!builderIsEmpty) {
+ builder.append(' ');
+ }
+ builder.append(elems.get(2));
+ builderIsEmpty = false;
+ }
+ if (second.length() > 0) {
+ if (!builderIsEmpty) {
+ builder.append(' ');
+ }
+ builder.append(second);
+ builderIsEmpty = false;
+ }
+ // Suffix
+ if (size > 4 && elems.get(4).length() > 0) {
+ if (!builderIsEmpty) {
+ builder.append(' ');
+ }
+ builder.append(elems.get(4));
+ builderIsEmpty = false;
+ }
+ return builder.toString();
+ } else if (size == 1) {
+ return elems.get(0);
+ } else {
+ return "";
+ }
+ }
+
+ public void addProperty(Property property) {
+ String propName = property.mPropertyName;
+ final Map<String, Collection<String>> paramMap = property.mParameterMap;
+ final List<String> propValueList = property.mPropertyValueList;
+ byte[] propBytes = property.mPropertyBytes;
+
+ if (propValueList.size() == 0) {
+ return;
+ }
+
+ String propValue = listToString(propValueList);
+
+ if (propName.equals("VERSION")) {
+ // vCard version. Ignore this.
+ } else if (propName.equals("FN")) {
+ mTmpFullName = propValue;
+ } else if (propName.equals("NAME") && mTmpFullName == null) {
+ // Only in vCard 3.0. Use this if FN does not exist.
+ // Though, note that vCard 3.0 requires FN.
+ mTmpFullName = propValue;
+ } else if (propName.equals("N")) {
+ mTmpNameFromNProperty = getNameFromNProperty(propValueList, mNameOrderType);
+ } else if (propName.equals("SORT-STRING")) {
+ mPhoneticName = propValue;
+ } else if (propName.equals("SOUND")) {
+ if ("X-IRMC-N".equals(paramMap.get("TYPE")) && mPhoneticName == null) {
+ // Some Japanese mobile phones use this field for phonetic name,
+ // since vCard 2.1 does not have "SORT-STRING" type.
+ // Also, in some cases, the field has some ';'s in it.
+ // We remove them.
+ StringBuilder builder = new StringBuilder();
+ String value = propValue;
+ int length = value.length();
+ for (int i = 0; i < length; i++) {
+ char ch = value.charAt(i);
+ if (ch != ';') {
+ builder.append(ch);
+ }
+ }
+ if (builder.length() > 0) {
+ mPhoneticName = builder.toString();
+ }
+ } else {
+ addExtension(propName, paramMap, propValueList);
+ }
+ } else if (propName.equals("ADR")) {
+ boolean valuesAreAllEmpty = true;
+ for (String value : propValueList) {
+ if (value.length() > 0) {
+ valuesAreAllEmpty = false;
+ break;
+ }
+ }
+ if (valuesAreAllEmpty) {
+ return;
+ }
+
+ int kind = Contacts.KIND_POSTAL;
+ int type = -1;
+ String label = "";
+ boolean isPrimary = false;
+ Collection<String> typeCollection = paramMap.get("TYPE");
+ if (typeCollection != null) {
+ for (String typeString : typeCollection) {
+ if (typeString.equals("PREF") && !mPrefIsSet_Address) {
+ // Only first "PREF" is considered.
+ mPrefIsSet_Address = true;
+ isPrimary = true;
+ } else if (typeString.equalsIgnoreCase("HOME")) {
+ type = Contacts.ContactMethodsColumns.TYPE_HOME;
+ label = "";
+ } else if (typeString.equalsIgnoreCase("WORK") ||
+ typeString.equalsIgnoreCase("COMPANY")) {
+ // "COMPANY" seems emitted by Windows Mobile, which is not
+ // specifically supported by vCard 2.1. We assume this is same
+ // as "WORK".
+ type = Contacts.ContactMethodsColumns.TYPE_WORK;
+ label = "";
+ } else if (typeString.equalsIgnoreCase("POSTAL")) {
+ kind = Contacts.KIND_POSTAL;
+ } else if (typeString.equalsIgnoreCase("PARCEL") ||
+ typeString.equalsIgnoreCase("DOM") ||
+ typeString.equalsIgnoreCase("INTL")) {
+ // We do not have a kind or type matching these.
+ // TODO: fix this. We may need to split entries into two.
+ // (e.g. entries for KIND_POSTAL and KIND_PERCEL)
+ } else if (typeString.toUpperCase().startsWith("X-") &&
+ type < 0) {
+ type = Contacts.ContactMethodsColumns.TYPE_CUSTOM;
+ label = typeString.substring(2);
+ } else if (type < 0) {
+ // vCard 3.0 allows iana-token. Also some vCard 2.1 exporters
+ // emit non-standard types. We do not handle their values now.
+ type = Contacts.ContactMethodsColumns.TYPE_CUSTOM;
+ label = typeString;
+ }
+ }
+ }
+ // We use "HOME" as default
+ if (type < 0) {
+ type = Contacts.ContactMethodsColumns.TYPE_HOME;
+ }
+
+ // adr-value = 0*6(text-value ";") text-value
+ // ; PO Box, Extended Address, Street, Locality, Region, Postal
+ // ; Code, Country Name
+ String address;
+ int size = propValueList.size();
+ if (size > 1) {
+ StringBuilder builder = new StringBuilder();
+ boolean builderIsEmpty = true;
+ if (Locale.getDefault().getCountry().equals(Locale.JAPAN.getCountry())) {
+ // In Japan, the order is reversed.
+ for (int i = size - 1; i >= 0; i--) {
+ String addressPart = propValueList.get(i);
+ if (addressPart.length() > 0) {
+ if (!builderIsEmpty) {
+ builder.append(' ');
+ }
+ builder.append(addressPart);
+ builderIsEmpty = false;
+ }
+ }
+ } else {
+ for (int i = 0; i < size; i++) {
+ String addressPart = propValueList.get(i);
+ if (addressPart.length() > 0) {
+ if (!builderIsEmpty) {
+ builder.append(' ');
+ }
+ builder.append(addressPart);
+ builderIsEmpty = false;
+ }
+ }
+ }
+ address = builder.toString().trim();
+ } else {
+ address = propValue;
+ }
+ addContactmethod(kind, type, address, label, isPrimary);
+ } else if (propName.equals("ORG")) {
+ // vCard specification does not specify other types.
+ int type = Contacts.OrganizationColumns.TYPE_WORK;
+ boolean isPrimary = false;
+
+ Collection<String> typeCollection = paramMap.get("TYPE");
+ if (typeCollection != null) {
+ for (String typeString : typeCollection) {
+ if (typeString.equals("PREF") && !mPrefIsSet_Organization) {
+ // vCard specification officially does not have PREF in ORG.
+ // This is just for safety.
+ mPrefIsSet_Organization = true;
+ isPrimary = true;
+ }
+ // XXX: Should we cope with X- words?
+ }
+ }
+
+ int size = propValueList.size();
+ StringBuilder builder = new StringBuilder();
+ for (Iterator<String> iter = propValueList.iterator(); iter.hasNext();) {
+ builder.append(iter.next());
+ if (iter.hasNext()) {
+ builder.append(' ');
+ }
+ }
+
+ addOrganization(type, builder.toString(), "", isPrimary);
+ } else if (propName.equals("TITLE")) {
+ setPosition(propValue);
+ } else if (propName.equals("ROLE")) {
+ setPosition(propValue);
+ } else if ((propName.equals("PHOTO") || (propName.equals("LOGO")) && mPhotoBytes == null)) {
+ // We prefer PHOTO to LOGO.
+ Collection<String> paramMapValue = paramMap.get("VALUE");
+ if (paramMapValue != null && paramMapValue.contains("URL")) {
+ // TODO: do something.
+ } else {
+ // Assume PHOTO is stored in BASE64. In that case,
+ // data is already stored in propValue_bytes in binary form.
+ // It should be automatically done by VBuilder (VDataBuilder/VCardDatabuilder)
+ mPhotoBytes = propBytes;
+ /*
+ Collection<String> typeCollection = paramMap.get("TYPE");
+ if (typeCollection != null) {
+ if (typeCollection.size() > 1) {
+ StringBuilder builder = new StringBuilder();
+ int size = typeCollection.size();
+ int i = 0;
+ for (String type : typeCollection) {
+ builder.append(type);
+ if (i < size - 1) {
+ builder.append(',');
+ }
+ i++;
+ }
+ Log.w(LOG_TAG, "There is more than TYPE: " + builder.toString());
+ }
+ mPhotoType = typeCollection.iterator().next();
+ }*/
+ }
+ } else if (propName.equals("EMAIL")) {
+ int type = -1;
+ String label = null;
+ boolean isPrimary = false;
+ Collection<String> typeCollection = paramMap.get("TYPE");
+ if (typeCollection != null) {
+ for (String typeString : typeCollection) {
+ if (typeString.equals("PREF") && !mPrefIsSet_Email) {
+ // Only first "PREF" is considered.
+ mPrefIsSet_Email = true;
+ isPrimary = true;
+ } else if (typeString.equalsIgnoreCase("HOME")) {
+ type = Contacts.ContactMethodsColumns.TYPE_HOME;
+ } else if (typeString.equalsIgnoreCase("WORK")) {
+ type = Contacts.ContactMethodsColumns.TYPE_WORK;
+ } else if (typeString.equalsIgnoreCase("CELL")) {
+ // We do not have Contacts.ContactMethodsColumns.TYPE_MOBILE yet.
+ type = Contacts.ContactMethodsColumns.TYPE_CUSTOM;
+ label = Contacts.ContactMethodsColumns.MOBILE_EMAIL_TYPE_NAME;
+ } else if (typeString.toUpperCase().startsWith("X-") &&
+ type < 0) {
+ type = Contacts.ContactMethodsColumns.TYPE_CUSTOM;
+ label = typeString.substring(2);
+ } else if (type < 0) {
+ // vCard 3.0 allows iana-token.
+ // We may have INTERNET (specified in vCard spec),
+ // SCHOOL, etc.
+ type = Contacts.ContactMethodsColumns.TYPE_CUSTOM;
+ label = typeString;
+ }
+ }
+ }
+ if (type < 0) {
+ type = Contacts.ContactMethodsColumns.TYPE_OTHER;
+ }
+ addContactmethod(Contacts.KIND_EMAIL, type, propValue,label, isPrimary);
+ } else if (propName.equals("TEL")) {
+ int type = -1;
+ String label = null;
+ boolean isPrimary = false;
+ boolean isFax = false;
+ Collection<String> typeCollection = paramMap.get("TYPE");
+ if (typeCollection != null) {
+ for (String typeString : typeCollection) {
+ if (typeString.equals("PREF") && !mPrefIsSet_Phone) {
+ // Only first "PREF" is considered.
+ mPrefIsSet_Phone = true;
+ isPrimary = true;
+ } else if (typeString.equalsIgnoreCase("HOME")) {
+ type = Contacts.PhonesColumns.TYPE_HOME;
+ } else if (typeString.equalsIgnoreCase("WORK")) {
+ type = Contacts.PhonesColumns.TYPE_WORK;
+ } else if (typeString.equalsIgnoreCase("CELL")) {
+ type = Contacts.PhonesColumns.TYPE_MOBILE;
+ } else if (typeString.equalsIgnoreCase("PAGER")) {
+ type = Contacts.PhonesColumns.TYPE_PAGER;
+ } else if (typeString.equalsIgnoreCase("FAX")) {
+ isFax = true;
+ } else if (typeString.equalsIgnoreCase("VOICE") ||
+ typeString.equalsIgnoreCase("MSG")) {
+ // Defined in vCard 3.0. Ignore these because they
+ // conflict with "HOME", "WORK", etc.
+ // XXX: do something?
+ } else if (typeString.toUpperCase().startsWith("X-") &&
+ type < 0) {
+ type = Contacts.PhonesColumns.TYPE_CUSTOM;
+ label = typeString.substring(2);
+ } else if (type < 0){
+ // We may have MODEM, CAR, ISDN, etc...
+ type = Contacts.PhonesColumns.TYPE_CUSTOM;
+ label = typeString;
+ }
+ }
+ }
+ if (type < 0) {
+ type = Contacts.PhonesColumns.TYPE_HOME;
+ }
+ if (isFax) {
+ if (type == Contacts.PhonesColumns.TYPE_HOME) {
+ type = Contacts.PhonesColumns.TYPE_FAX_HOME;
+ } else if (type == Contacts.PhonesColumns.TYPE_WORK) {
+ type = Contacts.PhonesColumns.TYPE_FAX_WORK;
+ }
+ }
+
+ addPhone(type, propValue, label, isPrimary);
+ } else if (propName.equals("NOTE")) {
+ if (mNotes == null) {
+ mNotes = new ArrayList<String>(1);
+ }
+ mNotes.add(propValue);
+ } else if (propName.equals("BDAY")) {
+ addExtension(propName, paramMap, propValueList);
+ } else if (propName.equals("URL")) {
+ addExtension(propName, paramMap, propValueList);
+ } else if (propName.equals("REV")) {
+ // Revision of this VCard entry. I think we can ignore this.
+ addExtension(propName, paramMap, propValueList);
+ } else if (propName.equals("UID")) {
+ addExtension(propName, paramMap, propValueList);
+ } else if (propName.equals("KEY")) {
+ // Type is X509 or PGP? I don't know how to handle this...
+ addExtension(propName, paramMap, propValueList);
+ } else if (propName.equals("MAILER")) {
+ addExtension(propName, paramMap, propValueList);
+ } else if (propName.equals("TZ")) {
+ addExtension(propName, paramMap, propValueList);
+ } else if (propName.equals("GEO")) {
+ addExtension(propName, paramMap, propValueList);
+ } else if (propName.equals("NICKNAME")) {
+ // vCard 3.0 only.
+ addExtension(propName, paramMap, propValueList);
+ } else if (propName.equals("CLASS")) {
+ // vCard 3.0 only.
+ // e.g. CLASS:CONFIDENTIAL
+ addExtension(propName, paramMap, propValueList);
+ } else if (propName.equals("PROFILE")) {
+ // VCard 3.0 only. Must be "VCARD". I think we can ignore this.
+ addExtension(propName, paramMap, propValueList);
+ } else if (propName.equals("CATEGORIES")) {
+ // VCard 3.0 only.
+ // e.g. CATEGORIES:INTERNET,IETF,INDUSTRY,INFORMATION TECHNOLOGY
+ addExtension(propName, paramMap, propValueList);
+ } else if (propName.equals("SOURCE")) {
+ // VCard 3.0 only.
+ addExtension(propName, paramMap, propValueList);
+ } else if (propName.equals("PRODID")) {
+ // VCard 3.0 only.
+ // To specify the identifier for the product that created
+ // the vCard object.
+ addExtension(propName, paramMap, propValueList);
+ } else if (propName.equals("X-PHONETIC-FIRST-NAME")) {
+ mTmpXPhoneticFirstName = propValue;
+ } else if (propName.equals("X-PHONETIC-MIDDLE-NAME")) {
+ mTmpXPhoneticMiddleName = propValue;
+ } else if (propName.equals("X-PHONETIC-LAST-NAME")) {
+ mTmpXPhoneticLastName = propValue;
+ } else {
+ // Unknown X- words and IANA token.
+ addExtension(propName, paramMap, propValueList);
+ }
+ }
+
+ public String displayString() {
+ if (mName.length() > 0) {
+ return mName;
+ }
+ if (mContactMethodList != null && mContactMethodList.size() > 0) {
+ for (ContactMethod contactMethod : mContactMethodList) {
+ if (contactMethod.kind == Contacts.KIND_EMAIL && contactMethod.isPrimary) {
+ return contactMethod.data;
+ }
+ }
+ }
+ if (mPhoneList != null && mPhoneList.size() > 0) {
+ for (PhoneData phoneData : mPhoneList) {
+ if (phoneData.isPrimary) {
+ return phoneData.data;
+ }
+ }
+ }
+ return "";
+ }
+
+ /**
+ * Consolidate several fielsds (like mName) using name candidates,
+ */
+ public void consolidateFields() {
+ if (mTmpFullName != null) {
+ mName = mTmpFullName;
+ } else if(mTmpNameFromNProperty != null) {
+ mName = mTmpNameFromNProperty;
+ } else {
+ mName = "";
+ }
+
+ if (mPhoneticName == null &&
+ (mTmpXPhoneticFirstName != null || mTmpXPhoneticMiddleName != null ||
+ mTmpXPhoneticLastName != null)) {
+ // Note: In Europe, this order should be "LAST FIRST MIDDLE". See the comment around
+ // NAME_ORDER_TYPE_* for more detail.
+ String first;
+ String second;
+ if (mNameOrderType == VCardConfig.NAME_ORDER_TYPE_JAPANESE) {
+ first = mTmpXPhoneticLastName;
+ second = mTmpXPhoneticFirstName;
+ } else {
+ first = mTmpXPhoneticFirstName;
+ second = mTmpXPhoneticLastName;
+ }
+ StringBuilder builder = new StringBuilder();
+ if (first != null) {
+ builder.append(first);
+ }
+ if (mTmpXPhoneticMiddleName != null) {
+ builder.append(mTmpXPhoneticMiddleName);
+ }
+ if (second != null) {
+ builder.append(second);
+ }
+ mPhoneticName = builder.toString();
+ }
+
+ // Remove unnecessary white spaces.
+ // It is found that some mobile phone emits phonetic name with just one white space
+ // when a user does not specify one.
+ // This logic is effective toward such kind of weird data.
+ if (mPhoneticName != null) {
+ mPhoneticName = mPhoneticName.trim();
+ }
+
+ // If there is no "PREF", we choose the first entries as primary.
+ if (!mPrefIsSet_Phone && mPhoneList != null && mPhoneList.size() > 0) {
+ mPhoneList.get(0).isPrimary = true;
+ }
+
+ if (!mPrefIsSet_Address && mContactMethodList != null) {
+ for (ContactMethod contactMethod : mContactMethodList) {
+ if (contactMethod.kind == Contacts.KIND_POSTAL) {
+ contactMethod.isPrimary = true;
+ break;
+ }
+ }
+ }
+ if (!mPrefIsSet_Email && mContactMethodList != null) {
+ for (ContactMethod contactMethod : mContactMethodList) {
+ if (contactMethod.kind == Contacts.KIND_EMAIL) {
+ contactMethod.isPrimary = true;
+ break;
+ }
+ }
+ }
+ if (!mPrefIsSet_Organization && mOrganizationList != null && mOrganizationList.size() > 0) {
+ mOrganizationList.get(0).isPrimary = true;
+ }
+
+ }
+
+ private void pushIntoContentProviderOrResolver(Object contentSomething,
+ long myContactsGroupId) {
+ ContentResolver resolver = null;
+ AbstractSyncableContentProvider provider = null;
+ if (contentSomething instanceof ContentResolver) {
+ resolver = (ContentResolver)contentSomething;
+ } else if (contentSomething instanceof AbstractSyncableContentProvider) {
+ provider = (AbstractSyncableContentProvider)contentSomething;
+ } else {
+ Log.e(LOG_TAG, "Unsupported object came.");
+ return;
+ }
+
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(People.NAME, mName);
+ contentValues.put(People.PHONETIC_NAME, mPhoneticName);
+
+ if (mNotes != null && mNotes.size() > 0) {
+ if (mNotes.size() > 1) {
+ StringBuilder builder = new StringBuilder();
+ for (String note : mNotes) {
+ builder.append(note);
+ builder.append("\n");
+ }
+ contentValues.put(People.NOTES, builder.toString());
+ } else {
+ contentValues.put(People.NOTES, mNotes.get(0));
+ }
+ }
+
+ Uri personUri;
+ long personId = 0;
+ if (resolver != null) {
+ personUri = Contacts.People.createPersonInMyContactsGroup(resolver, contentValues);
+ if (personUri != null) {
+ personId = ContentUris.parseId(personUri);
+ }
+ } else {
+ personUri = provider.insert(People.CONTENT_URI, contentValues);
+ if (personUri != null) {
+ personId = ContentUris.parseId(personUri);
+ ContentValues values = new ContentValues();
+ values.put(GroupMembership.PERSON_ID, personId);
+ values.put(GroupMembership.GROUP_ID, myContactsGroupId);
+ Uri resultUri = provider.insert(GroupMembership.CONTENT_URI, values);
+ if (resultUri == null) {
+ Log.e(LOG_TAG, "Faild to insert the person to MyContact.");
+ provider.delete(personUri, null, null);
+ personUri = null;
+ }
+ }
+ }
+
+ if (personUri == null) {
+ Log.e(LOG_TAG, "Failed to create the contact.");
+ return;
+ }
+
+ if (mPhotoBytes != null) {
+ if (resolver != null) {
+ People.setPhotoData(resolver, personUri, mPhotoBytes);
+ } else {
+ Uri photoUri = Uri.withAppendedPath(personUri, Contacts.Photos.CONTENT_DIRECTORY);
+ ContentValues values = new ContentValues();
+ values.put(Photos.DATA, mPhotoBytes);
+ provider.update(photoUri, values, null, null);
+ }
+ }
+
+ long primaryPhoneId = -1;
+ if (mPhoneList != null && mPhoneList.size() > 0) {
+ for (PhoneData phoneData : mPhoneList) {
+ ContentValues values = new ContentValues();
+ values.put(Contacts.PhonesColumns.TYPE, phoneData.type);
+ if (phoneData.type == Contacts.PhonesColumns.TYPE_CUSTOM) {
+ values.put(Contacts.PhonesColumns.LABEL, phoneData.label);
+ }
+ // Already formatted.
+ values.put(Contacts.PhonesColumns.NUMBER, phoneData.data);
+
+ // Not sure about Contacts.PhonesColumns.NUMBER_KEY ...
+ values.put(Contacts.PhonesColumns.ISPRIMARY, 1);
+ values.put(Contacts.Phones.PERSON_ID, personId);
+ Uri phoneUri;
+ if (resolver != null) {
+ phoneUri = resolver.insert(Phones.CONTENT_URI, values);
+ } else {
+ phoneUri = provider.insert(Phones.CONTENT_URI, values);
+ }
+ if (phoneData.isPrimary) {
+ primaryPhoneId = Long.parseLong(phoneUri.getLastPathSegment());
+ }
+ }
+ }
+
+ long primaryOrganizationId = -1;
+ if (mOrganizationList != null && mOrganizationList.size() > 0) {
+ for (OrganizationData organizationData : mOrganizationList) {
+ ContentValues values = new ContentValues();
+ // Currently, we do not use TYPE_CUSTOM.
+ values.put(Contacts.OrganizationColumns.TYPE,
+ organizationData.type);
+ values.put(Contacts.OrganizationColumns.COMPANY,
+ organizationData.companyName);
+ values.put(Contacts.OrganizationColumns.TITLE,
+ organizationData.positionName);
+ values.put(Contacts.OrganizationColumns.ISPRIMARY, 1);
+ values.put(Contacts.OrganizationColumns.PERSON_ID, personId);
+
+ Uri organizationUri;
+ if (resolver != null) {
+ organizationUri = resolver.insert(Organizations.CONTENT_URI, values);
+ } else {
+ organizationUri = provider.insert(Organizations.CONTENT_URI, values);
+ }
+ if (organizationData.isPrimary) {
+ primaryOrganizationId = Long.parseLong(organizationUri.getLastPathSegment());
+ }
+ }
+ }
+
+ long primaryEmailId = -1;
+ if (mContactMethodList != null && mContactMethodList.size() > 0) {
+ for (ContactMethod contactMethod : mContactMethodList) {
+ ContentValues values = new ContentValues();
+ values.put(Contacts.ContactMethodsColumns.KIND, contactMethod.kind);
+ values.put(Contacts.ContactMethodsColumns.TYPE, contactMethod.type);
+ if (contactMethod.type == Contacts.ContactMethodsColumns.TYPE_CUSTOM) {
+ values.put(Contacts.ContactMethodsColumns.LABEL, contactMethod.label);
+ }
+ values.put(Contacts.ContactMethodsColumns.DATA, contactMethod.data);
+ values.put(Contacts.ContactMethodsColumns.ISPRIMARY, 1);
+ values.put(Contacts.ContactMethods.PERSON_ID, personId);
+
+ if (contactMethod.kind == Contacts.KIND_EMAIL) {
+ Uri emailUri;
+ if (resolver != null) {
+ emailUri = resolver.insert(ContactMethods.CONTENT_URI, values);
+ } else {
+ emailUri = provider.insert(ContactMethods.CONTENT_URI, values);
+ }
+ if (contactMethod.isPrimary) {
+ primaryEmailId = Long.parseLong(emailUri.getLastPathSegment());
+ }
+ } else { // probably KIND_POSTAL
+ if (resolver != null) {
+ resolver.insert(ContactMethods.CONTENT_URI, values);
+ } else {
+ provider.insert(ContactMethods.CONTENT_URI, values);
+ }
+ }
+ }
+ }
+
+ if (mExtensionMap != null && mExtensionMap.size() > 0) {
+ ArrayList<ContentValues> contentValuesArray;
+ if (resolver != null) {
+ contentValuesArray = new ArrayList<ContentValues>();
+ } else {
+ contentValuesArray = null;
+ }
+ for (Entry<String, List<String>> entry : mExtensionMap.entrySet()) {
+ String key = entry.getKey();
+ List<String> list = entry.getValue();
+ for (String value : list) {
+ ContentValues values = new ContentValues();
+ values.put(Extensions.NAME, key);
+ values.put(Extensions.VALUE, value);
+ values.put(Extensions.PERSON_ID, personId);
+ if (resolver != null) {
+ contentValuesArray.add(values);
+ } else {
+ provider.insert(Extensions.CONTENT_URI, values);
+ }
+ }
+ }
+ if (resolver != null) {
+ resolver.bulkInsert(Extensions.CONTENT_URI,
+ contentValuesArray.toArray(new ContentValues[0]));
+ }
+ }
+
+ if (primaryPhoneId >= 0 || primaryOrganizationId >= 0 || primaryEmailId >= 0) {
+ ContentValues values = new ContentValues();
+ if (primaryPhoneId >= 0) {
+ values.put(People.PRIMARY_PHONE_ID, primaryPhoneId);
+ }
+ if (primaryOrganizationId >= 0) {
+ values.put(People.PRIMARY_ORGANIZATION_ID, primaryOrganizationId);
+ }
+ if (primaryEmailId >= 0) {
+ values.put(People.PRIMARY_EMAIL_ID, primaryEmailId);
+ }
+ if (resolver != null) {
+ resolver.update(personUri, values, null, null);
+ } else {
+ provider.update(personUri, values, null, null);
+ }
+ }
+ }
+
+ /**
+ * Push this object into database in the resolver.
+ */
+ public void pushIntoContentResolver(ContentResolver resolver) {
+ pushIntoContentProviderOrResolver(resolver, 0);
+ }
+
+ /**
+ * Push this object into AbstractSyncableContentProvider object.
+ * {@link #consolidateFields() must be called before this method is called}
+ * @hide
+ */
+ public void pushIntoAbstractSyncableContentProvider(
+ AbstractSyncableContentProvider provider, long myContactsGroupId) {
+ boolean successful = false;
+ provider.beginBatch();
+ try {
+ pushIntoContentProviderOrResolver(provider, myContactsGroupId);
+ successful = true;
+ } finally {
+ provider.endBatch(successful);
+ }
+ }
+
+ public boolean isIgnorable() {
+ return TextUtils.isEmpty(mName) &&
+ TextUtils.isEmpty(mPhoneticName) &&
+ (mPhoneList == null || mPhoneList.size() == 0) &&
+ (mContactMethodList == null || mContactMethodList.size() == 0);
+ }
+
+ private String listToString(List<String> list){
+ final int size = list.size();
+ if (size > 1) {
+ StringBuilder builder = new StringBuilder();
+ int i = 0;
+ for (String type : list) {
+ builder.append(type);
+ if (i < size - 1) {
+ builder.append(";");
+ }
+ }
+ return builder.toString();
+ } else if (size == 1) {
+ return list.get(0);
+ } else {
+ return "";
+ }
+ }
+}
diff --git a/core/java/android/pim/vcard/EntryCommitter.java b/core/java/android/pim/vcard/EntryCommitter.java
new file mode 100644
index 000000000000..e26fac5dbd70
--- /dev/null
+++ b/core/java/android/pim/vcard/EntryCommitter.java
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+package android.pim.vcard;
+
+import android.content.AbstractSyncableContentProvider;
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.IContentProvider;
+import android.provider.Contacts;
+import android.util.Log;
+
+/**
+ * EntryHandler implementation which commits the entry to Contacts Provider
+ */
+public class EntryCommitter implements EntryHandler {
+ public static String LOG_TAG = "vcard.EntryComitter";
+
+ private ContentResolver mContentResolver;
+
+ // Ideally, this should be ContactsProvider but it seems Class loader cannot find it,
+ // even when it is subclass of ContactsProvider...
+ private AbstractSyncableContentProvider mProvider;
+ private long mMyContactsGroupId;
+
+ private long mTimeToCommit;
+
+ public EntryCommitter(ContentResolver resolver) {
+ mContentResolver = resolver;
+
+ tryGetOriginalProvider();
+ }
+
+ public void onFinal() {
+ if (VCardConfig.showPerformanceLog()) {
+ Log.d(LOG_TAG,
+ String.format("time to commit entries: %ld ms", mTimeToCommit));
+ }
+ }
+
+ private void tryGetOriginalProvider() {
+ final ContentResolver resolver = mContentResolver;
+
+ if ((mMyContactsGroupId = Contacts.People.tryGetMyContactsGroupId(resolver)) == 0) {
+ Log.e(LOG_TAG, "Could not get group id of MyContact");
+ return;
+ }
+
+ IContentProvider iProviderForName = resolver.acquireProvider(Contacts.CONTENT_URI);
+ ContentProvider contentProvider =
+ ContentProvider.coerceToLocalContentProvider(iProviderForName);
+ if (contentProvider == null) {
+ Log.e(LOG_TAG, "Fail to get ContentProvider object.");
+ return;
+ }
+
+ if (!(contentProvider instanceof AbstractSyncableContentProvider)) {
+ Log.e(LOG_TAG,
+ "Acquired ContentProvider object is not AbstractSyncableContentProvider.");
+ return;
+ }
+
+ mProvider = (AbstractSyncableContentProvider)contentProvider;
+ }
+
+ public void onEntryCreated(final ContactStruct contactStruct) {
+ long start = System.currentTimeMillis();
+ if (mProvider != null) {
+ contactStruct.pushIntoAbstractSyncableContentProvider(
+ mProvider, mMyContactsGroupId);
+ } else {
+ contactStruct.pushIntoContentResolver(mContentResolver);
+ }
+ mTimeToCommit += System.currentTimeMillis() - start;
+ }
+} \ No newline at end of file
diff --git a/core/java/android/pim/vcard/EntryHandler.java b/core/java/android/pim/vcard/EntryHandler.java
new file mode 100644
index 000000000000..4015cb5b98b0
--- /dev/null
+++ b/core/java/android/pim/vcard/EntryHandler.java
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+package android.pim.vcard;
+
+/**
+ * Unlike VCardBuilderBase, this (and VCardDataBuilder) assumes
+ * "each VCard entry should be correctly parsed and passed to each EntryHandler object",
+ */
+public interface EntryHandler {
+ /**
+ * Able to be use this method for showing performance log, etc.
+ * TODO: better name?
+ */
+ public void onFinal();
+
+ /**
+ * The method called when one VCard entry is successfully created
+ */
+ public void onEntryCreated(final ContactStruct entry);
+}
diff --git a/core/java/android/pim/vcard/VCardBuilder.java b/core/java/android/pim/vcard/VCardBuilder.java
new file mode 100644
index 000000000000..e1c4b338e501
--- /dev/null
+++ b/core/java/android/pim/vcard/VCardBuilder.java
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+package android.pim.vcard;
+
+import java.util.List;
+
+public interface VCardBuilder {
+ void start();
+
+ void end();
+
+ /**
+ * BEGIN:VCARD
+ */
+ void startRecord(String type);
+
+ /** END:VXX */
+ void endRecord();
+
+ void startProperty();
+
+ void endProperty();
+
+ /**
+ * @param group
+ */
+ void propertyGroup(String group);
+
+ /**
+ * @param name
+ * N <br>
+ * N
+ */
+ void propertyName(String name);
+
+ /**
+ * @param type
+ * LANGUAGE \ ENCODING <br>
+ * ;LANGUage= \ ;ENCODING=
+ */
+ void propertyParamType(String type);
+
+ /**
+ * @param value
+ * FR-EN \ GBK <br>
+ * FR-EN \ GBK
+ */
+ void propertyParamValue(String value);
+
+ void propertyValues(List<String> values);
+}
diff --git a/core/java/android/pim/vcard/VCardBuilderCollection.java b/core/java/android/pim/vcard/VCardBuilderCollection.java
new file mode 100644
index 000000000000..e3985b600e69
--- /dev/null
+++ b/core/java/android/pim/vcard/VCardBuilderCollection.java
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+package android.pim.vcard;
+
+import java.util.Collection;
+import java.util.List;
+
+public class VCardBuilderCollection implements VCardBuilder {
+
+ private final Collection<VCardBuilder> mVCardBuilderCollection;
+
+ public VCardBuilderCollection(Collection<VCardBuilder> vBuilderCollection) {
+ mVCardBuilderCollection = vBuilderCollection;
+ }
+
+ public Collection<VCardBuilder> getVCardBuilderBaseCollection() {
+ return mVCardBuilderCollection;
+ }
+
+ public void start() {
+ for (VCardBuilder builder : mVCardBuilderCollection) {
+ builder.start();
+ }
+ }
+
+ public void end() {
+ for (VCardBuilder builder : mVCardBuilderCollection) {
+ builder.end();
+ }
+ }
+
+ public void startRecord(String type) {
+ for (VCardBuilder builder : mVCardBuilderCollection) {
+ builder.startRecord(type);
+ }
+ }
+
+ public void endRecord() {
+ for (VCardBuilder builder : mVCardBuilderCollection) {
+ builder.endRecord();
+ }
+ }
+
+ public void startProperty() {
+ for (VCardBuilder builder : mVCardBuilderCollection) {
+ builder.startProperty();
+ }
+ }
+
+
+ public void endProperty() {
+ for (VCardBuilder builder : mVCardBuilderCollection) {
+ builder.endProperty();
+ }
+ }
+
+ public void propertyGroup(String group) {
+ for (VCardBuilder builder : mVCardBuilderCollection) {
+ builder.propertyGroup(group);
+ }
+ }
+
+ public void propertyName(String name) {
+ for (VCardBuilder builder : mVCardBuilderCollection) {
+ builder.propertyName(name);
+ }
+ }
+
+ public void propertyParamType(String type) {
+ for (VCardBuilder builder : mVCardBuilderCollection) {
+ builder.propertyParamType(type);
+ }
+ }
+
+ public void propertyParamValue(String value) {
+ for (VCardBuilder builder : mVCardBuilderCollection) {
+ builder.propertyParamValue(value);
+ }
+ }
+
+ public void propertyValues(List<String> values) {
+ for (VCardBuilder builder : mVCardBuilderCollection) {
+ builder.propertyValues(values);
+ }
+ }
+}
diff --git a/core/java/android/pim/vcard/VCardConfig.java b/core/java/android/pim/vcard/VCardConfig.java
new file mode 100644
index 000000000000..fef9dba4cc32
--- /dev/null
+++ b/core/java/android/pim/vcard/VCardConfig.java
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+package android.pim.vcard;
+
+/**
+ * The class representing VCard related configurations
+ */
+public class VCardConfig {
+ static final int LOG_LEVEL_NONE = 0;
+ static final int LOG_LEVEL_PERFORMANCE_MEASUREMENT = 0x1;
+ static final int LOG_LEVEL_SHOW_WARNING = 0x2;
+ static final int LOG_LEVEL_VERBOSE =
+ LOG_LEVEL_PERFORMANCE_MEASUREMENT | LOG_LEVEL_SHOW_WARNING;
+
+ // Assumes that "iso-8859-1" is able to map "all" 8bit characters to some unicode and
+ // decode the unicode to the original charset. If not, this setting will cause some bug.
+ public static final String DEFAULT_CHARSET = "iso-8859-1";
+
+ // TODO: use this flag
+ public static boolean IGNORE_CASE_EXCEPT_VALUE = true;
+
+ protected static final int LOG_LEVEL = LOG_LEVEL_PERFORMANCE_MEASUREMENT;
+
+ // Note: phonetic name probably should be "LAST FIRST MIDDLE" for European languages, and
+ // space should be added between each element while it should not be in Japanese.
+ // But unfortunately, we currently do not have the data and are not sure whether we should
+ // support European version of name ordering.
+ //
+ // TODO: Implement the logic described above if we really need European version of
+ // phonetic name handling. Also, adding the appropriate test case of vCard would be
+ // highly appreciated.
+ public static final int NAME_ORDER_TYPE_ENGLISH = 0;
+ public static final int NAME_ORDER_TYPE_JAPANESE = 1;
+
+ public static final int NAME_ORDER_TYPE_DEFAULT = NAME_ORDER_TYPE_ENGLISH;
+
+ /**
+ * @hide temporal. may be deleted
+ */
+ public static boolean showPerformanceLog() {
+ return (LOG_LEVEL & LOG_LEVEL_PERFORMANCE_MEASUREMENT) != 0;
+ }
+
+ private VCardConfig() {
+ }
+} \ No newline at end of file
diff --git a/core/java/android/pim/vcard/VCardDataBuilder.java b/core/java/android/pim/vcard/VCardDataBuilder.java
new file mode 100644
index 000000000000..4025f6c164ca
--- /dev/null
+++ b/core/java/android/pim/vcard/VCardDataBuilder.java
@@ -0,0 +1,319 @@
+/*
+ * 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.
+ */
+package android.pim.vcard;
+
+import android.util.CharsetUtils;
+import android.util.Log;
+
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.codec.net.QuotedPrintableCodec;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * VBuilder for VCard. VCard may contain big photo images encoded by BASE64,
+ * If we store all VNode entries in memory like VDataBuilder.java,
+ * OutOfMemoryError may be thrown. Thus, this class push each VCard entry into
+ * ContentResolver immediately.
+ */
+public class VCardDataBuilder implements VCardBuilder {
+ static private String LOG_TAG = "VCardDataBuilder";
+
+ /**
+ * If there's no other information available, this class uses this charset for encoding
+ * byte arrays.
+ */
+ static public String TARGET_CHARSET = "UTF-8";
+
+ private ContactStruct.Property mCurrentProperty = new ContactStruct.Property();
+ private ContactStruct mCurrentContactStruct;
+ private String mParamType;
+
+ /**
+ * The charset using which VParser parses the text.
+ */
+ private String mSourceCharset;
+
+ /**
+ * The charset with which byte array is encoded to String.
+ */
+ private String mTargetCharset;
+ private boolean mStrictLineBreakParsing;
+
+ private int mNameOrderType;
+
+ // Just for testing.
+ private long mTimePushIntoContentResolver;
+
+ private List<EntryHandler> mEntryHandlers = new ArrayList<EntryHandler>();
+
+ public VCardDataBuilder() {
+ this(null, null, false, VCardConfig.NAME_ORDER_TYPE_DEFAULT);
+ }
+
+ /**
+ * @hide
+ */
+ public VCardDataBuilder(int nameOrderType) {
+ this(null, null, false, nameOrderType);
+ }
+
+ /**
+ * @hide
+ */
+ public VCardDataBuilder(String charset,
+ boolean strictLineBreakParsing,
+ int nameOrderType) {
+ this(null, charset, strictLineBreakParsing, nameOrderType);
+ }
+
+ /**
+ * @hide
+ */
+ public VCardDataBuilder(String sourceCharset,
+ String targetCharset,
+ boolean strictLineBreakParsing,
+ int nameOrderType) {
+ if (sourceCharset != null) {
+ mSourceCharset = sourceCharset;
+ } else {
+ mSourceCharset = VCardConfig.DEFAULT_CHARSET;
+ }
+ if (targetCharset != null) {
+ mTargetCharset = targetCharset;
+ } else {
+ mTargetCharset = TARGET_CHARSET;
+ }
+ mStrictLineBreakParsing = strictLineBreakParsing;
+ mNameOrderType = nameOrderType;
+ }
+
+ public void addEntryHandler(EntryHandler entryHandler) {
+ mEntryHandlers.add(entryHandler);
+ }
+
+ public void start() {
+ }
+
+ public void end() {
+ for (EntryHandler entryHandler : mEntryHandlers) {
+ entryHandler.onFinal();
+ }
+ }
+
+ /**
+ * Assume that VCard is not nested. In other words, this code does not accept
+ */
+ public void startRecord(String type) {
+ // TODO: add the method clear() instead of using null for reducing GC?
+ if (mCurrentContactStruct != null) {
+ // This means startRecord() is called inside startRecord() - endRecord() block.
+ // TODO: should throw some Exception
+ Log.e(LOG_TAG, "Nested VCard code is not supported now.");
+ }
+ if (!type.equalsIgnoreCase("VCARD")) {
+ // TODO: add test case for this
+ Log.e(LOG_TAG, "This is not VCARD!");
+ }
+
+ mCurrentContactStruct = new ContactStruct(mNameOrderType);
+ }
+
+ public void endRecord() {
+ mCurrentContactStruct.consolidateFields();
+ for (EntryHandler entryHandler : mEntryHandlers) {
+ entryHandler.onEntryCreated(mCurrentContactStruct);
+ }
+ mCurrentContactStruct = null;
+ }
+
+ public void startProperty() {
+ mCurrentProperty.clear();
+ }
+
+ public void endProperty() {
+ mCurrentContactStruct.addProperty(mCurrentProperty);
+ }
+
+ public void propertyName(String name) {
+ mCurrentProperty.setPropertyName(name);
+ }
+
+ public void propertyGroup(String group) {
+ // ContactStruct does not support Group.
+ }
+
+ public void propertyParamType(String type) {
+ if (mParamType != null) {
+ Log.e(LOG_TAG,
+ "propertyParamType() is called more than once " +
+ "before propertyParamValue() is called");
+ }
+ mParamType = type;
+ }
+
+ public void propertyParamValue(String value) {
+ if (mParamType == null) {
+ mParamType = "TYPE";
+ }
+ mCurrentProperty.addParameter(mParamType, value);
+ mParamType = null;
+ }
+
+ private String encodeString(String originalString, String targetCharset) {
+ if (mSourceCharset.equalsIgnoreCase(targetCharset)) {
+ return originalString;
+ }
+ Charset charset = Charset.forName(mSourceCharset);
+ ByteBuffer byteBuffer = charset.encode(originalString);
+ // byteBuffer.array() "may" return byte array which is larger than
+ // byteBuffer.remaining(). Here, we keep on the safe side.
+ byte[] bytes = new byte[byteBuffer.remaining()];
+ byteBuffer.get(bytes);
+ try {
+ return new String(bytes, targetCharset);
+ } catch (UnsupportedEncodingException e) {
+ Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset);
+ return null;
+ }
+ }
+
+ private String handleOneValue(String value, String targetCharset, String encoding) {
+ if (encoding != null) {
+ if (encoding.equals("BASE64") || encoding.equals("B")) {
+ mCurrentProperty.setPropertyBytes(Base64.decodeBase64(value.getBytes()));
+ return value;
+ } else if (encoding.equals("QUOTED-PRINTABLE")) {
+ // "= " -> " ", "=\t" -> "\t".
+ // Previous code had done this replacement. Keep on the safe side.
+ StringBuilder builder = new StringBuilder();
+ int length = value.length();
+ for (int i = 0; i < length; i++) {
+ char ch = value.charAt(i);
+ if (ch == '=' && i < length - 1) {
+ char nextCh = value.charAt(i + 1);
+ if (nextCh == ' ' || nextCh == '\t') {
+
+ builder.append(nextCh);
+ i++;
+ continue;
+ }
+ }
+ builder.append(ch);
+ }
+ String quotedPrintable = builder.toString();
+
+ String[] lines;
+ if (mStrictLineBreakParsing) {
+ lines = quotedPrintable.split("\r\n");
+ } else {
+ builder = new StringBuilder();
+ length = quotedPrintable.length();
+ ArrayList<String> list = new ArrayList<String>();
+ for (int i = 0; i < length; i++) {
+ char ch = quotedPrintable.charAt(i);
+ if (ch == '\n') {
+ list.add(builder.toString());
+ builder = new StringBuilder();
+ } else if (ch == '\r') {
+ list.add(builder.toString());
+ builder = new StringBuilder();
+ if (i < length - 1) {
+ char nextCh = quotedPrintable.charAt(i + 1);
+ if (nextCh == '\n') {
+ i++;
+ }
+ }
+ } else {
+ builder.append(ch);
+ }
+ }
+ String finalLine = builder.toString();
+ if (finalLine.length() > 0) {
+ list.add(finalLine);
+ }
+ lines = list.toArray(new String[0]);
+ }
+
+ builder = new StringBuilder();
+ for (String line : lines) {
+ if (line.endsWith("=")) {
+ line = line.substring(0, line.length() - 1);
+ }
+ builder.append(line);
+ }
+ byte[] bytes;
+ try {
+ bytes = builder.toString().getBytes(mSourceCharset);
+ } catch (UnsupportedEncodingException e1) {
+ Log.e(LOG_TAG, "Failed to encode: charset=" + mSourceCharset);
+ bytes = builder.toString().getBytes();
+ }
+
+ try {
+ bytes = QuotedPrintableCodec.decodeQuotedPrintable(bytes);
+ } catch (DecoderException e) {
+ Log.e(LOG_TAG, "Failed to decode quoted-printable: " + e);
+ return "";
+ }
+
+ try {
+ return new String(bytes, targetCharset);
+ } catch (UnsupportedEncodingException e) {
+ Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset);
+ return new String(bytes);
+ }
+ }
+ // Unknown encoding. Fall back to default.
+ }
+ return encodeString(value, targetCharset);
+ }
+
+ public void propertyValues(List<String> values) {
+ if (values == null || values.size() == 0) {
+ return;
+ }
+
+ final Collection<String> charsetCollection = mCurrentProperty.getParameters("CHARSET");
+ String charset =
+ ((charsetCollection != null) ? charsetCollection.iterator().next() : null);
+ String targetCharset = CharsetUtils.nameForDefaultVendor(charset);
+
+ final Collection<String> encodingCollection = mCurrentProperty.getParameters("ENCODING");
+ String encoding =
+ ((encodingCollection != null) ? encodingCollection.iterator().next() : null);
+
+ if (targetCharset == null || targetCharset.length() == 0) {
+ targetCharset = mTargetCharset;
+ }
+
+ for (String value : values) {
+ mCurrentProperty.addToPropertyValueList(
+ handleOneValue(value, targetCharset, encoding));
+ }
+ }
+
+ public void showPerformanceInfo() {
+ Log.d(LOG_TAG, "time for insert ContactStruct to database: " +
+ mTimePushIntoContentResolver + " ms");
+ }
+}
diff --git a/core/java/android/pim/vcard/VCardEntryCounter.java b/core/java/android/pim/vcard/VCardEntryCounter.java
new file mode 100644
index 000000000000..f99b46c83ede
--- /dev/null
+++ b/core/java/android/pim/vcard/VCardEntryCounter.java
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+package android.pim.vcard;
+
+import java.util.List;
+
+public class VCardEntryCounter implements VCardBuilder {
+ private int mCount;
+
+ public int getCount() {
+ return mCount;
+ }
+
+ public void start() {
+ }
+
+ public void end() {
+ }
+
+ public void startRecord(String type) {
+ }
+
+ public void endRecord() {
+ mCount++;
+ }
+
+ public void startProperty() {
+ }
+
+ public void endProperty() {
+ }
+
+ public void propertyGroup(String group) {
+ }
+
+ public void propertyName(String name) {
+ }
+
+ public void propertyParamType(String type) {
+ }
+
+ public void propertyParamValue(String value) {
+ }
+
+ public void propertyValues(List<String> values) {
+ }
+} \ No newline at end of file
diff --git a/core/java/android/pim/vcard/VCardParser.java b/core/java/android/pim/vcard/VCardParser.java
new file mode 100644
index 000000000000..b5e504982b36
--- /dev/null
+++ b/core/java/android/pim/vcard/VCardParser.java
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+package android.pim.vcard;
+
+import android.pim.vcard.exception.VCardException;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public abstract class VCardParser {
+
+ protected boolean mCanceled;
+
+ /**
+ * Parses the given stream and send the VCard data into VCardBuilderBase object.
+ *
+ * Note that vCard 2.1 specification allows "CHARSET" parameter, and some career sets
+ * local encoding to it. For example, Japanese phone career uses Shift_JIS, which is
+ * formally allowed in VCard 2.1, but not recommended in VCard 3.0. In VCard 2.1,
+ * In some exreme case, some VCard may have different charsets in one VCard (though
+ * we do not see any device which emits such kind of malicious data)
+ *
+ * In order to avoid "misunderstanding" charset as much as possible, this method
+ * use "ISO-8859-1" for reading the stream. When charset is specified in some property
+ * (with "CHARSET=..." attribute), the string is decoded to raw bytes and encoded to
+ * the charset. This method assumes that "ISO-8859-1" has 1 to 1 mapping in all 8bit
+ * characters, which is not completely sure. In some cases, this "decoding-encoding"
+ * scheme may fail. To avoid the case,
+ *
+ * We recommend you to use VCardSourceDetector and detect which kind of source the
+ * VCard comes from and explicitly specify a charset using the result.
+ *
+ * @param is The source to parse.
+ * @param builder The VCardBuilderBase object which used to construct data. If you want to
+ * include multiple VCardBuilderBase objects in this field, consider using
+ * {#link VCardBuilderCollection} class.
+ * @return Returns true for success. Otherwise returns false.
+ * @throws IOException, VCardException
+ */
+ public abstract boolean parse(InputStream is, VCardBuilder builder)
+ throws IOException, VCardException;
+
+ /**
+ * The method variants which accept charset.
+ *
+ * RFC 2426 "recommends" (not forces) to use UTF-8, so it may be OK to use
+ * UTF-8 as an encoding when parsing vCard 3.0. But note that some Japanese
+ * phone uses Shift_JIS as a charset (e.g. W61SH), and another uses
+ * "CHARSET=SHIFT_JIS", which is explicitly prohibited in vCard 3.0 specification
+ * (e.g. W53K).
+ *
+ * @param is The source to parse.
+ * @param charset Charset to be used.
+ * @param builder The VCardBuilderBase object.
+ * @return Returns true when successful. Otherwise returns false.
+ * @throws IOException, VCardException
+ */
+ public abstract boolean parse(InputStream is, String charset, VCardBuilder builder)
+ throws IOException, VCardException;
+
+ /**
+ * The method variants which tells this object the operation is already canceled.
+ * XXX: Is this really necessary?
+ * @hide
+ */
+ public abstract void parse(InputStream is, String charset,
+ VCardBuilder builder, boolean canceled)
+ throws IOException, VCardException;
+
+ /**
+ * Cancel parsing.
+ * Actual cancel is done after the end of the current one vcard entry parsing.
+ */
+ public void cancel() {
+ mCanceled = true;
+ }
+}
diff --git a/core/java/android/pim/vcard/VCardParser_V21.java b/core/java/android/pim/vcard/VCardParser_V21.java
new file mode 100644
index 000000000000..17a138fab981
--- /dev/null
+++ b/core/java/android/pim/vcard/VCardParser_V21.java
@@ -0,0 +1,948 @@
+/*
+ * 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.
+ */
+package android.pim.vcard;
+
+import android.pim.vcard.exception.VCardException;
+import android.pim.vcard.exception.VCardNestedException;
+import android.pim.vcard.exception.VCardNotSupportedException;
+import android.pim.vcard.exception.VCardVersionException;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+
+/**
+ * This class is used to parse vcard. Please refer to vCard Specification 2.1.
+ */
+public class VCardParser_V21 extends VCardParser {
+ private static final String LOG_TAG = "VCardParser_V21";
+
+ /** Store the known-type */
+ private static final HashSet<String> sKnownTypeSet = new HashSet<String>(
+ Arrays.asList("DOM", "INTL", "POSTAL", "PARCEL", "HOME", "WORK",
+ "PREF", "VOICE", "FAX", "MSG", "CELL", "PAGER", "BBS",
+ "MODEM", "CAR", "ISDN", "VIDEO", "AOL", "APPLELINK",
+ "ATTMAIL", "CIS", "EWORLD", "INTERNET", "IBMMAIL",
+ "MCIMAIL", "POWERSHARE", "PRODIGY", "TLX", "X400", "GIF",
+ "CGM", "WMF", "BMP", "MET", "PMB", "DIB", "PICT", "TIFF",
+ "PDF", "PS", "JPEG", "QTIME", "MPEG", "MPEG2", "AVI",
+ "WAVE", "AIFF", "PCM", "X509", "PGP"));
+
+ /** Store the known-value */
+ private static final HashSet<String> sKnownValueSet = new HashSet<String>(
+ Arrays.asList("INLINE", "URL", "CONTENT-ID", "CID"));
+
+ /** Store the property names available in vCard 2.1 */
+ private static final HashSet<String> sAvailablePropertyNameV21 =
+ new HashSet<String>(Arrays.asList(
+ "BEGIN", "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND",
+ "VERSION", "TEL", "EMAIL", "TZ", "GEO", "NOTE", "URL",
+ "BDAY", "ROLE", "REV", "UID", "KEY", "MAILER"));
+
+ // Though vCard 2.1 specification does not allow "B" encoding, some data may have it.
+ // We allow it for safety...
+ private static final HashSet<String> sAvailableEncodingV21 =
+ new HashSet<String>(Arrays.asList(
+ "7BIT", "8BIT", "QUOTED-PRINTABLE", "BASE64", "B"));
+
+ // Used only for parsing END:VCARD.
+ private String mPreviousLine;
+
+ /** The builder to build parsed data */
+ protected VCardBuilder mBuilder = null;
+
+ /** The encoding type */
+ protected String mEncoding = null;
+
+ protected final String sDefaultEncoding = "8BIT";
+
+ // Should not directly read a line from this. Use getLine() instead.
+ protected BufferedReader mReader;
+
+ // In some cases, vCard is nested. Currently, we only consider the most interior vCard data.
+ // See v21_foma_1.vcf in test directory for more information.
+ private int mNestCount;
+
+ // In order to reduce warning message as much as possible, we hold the value which made Logger
+ // emit a warning message.
+ protected HashSet<String> mWarningValueMap = new HashSet<String>();
+
+ // Just for debugging
+ private long mTimeTotal;
+ private long mTimeStartRecord;
+ private long mTimeEndRecord;
+ private long mTimeStartProperty;
+ private long mTimeEndProperty;
+ private long mTimeParseItems;
+ private long mTimeParseItem1;
+ private long mTimeParseItem2;
+ private long mTimeParseItem3;
+ private long mTimeHandlePropertyValue1;
+ private long mTimeHandlePropertyValue2;
+ private long mTimeHandlePropertyValue3;
+
+ /**
+ * Create a new VCard parser.
+ */
+ public VCardParser_V21() {
+ super();
+ }
+
+ public VCardParser_V21(VCardSourceDetector detector) {
+ super();
+ if (detector != null && detector.getType() == VCardSourceDetector.TYPE_FOMA) {
+ mNestCount = 1;
+ }
+ }
+
+ /**
+ * Parse the file at the given position
+ * vcard_file = [wsls] vcard [wsls]
+ */
+ protected void parseVCardFile() throws IOException, VCardException {
+ boolean firstReading = true;
+ while (true) {
+ if (mCanceled) {
+ break;
+ }
+ if (!parseOneVCard(firstReading)) {
+ break;
+ }
+ firstReading = false;
+ }
+
+ if (mNestCount > 0) {
+ boolean useCache = true;
+ for (int i = 0; i < mNestCount; i++) {
+ readEndVCard(useCache, true);
+ useCache = false;
+ }
+ }
+ }
+
+ protected String getVersion() {
+ return "2.1";
+ }
+
+ /**
+ * @return true when the propertyName is a valid property name.
+ */
+ protected boolean isValidPropertyName(String propertyName) {
+ if (!(sAvailablePropertyNameV21.contains(propertyName.toUpperCase()) ||
+ propertyName.startsWith("X-")) &&
+ !mWarningValueMap.contains(propertyName)) {
+ mWarningValueMap.add(propertyName);
+ Log.w(LOG_TAG, "Property name unsupported by vCard 2.1: " + propertyName);
+ }
+ return true;
+ }
+
+ /**
+ * @return true when the encoding is a valid encoding.
+ */
+ protected boolean isValidEncoding(String encoding) {
+ return sAvailableEncodingV21.contains(encoding.toUpperCase());
+ }
+
+ /**
+ * @return String. It may be null, or its length may be 0
+ * @throws IOException
+ */
+ protected String getLine() throws IOException {
+ return mReader.readLine();
+ }
+
+ /**
+ * @return String with it's length > 0
+ * @throws IOException
+ * @throws VCardException when the stream reached end of line
+ */
+ protected String getNonEmptyLine() throws IOException, VCardException {
+ String line;
+ while (true) {
+ line = getLine();
+ if (line == null) {
+ throw new VCardException("Reached end of buffer.");
+ } else if (line.trim().length() > 0) {
+ return line;
+ }
+ }
+ }
+
+ /**
+ * vcard = "BEGIN" [ws] ":" [ws] "VCARD" [ws] 1*CRLF
+ * items *CRLF
+ * "END" [ws] ":" [ws] "VCARD"
+ */
+ private boolean parseOneVCard(boolean firstReading) throws IOException, VCardException {
+ boolean allowGarbage = false;
+ if (firstReading) {
+ if (mNestCount > 0) {
+ for (int i = 0; i < mNestCount; i++) {
+ if (!readBeginVCard(allowGarbage)) {
+ return false;
+ }
+ allowGarbage = true;
+ }
+ }
+ }
+
+ if (!readBeginVCard(allowGarbage)) {
+ return false;
+ }
+ long start;
+ if (mBuilder != null) {
+ start = System.currentTimeMillis();
+ mBuilder.startRecord("VCARD");
+ mTimeStartRecord += System.currentTimeMillis() - start;
+ }
+ start = System.currentTimeMillis();
+ parseItems();
+ mTimeParseItems += System.currentTimeMillis() - start;
+ readEndVCard(true, false);
+ if (mBuilder != null) {
+ start = System.currentTimeMillis();
+ mBuilder.endRecord();
+ mTimeEndRecord += System.currentTimeMillis() - start;
+ }
+ return true;
+ }
+
+ /**
+ * @return True when successful. False when reaching the end of line
+ * @throws IOException
+ * @throws VCardException
+ */
+ protected boolean readBeginVCard(boolean allowGarbage)
+ throws IOException, VCardException {
+ String line;
+ do {
+ while (true) {
+ line = getLine();
+ if (line == null) {
+ return false;
+ } else if (line.trim().length() > 0) {
+ break;
+ }
+ }
+ String[] strArray = line.split(":", 2);
+ int length = strArray.length;
+
+ // Though vCard 2.1/3.0 specification does not allow lower cases,
+ // some data may have them, so we allow it (Actually, previous code
+ // had explicitly allowed "BEGIN:vCard" though there's no example).
+ //
+ // TODO: ignore non vCard entry (e.g. vcalendar).
+ // XXX: Not sure, but according to VDataBuilder.java, vcalendar
+ // entry
+ // may be nested. Just seeking "END:SOMETHING" may not be enough.
+ // e.g.
+ // BEGIN:VCARD
+ // ... (Valid. Must parse this)
+ // END:VCARD
+ // BEGIN:VSOMETHING
+ // ... (Must ignore this)
+ // BEGIN:VSOMETHING2
+ // ... (Must ignore this)
+ // END:VSOMETHING2
+ // ... (Must ignore this!)
+ // END:VSOMETHING
+ // BEGIN:VCARD
+ // ... (Valid. Must parse this)
+ // END:VCARD
+ // INVALID_STRING (VCardException should be thrown)
+ if (length == 2 &&
+ strArray[0].trim().equalsIgnoreCase("BEGIN") &&
+ strArray[1].trim().equalsIgnoreCase("VCARD")) {
+ return true;
+ } else if (!allowGarbage) {
+ if (mNestCount > 0) {
+ mPreviousLine = line;
+ return false;
+ } else {
+ throw new VCardException(
+ "Expected String \"BEGIN:VCARD\" did not come "
+ + "(Instead, \"" + line + "\" came)");
+ }
+ }
+ } while(allowGarbage);
+
+ throw new VCardException("Reached where must not be reached.");
+ }
+
+ /**
+ * The arguments useCache and allowGarbase are usually true and false accordingly when
+ * this function is called outside this function itself.
+ *
+ * @param useCache When true, line is obtained from mPreviousline. Otherwise, getLine()
+ * is used.
+ * @param allowGarbage When true, ignore non "END:VCARD" line.
+ * @throws IOException
+ * @throws VCardException
+ */
+ protected void readEndVCard(boolean useCache, boolean allowGarbage)
+ throws IOException, VCardException {
+ String line;
+ do {
+ if (useCache) {
+ // Though vCard specification does not allow lower cases,
+ // some data may have them, so we allow it.
+ line = mPreviousLine;
+ } else {
+ while (true) {
+ line = getLine();
+ if (line == null) {
+ throw new VCardException("Expected END:VCARD was not found.");
+ } else if (line.trim().length() > 0) {
+ break;
+ }
+ }
+ }
+
+ String[] strArray = line.split(":", 2);
+ if (strArray.length == 2 &&
+ strArray[0].trim().equalsIgnoreCase("END") &&
+ strArray[1].trim().equalsIgnoreCase("VCARD")) {
+ return;
+ } else if (!allowGarbage) {
+ throw new VCardException("END:VCARD != \"" + mPreviousLine + "\"");
+ }
+ useCache = false;
+ } while (allowGarbage);
+ }
+
+ /**
+ * items = *CRLF item
+ * / item
+ */
+ protected void parseItems() throws IOException, VCardException {
+ /* items *CRLF item / item */
+ boolean ended = false;
+
+ if (mBuilder != null) {
+ long start = System.currentTimeMillis();
+ mBuilder.startProperty();
+ mTimeStartProperty += System.currentTimeMillis() - start;
+ }
+ ended = parseItem();
+ if (mBuilder != null && !ended) {
+ long start = System.currentTimeMillis();
+ mBuilder.endProperty();
+ mTimeEndProperty += System.currentTimeMillis() - start;
+ }
+
+ while (!ended) {
+ // follow VCARD ,it wont reach endProperty
+ if (mBuilder != null) {
+ long start = System.currentTimeMillis();
+ mBuilder.startProperty();
+ mTimeStartProperty += System.currentTimeMillis() - start;
+ }
+ ended = parseItem();
+ if (mBuilder != null && !ended) {
+ long start = System.currentTimeMillis();
+ mBuilder.endProperty();
+ mTimeEndProperty += System.currentTimeMillis() - start;
+ }
+ }
+ }
+
+ /**
+ * item = [groups "."] name [params] ":" value CRLF
+ * / [groups "."] "ADR" [params] ":" addressparts CRLF
+ * / [groups "."] "ORG" [params] ":" orgparts CRLF
+ * / [groups "."] "N" [params] ":" nameparts CRLF
+ * / [groups "."] "AGENT" [params] ":" vcard CRLF
+ */
+ protected boolean parseItem() throws IOException, VCardException {
+ mEncoding = sDefaultEncoding;
+
+ String line = getNonEmptyLine();
+ long start = System.currentTimeMillis();
+
+ String[] propertyNameAndValue = separateLineAndHandleGroup(line);
+ if (propertyNameAndValue == null) {
+ return true;
+ }
+ if (propertyNameAndValue.length != 2) {
+ throw new VCardException("Invalid line \"" + line + "\"");
+ }
+ String propertyName = propertyNameAndValue[0].toUpperCase();
+ String propertyValue = propertyNameAndValue[1];
+
+ mTimeParseItem1 += System.currentTimeMillis() - start;
+
+ if (propertyName.equals("ADR") ||
+ propertyName.equals("ORG") ||
+ propertyName.equals("N")) {
+ start = System.currentTimeMillis();
+ handleMultiplePropertyValue(propertyName, propertyValue);
+ mTimeParseItem3 += System.currentTimeMillis() - start;
+ return false;
+ } else if (propertyName.equals("AGENT")) {
+ handleAgent(propertyValue);
+ return false;
+ } else if (isValidPropertyName(propertyName)) {
+ if (propertyName.equals("BEGIN")) {
+ if (propertyValue.equals("VCARD")) {
+ throw new VCardNestedException("This vCard has nested vCard data in it.");
+ } else {
+ throw new VCardException("Unknown BEGIN type: " + propertyValue);
+ }
+ } else if (propertyName.equals("VERSION") &&
+ !propertyValue.equals(getVersion())) {
+ throw new VCardVersionException("Incompatible version: " +
+ propertyValue + " != " + getVersion());
+ }
+ start = System.currentTimeMillis();
+ handlePropertyValue(propertyName, propertyValue);
+ mTimeParseItem2 += System.currentTimeMillis() - start;
+ return false;
+ }
+
+ throw new VCardException("Unknown property name: \"" +
+ propertyName + "\"");
+ }
+
+ static private final int STATE_GROUP_OR_PROPNAME = 0;
+ static private final int STATE_PARAMS = 1;
+ // vCard 3.1 specification allows double-quoted param-value, while vCard 2.1 does not.
+ // This is just for safety.
+ static private final int STATE_PARAMS_IN_DQUOTE = 2;
+
+ protected String[] separateLineAndHandleGroup(String line) throws VCardException {
+ int length = line.length();
+ int state = STATE_GROUP_OR_PROPNAME;
+ int nameIndex = 0;
+
+ String[] propertyNameAndValue = new String[2];
+
+ for (int i = 0; i < length; i++) {
+ char ch = line.charAt(i);
+ switch (state) {
+ case STATE_GROUP_OR_PROPNAME:
+ if (ch == ':') {
+ String propertyName = line.substring(nameIndex, i);
+ if (propertyName.equalsIgnoreCase("END")) {
+ mPreviousLine = line;
+ return null;
+ }
+ if (mBuilder != null) {
+ mBuilder.propertyName(propertyName);
+ }
+ propertyNameAndValue[0] = propertyName;
+ if (i < length - 1) {
+ propertyNameAndValue[1] = line.substring(i + 1);
+ } else {
+ propertyNameAndValue[1] = "";
+ }
+ return propertyNameAndValue;
+ } else if (ch == '.') {
+ String groupName = line.substring(nameIndex, i);
+ if (mBuilder != null) {
+ mBuilder.propertyGroup(groupName);
+ }
+ nameIndex = i + 1;
+ } else if (ch == ';') {
+ String propertyName = line.substring(nameIndex, i);
+ if (propertyName.equalsIgnoreCase("END")) {
+ mPreviousLine = line;
+ return null;
+ }
+ if (mBuilder != null) {
+ mBuilder.propertyName(propertyName);
+ }
+ propertyNameAndValue[0] = propertyName;
+ nameIndex = i + 1;
+ state = STATE_PARAMS;
+ }
+ break;
+ case STATE_PARAMS:
+ if (ch == '"') {
+ state = STATE_PARAMS_IN_DQUOTE;
+ } else if (ch == ';') {
+ handleParams(line.substring(nameIndex, i));
+ nameIndex = i + 1;
+ } else if (ch == ':') {
+ handleParams(line.substring(nameIndex, i));
+ if (i < length - 1) {
+ propertyNameAndValue[1] = line.substring(i + 1);
+ } else {
+ propertyNameAndValue[1] = "";
+ }
+ return propertyNameAndValue;
+ }
+ break;
+ case STATE_PARAMS_IN_DQUOTE:
+ if (ch == '"') {
+ state = STATE_PARAMS;
+ }
+ break;
+ }
+ }
+
+ throw new VCardException("Invalid line: \"" + line + "\"");
+ }
+
+
+ /**
+ * params = ";" [ws] paramlist
+ * paramlist = paramlist [ws] ";" [ws] param
+ * / param
+ * param = "TYPE" [ws] "=" [ws] ptypeval
+ * / "VALUE" [ws] "=" [ws] pvalueval
+ * / "ENCODING" [ws] "=" [ws] pencodingval
+ * / "CHARSET" [ws] "=" [ws] charsetval
+ * / "LANGUAGE" [ws] "=" [ws] langval
+ * / "X-" word [ws] "=" [ws] word
+ * / knowntype
+ */
+ protected void handleParams(String params) throws VCardException {
+ String[] strArray = params.split("=", 2);
+ if (strArray.length == 2) {
+ String paramName = strArray[0].trim();
+ String paramValue = strArray[1].trim();
+ if (paramName.equals("TYPE")) {
+ handleType(paramValue);
+ } else if (paramName.equals("VALUE")) {
+ handleValue(paramValue);
+ } else if (paramName.equals("ENCODING")) {
+ handleEncoding(paramValue);
+ } else if (paramName.equals("CHARSET")) {
+ handleCharset(paramValue);
+ } else if (paramName.equals("LANGUAGE")) {
+ handleLanguage(paramValue);
+ } else if (paramName.startsWith("X-")) {
+ handleAnyParam(paramName, paramValue);
+ } else {
+ throw new VCardException("Unknown type \"" + paramName + "\"");
+ }
+ } else {
+ handleType(strArray[0]);
+ }
+ }
+
+ /**
+ * ptypeval = knowntype / "X-" word
+ */
+ protected void handleType(String ptypeval) {
+ String upperTypeValue = ptypeval;
+ if (!(sKnownTypeSet.contains(upperTypeValue) || upperTypeValue.startsWith("X-")) &&
+ !mWarningValueMap.contains(ptypeval)) {
+ mWarningValueMap.add(ptypeval);
+ Log.w(LOG_TAG, "Type unsupported by vCard 2.1: " + ptypeval);
+ }
+ if (mBuilder != null) {
+ mBuilder.propertyParamType("TYPE");
+ mBuilder.propertyParamValue(upperTypeValue);
+ }
+ }
+
+ /**
+ * pvalueval = "INLINE" / "URL" / "CONTENT-ID" / "CID" / "X-" word
+ */
+ protected void handleValue(String pvalueval) throws VCardException {
+ if (sKnownValueSet.contains(pvalueval.toUpperCase()) ||
+ pvalueval.startsWith("X-")) {
+ if (mBuilder != null) {
+ mBuilder.propertyParamType("VALUE");
+ mBuilder.propertyParamValue(pvalueval);
+ }
+ } else {
+ throw new VCardException("Unknown value \"" + pvalueval + "\"");
+ }
+ }
+
+ /**
+ * pencodingval = "7BIT" / "8BIT" / "QUOTED-PRINTABLE" / "BASE64" / "X-" word
+ */
+ protected void handleEncoding(String pencodingval) throws VCardException {
+ if (isValidEncoding(pencodingval) ||
+ pencodingval.startsWith("X-")) {
+ if (mBuilder != null) {
+ mBuilder.propertyParamType("ENCODING");
+ mBuilder.propertyParamValue(pencodingval);
+ }
+ mEncoding = pencodingval;
+ } else {
+ throw new VCardException("Unknown encoding \"" + pencodingval + "\"");
+ }
+ }
+
+ /**
+ * vCard specification only allows us-ascii and iso-8859-xxx (See RFC 1521),
+ * but some vCard contains other charset, so we allow them.
+ */
+ protected void handleCharset(String charsetval) {
+ if (mBuilder != null) {
+ mBuilder.propertyParamType("CHARSET");
+ mBuilder.propertyParamValue(charsetval);
+ }
+ }
+
+ /**
+ * See also Section 7.1 of RFC 1521
+ */
+ protected void handleLanguage(String langval) throws VCardException {
+ String[] strArray = langval.split("-");
+ if (strArray.length != 2) {
+ throw new VCardException("Invalid Language: \"" + langval + "\"");
+ }
+ String tmp = strArray[0];
+ int length = tmp.length();
+ for (int i = 0; i < length; i++) {
+ if (!isLetter(tmp.charAt(i))) {
+ throw new VCardException("Invalid Language: \"" + langval + "\"");
+ }
+ }
+ tmp = strArray[1];
+ length = tmp.length();
+ for (int i = 0; i < length; i++) {
+ if (!isLetter(tmp.charAt(i))) {
+ throw new VCardException("Invalid Language: \"" + langval + "\"");
+ }
+ }
+ if (mBuilder != null) {
+ mBuilder.propertyParamType("LANGUAGE");
+ mBuilder.propertyParamValue(langval);
+ }
+ }
+
+ /**
+ * Mainly for "X-" type. This accepts any kind of type without check.
+ */
+ protected void handleAnyParam(String paramName, String paramValue) {
+ if (mBuilder != null) {
+ mBuilder.propertyParamType(paramName);
+ mBuilder.propertyParamValue(paramValue);
+ }
+ }
+
+ protected void handlePropertyValue(
+ String propertyName, String propertyValue) throws
+ IOException, VCardException {
+ if (mEncoding.equalsIgnoreCase("QUOTED-PRINTABLE")) {
+ long start = System.currentTimeMillis();
+ String result = getQuotedPrintable(propertyValue);
+ if (mBuilder != null) {
+ ArrayList<String> v = new ArrayList<String>();
+ v.add(result);
+ mBuilder.propertyValues(v);
+ }
+ mTimeHandlePropertyValue2 += System.currentTimeMillis() - start;
+ } else if (mEncoding.equalsIgnoreCase("BASE64") ||
+ mEncoding.equalsIgnoreCase("B")) {
+ long start = System.currentTimeMillis();
+ // It is very rare, but some BASE64 data may be so big that
+ // OutOfMemoryError occurs. To ignore such cases, use try-catch.
+ try {
+ String result = getBase64(propertyValue);
+ if (mBuilder != null) {
+ ArrayList<String> v = new ArrayList<String>();
+ v.add(result);
+ mBuilder.propertyValues(v);
+ }
+ } catch (OutOfMemoryError error) {
+ Log.e(LOG_TAG, "OutOfMemoryError happened during parsing BASE64 data!");
+ if (mBuilder != null) {
+ mBuilder.propertyValues(null);
+ }
+ }
+ mTimeHandlePropertyValue3 += System.currentTimeMillis() - start;
+ } else {
+ if (!(mEncoding == null || mEncoding.equalsIgnoreCase("7BIT")
+ || mEncoding.equalsIgnoreCase("8BIT")
+ || mEncoding.toUpperCase().startsWith("X-"))) {
+ Log.w(LOG_TAG, "The encoding unsupported by vCard spec: \"" + mEncoding + "\".");
+ }
+
+ long start = System.currentTimeMillis();
+ if (mBuilder != null) {
+ ArrayList<String> v = new ArrayList<String>();
+ v.add(maybeUnescapeText(propertyValue));
+ mBuilder.propertyValues(v);
+ }
+ mTimeHandlePropertyValue1 += System.currentTimeMillis() - start;
+ }
+ }
+
+ protected String getQuotedPrintable(String firstString) throws IOException, VCardException {
+ // Specifically, there may be some padding between = and CRLF.
+ // See the following:
+ //
+ // qp-line := *(qp-segment transport-padding CRLF)
+ // qp-part transport-padding
+ // qp-segment := qp-section *(SPACE / TAB) "="
+ // ; Maximum length of 76 characters
+ //
+ // e.g. (from RFC 2045)
+ // Now's the time =
+ // for all folk to come=
+ // to the aid of their country.
+ if (firstString.trim().endsWith("=")) {
+ // remove "transport-padding"
+ int pos = firstString.length() - 1;
+ while(firstString.charAt(pos) != '=') {
+ }
+ StringBuilder builder = new StringBuilder();
+ builder.append(firstString.substring(0, pos + 1));
+ builder.append("\r\n");
+ String line;
+ while (true) {
+ line = getLine();
+ if (line == null) {
+ throw new VCardException(
+ "File ended during parsing quoted-printable String");
+ }
+ if (line.trim().endsWith("=")) {
+ // remove "transport-padding"
+ pos = line.length() - 1;
+ while(line.charAt(pos) != '=') {
+ }
+ builder.append(line.substring(0, pos + 1));
+ builder.append("\r\n");
+ } else {
+ builder.append(line);
+ break;
+ }
+ }
+ return builder.toString();
+ } else {
+ return firstString;
+ }
+ }
+
+ protected String getBase64(String firstString) throws IOException, VCardException {
+ StringBuilder builder = new StringBuilder();
+ builder.append(firstString);
+
+ while (true) {
+ String line = getLine();
+ if (line == null) {
+ throw new VCardException(
+ "File ended during parsing BASE64 binary");
+ }
+ if (line.length() == 0) {
+ break;
+ }
+ builder.append(line);
+ }
+
+ return builder.toString();
+ }
+
+ /**
+ * Mainly for "ADR", "ORG", and "N"
+ * We do not care the number of strnosemi here.
+ *
+ * addressparts = 0*6(strnosemi ";") strnosemi
+ * ; PO Box, Extended Addr, Street, Locality, Region,
+ * Postal Code, Country Name
+ * orgparts = *(strnosemi ";") strnosemi
+ * ; First is Organization Name,
+ * remainder are Organization Units.
+ * nameparts = 0*4(strnosemi ";") strnosemi
+ * ; Family, Given, Middle, Prefix, Suffix.
+ * ; Example:Public;John;Q.;Reverend Dr.;III, Esq.
+ * strnosemi = *(*nonsemi ("\;" / "\" CRLF)) *nonsemi
+ * ; To include a semicolon in this string, it must be escaped
+ * ; with a "\" character.
+ *
+ * We are not sure whether we should add "\" CRLF to each value.
+ * For now, we exclude them.
+ */
+ protected void handleMultiplePropertyValue(
+ String propertyName, String propertyValue) throws IOException, VCardException {
+ // vCard 2.1 does not allow QUOTED-PRINTABLE here, but some data have it.
+ if (mEncoding.equalsIgnoreCase("QUOTED-PRINTABLE")) {
+ propertyValue = getQuotedPrintable(propertyValue);
+ }
+
+ if (mBuilder != null) {
+ // TODO: limit should be set in accordance with propertyName?
+ StringBuilder builder = new StringBuilder();
+ ArrayList<String> list = new ArrayList<String>();
+ int length = propertyValue.length();
+ for (int i = 0; i < length; i++) {
+ char ch = propertyValue.charAt(i);
+ if (ch == '\\' && i < length - 1) {
+ char nextCh = propertyValue.charAt(i + 1);
+ String unescapedString = maybeUnescape(nextCh);
+ if (unescapedString != null) {
+ builder.append(unescapedString);
+ i++;
+ } else {
+ builder.append(ch);
+ }
+ } else if (ch == ';') {
+ list.add(builder.toString());
+ builder = new StringBuilder();
+ } else {
+ builder.append(ch);
+ }
+ }
+ list.add(builder.toString());
+ mBuilder.propertyValues(list);
+ }
+ }
+
+ /**
+ * vCard 2.1 specifies AGENT allows one vcard entry. It is not encoded at all.
+ *
+ * item = ...
+ * / [groups "."] "AGENT"
+ * [params] ":" vcard CRLF
+ * vcard = "BEGIN" [ws] ":" [ws] "VCARD" [ws] 1*CRLF
+ * items *CRLF "END" [ws] ":" [ws] "VCARD"
+ *
+ */
+ protected void handleAgent(String propertyValue) throws VCardException {
+ throw new VCardNotSupportedException("AGENT Property is not supported now.");
+ /* This is insufficient support. Also, AGENT Property is very rare.
+ Ignore it for now.
+ TODO: fix this.
+
+ String[] strArray = propertyValue.split(":", 2);
+ if (!(strArray.length == 2 ||
+ strArray[0].trim().equalsIgnoreCase("BEGIN") &&
+ strArray[1].trim().equalsIgnoreCase("VCARD"))) {
+ throw new VCardException("BEGIN:VCARD != \"" + propertyValue + "\"");
+ }
+ parseItems();
+ readEndVCard();
+ */
+ }
+
+ /**
+ * For vCard 3.0.
+ */
+ protected String maybeUnescapeText(String text) {
+ return text;
+ }
+
+ /**
+ * Returns unescaped String if the character should be unescaped. Return null otherwise.
+ * e.g. In vCard 2.1, "\;" should be unescaped into ";" while "\x" should not be.
+ */
+ protected String maybeUnescape(char ch) {
+ // Original vCard 2.1 specification does not allow transformation
+ // "\:" -> ":", "\," -> ",", and "\\" -> "\", but previous implementation of
+ // this class allowed them, so keep it as is.
+ if (ch == '\\' || ch == ';' || ch == ':' || ch == ',') {
+ return String.valueOf(ch);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public boolean parse(InputStream is, VCardBuilder builder)
+ throws IOException, VCardException {
+ return parse(is, VCardConfig.DEFAULT_CHARSET, builder);
+ }
+
+ @Override
+ public boolean parse(InputStream is, String charset, VCardBuilder builder)
+ throws IOException, VCardException {
+ // TODO: make this count error entries instead of just throwing VCardException.
+
+ {
+ // TODO: If we really need to allow only CRLF as line break,
+ // we will have to develop our own BufferedReader().
+ final InputStreamReader tmpReader = new InputStreamReader(is, charset);
+ if (VCardConfig.showPerformanceLog()) {
+ mReader = new CustomBufferedReader(tmpReader);
+ } else {
+ mReader = new BufferedReader(tmpReader);
+ }
+ }
+
+ mBuilder = builder;
+
+ long start = System.currentTimeMillis();
+ if (mBuilder != null) {
+ mBuilder.start();
+ }
+ parseVCardFile();
+ if (mBuilder != null) {
+ mBuilder.end();
+ }
+ mTimeTotal += System.currentTimeMillis() - start;
+
+ if (VCardConfig.showPerformanceLog()) {
+ showPerformanceInfo();
+ }
+
+ return true;
+ }
+
+ @Override
+ public void parse(InputStream is, String charset, VCardBuilder builder, boolean canceled)
+ throws IOException, VCardException {
+ mCanceled = canceled;
+ parse(is, charset, builder);
+ }
+
+ private void showPerformanceInfo() {
+ Log.d(LOG_TAG, "total parsing time: " + mTimeTotal + " ms");
+ if (mReader instanceof CustomBufferedReader) {
+ Log.d(LOG_TAG, "total readLine time: " +
+ ((CustomBufferedReader)mReader).getTotalmillisecond() + " ms");
+ }
+ Log.d(LOG_TAG, "mTimeStartRecord: " + mTimeStartRecord + " ms");
+ Log.d(LOG_TAG, "mTimeEndRecord: " + mTimeEndRecord + " ms");
+ Log.d(LOG_TAG, "mTimeParseItem1: " + mTimeParseItem1 + " ms");
+ Log.d(LOG_TAG, "mTimeParseItem2: " + mTimeParseItem2 + " ms");
+ Log.d(LOG_TAG, "mTimeParseItem3: " + mTimeParseItem3 + " ms");
+ Log.d(LOG_TAG, "mTimeHandlePropertyValue1: " + mTimeHandlePropertyValue1 + " ms");
+ Log.d(LOG_TAG, "mTimeHandlePropertyValue2: " + mTimeHandlePropertyValue2 + " ms");
+ Log.d(LOG_TAG, "mTimeHandlePropertyValue3: " + mTimeHandlePropertyValue3 + " ms");
+ }
+
+ private boolean isLetter(char ch) {
+ if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
+ return true;
+ }
+ return false;
+ }
+}
+
+class CustomBufferedReader extends BufferedReader {
+ private long mTime;
+
+ public CustomBufferedReader(Reader in) {
+ super(in);
+ }
+
+ @Override
+ public String readLine() throws IOException {
+ long start = System.currentTimeMillis();
+ String ret = super.readLine();
+ long end = System.currentTimeMillis();
+ mTime += end - start;
+ return ret;
+ }
+
+ public long getTotalmillisecond() {
+ return mTime;
+ }
+}
diff --git a/core/java/android/pim/vcard/VCardParser_V30.java b/core/java/android/pim/vcard/VCardParser_V30.java
new file mode 100644
index 000000000000..634d9f5d20dd
--- /dev/null
+++ b/core/java/android/pim/vcard/VCardParser_V30.java
@@ -0,0 +1,306 @@
+/*
+ * 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.
+ */
+package android.pim.vcard;
+
+import android.pim.vcard.exception.VCardException;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashSet;
+
+/**
+ * This class is used to parse vcard3.0. <br>
+ * Please refer to vCard Specification 3.0 (http://tools.ietf.org/html/rfc2426)
+ */
+public class VCardParser_V30 extends VCardParser_V21 {
+ private static final String LOG_TAG = "VCardParser_V30";
+
+ private static final HashSet<String> sAcceptablePropsWithParam = new HashSet<String>(
+ Arrays.asList(
+ "BEGIN", "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND",
+ "VERSION", "TEL", "EMAIL", "TZ", "GEO", "NOTE", "URL",
+ "BDAY", "ROLE", "REV", "UID", "KEY", "MAILER", // 2.1
+ "NAME", "PROFILE", "SOURCE", "NICKNAME", "CLASS",
+ "SORT-STRING", "CATEGORIES", "PRODID")); // 3.0
+
+ // Although "7bit" and "BASE64" is not allowed in vCard 3.0, we allow it for safety.
+ private static final HashSet<String> sAcceptableEncodingV30 = new HashSet<String>(
+ Arrays.asList("7BIT", "8BIT", "BASE64", "B"));
+
+ // Although RFC 2426 specifies some property must not have parameters, we allow it,
+ // since there may be some careers which violates the RFC...
+ private static final HashSet<String> acceptablePropsWithoutParam = new HashSet<String>();
+
+ private String mPreviousLine;
+
+ @Override
+ protected String getVersion() {
+ return "3.0";
+ }
+
+ @Override
+ protected boolean isValidPropertyName(String propertyName) {
+ if (!(sAcceptablePropsWithParam.contains(propertyName) ||
+ acceptablePropsWithoutParam.contains(propertyName) ||
+ propertyName.startsWith("X-")) &&
+ !mWarningValueMap.contains(propertyName)) {
+ mWarningValueMap.add(propertyName);
+ Log.w(LOG_TAG, "Property name unsupported by vCard 3.0: " + propertyName);
+ }
+ return true;
+ }
+
+ @Override
+ protected boolean isValidEncoding(String encoding) {
+ return sAcceptableEncodingV30.contains(encoding.toUpperCase());
+ }
+
+ @Override
+ protected String getLine() throws IOException {
+ if (mPreviousLine != null) {
+ String ret = mPreviousLine;
+ mPreviousLine = null;
+ return ret;
+ } else {
+ return mReader.readLine();
+ }
+ }
+
+ /**
+ * vCard 3.0 requires that the line with space at the beginning of the line
+ * must be combined with previous line.
+ */
+ @Override
+ protected String getNonEmptyLine() throws IOException, VCardException {
+ String line;
+ StringBuilder builder = null;
+ while (true) {
+ line = mReader.readLine();
+ if (line == null) {
+ if (builder != null) {
+ return builder.toString();
+ } else if (mPreviousLine != null) {
+ String ret = mPreviousLine;
+ mPreviousLine = null;
+ return ret;
+ }
+ throw new VCardException("Reached end of buffer.");
+ } else if (line.length() == 0) {
+ if (builder != null) {
+ return builder.toString();
+ } else if (mPreviousLine != null) {
+ String ret = mPreviousLine;
+ mPreviousLine = null;
+ return ret;
+ }
+ } else if (line.charAt(0) == ' ' || line.charAt(0) == '\t') {
+ if (builder != null) {
+ // See Section 5.8.1 of RFC 2425 (MIME-DIR document).
+ // Following is the excerpts from it.
+ //
+ // DESCRIPTION:This is a long description that exists on a long line.
+ //
+ // Can be represented as:
+ //
+ // DESCRIPTION:This is a long description
+ // that exists on a long line.
+ //
+ // It could also be represented as:
+ //
+ // DESCRIPTION:This is a long descrip
+ // tion that exists o
+ // n a long line.
+ builder.append(line.substring(1));
+ } else if (mPreviousLine != null) {
+ builder = new StringBuilder();
+ builder.append(mPreviousLine);
+ mPreviousLine = null;
+ builder.append(line.substring(1));
+ } else {
+ throw new VCardException("Space exists at the beginning of the line");
+ }
+ } else {
+ if (mPreviousLine == null) {
+ mPreviousLine = line;
+ if (builder != null) {
+ return builder.toString();
+ }
+ } else {
+ String ret = mPreviousLine;
+ mPreviousLine = line;
+ return ret;
+ }
+ }
+ }
+ }
+
+
+ /**
+ * vcard = [group "."] "BEGIN" ":" "VCARD" 1*CRLF
+ * 1*(contentline)
+ * ;A vCard object MUST include the VERSION, FN and N types.
+ * [group "."] "END" ":" "VCARD" 1*CRLF
+ */
+ @Override
+ protected boolean readBeginVCard(boolean allowGarbage) throws IOException, VCardException {
+ // TODO: vCard 3.0 supports group.
+ return super.readBeginVCard(allowGarbage);
+ }
+
+ @Override
+ protected void readEndVCard(boolean useCache, boolean allowGarbage)
+ throws IOException, VCardException {
+ // TODO: vCard 3.0 supports group.
+ super.readEndVCard(useCache, allowGarbage);
+ }
+
+ /**
+ * vCard 3.0 allows iana-token as paramType, while vCard 2.1 does not.
+ */
+ @Override
+ protected void handleParams(String params) throws VCardException {
+ try {
+ super.handleParams(params);
+ } catch (VCardException e) {
+ // maybe IANA type
+ String[] strArray = params.split("=", 2);
+ if (strArray.length == 2) {
+ handleAnyParam(strArray[0], strArray[1]);
+ } else {
+ // Must not come here in the current implementation.
+ throw new VCardException(
+ "Unknown params value: " + params);
+ }
+ }
+ }
+
+ @Override
+ protected void handleAnyParam(String paramName, String paramValue) {
+ // vCard 3.0 accept comma-separated multiple values, but
+ // current PropertyNode does not accept it.
+ // For now, we do not split the values.
+ //
+ // TODO: fix this.
+ super.handleAnyParam(paramName, paramValue);
+ }
+
+ /**
+ * vCard 3.0 defines
+ *
+ * param = param-name "=" param-value *("," param-value)
+ * param-name = iana-token / x-name
+ * param-value = ptext / quoted-string
+ * quoted-string = DQUOTE QSAFE-CHAR DQUOTE
+ */
+ @Override
+ protected void handleType(String ptypevalues) {
+ String[] ptypeArray = ptypevalues.split(",");
+ mBuilder.propertyParamType("TYPE");
+ for (String value : ptypeArray) {
+ int length = value.length();
+ if (length >= 2 && value.startsWith("\"") && value.endsWith("\"")) {
+ mBuilder.propertyParamValue(value.substring(1, value.length() - 1));
+ } else {
+ mBuilder.propertyParamValue(value);
+ }
+ }
+ }
+
+ @Override
+ protected void handleAgent(String propertyValue) throws VCardException {
+ // The way how vCard 3.0 supports "AGENT" is completely different from vCard 2.0.
+ //
+ // e.g.
+ // AGENT:BEGIN:VCARD\nFN:Joe Friday\nTEL:+1-919-555-7878\n
+ // TITLE:Area Administrator\, Assistant\n EMAIL\;TYPE=INTERN\n
+ // ET:jfriday@host.com\nEND:VCARD\n
+ //
+ // TODO: fix this.
+ //
+ // issue:
+ // vCard 3.0 also allows this as an example.
+ //
+ // AGENT;VALUE=uri:
+ // CID:JQPUBLIC.part3.960129T083020.xyzMail@host3.com
+ //
+ // This is not VCARD. Should we support this?
+ throw new VCardException("AGENT in vCard 3.0 is not supported yet.");
+ }
+
+ /**
+ * vCard 3.0 does not require two CRLF at the last of BASE64 data.
+ * It only requires that data should be MIME-encoded.
+ */
+ @Override
+ protected String getBase64(String firstString) throws IOException, VCardException {
+ StringBuilder builder = new StringBuilder();
+ builder.append(firstString);
+
+ while (true) {
+ String line = getLine();
+ if (line == null) {
+ throw new VCardException(
+ "File ended during parsing BASE64 binary");
+ }
+ if (line.length() == 0) {
+ break;
+ } else if (!line.startsWith(" ") && !line.startsWith("\t")) {
+ mPreviousLine = line;
+ break;
+ }
+ builder.append(line);
+ }
+
+ return builder.toString();
+ }
+
+ /**
+ * ESCAPED-CHAR = "\\" / "\;" / "\," / "\n" / "\N")
+ * ; \\ encodes \, \n or \N encodes newline
+ * ; \; encodes ;, \, encodes ,
+ *
+ * Note: Apple escape ':' into '\:' while does not escape '\'
+ */
+ @Override
+ protected String maybeUnescapeText(String text) {
+ StringBuilder builder = new StringBuilder();
+ int length = text.length();
+ for (int i = 0; i < length; i++) {
+ char ch = text.charAt(i);
+ if (ch == '\\' && i < length - 1) {
+ char next_ch = text.charAt(++i);
+ if (next_ch == 'n' || next_ch == 'N') {
+ builder.append("\r\n");
+ } else {
+ builder.append(next_ch);
+ }
+ } else {
+ builder.append(ch);
+ }
+ }
+ return builder.toString();
+ }
+
+ @Override
+ protected String maybeUnescape(char ch) {
+ if (ch == 'n' || ch == 'N') {
+ return "\r\n";
+ } else {
+ return String.valueOf(ch);
+ }
+ }
+}
diff --git a/core/java/android/pim/vcard/VCardSourceDetector.java b/core/java/android/pim/vcard/VCardSourceDetector.java
new file mode 100644
index 000000000000..7e2be2b12eed
--- /dev/null
+++ b/core/java/android/pim/vcard/VCardSourceDetector.java
@@ -0,0 +1,137 @@
+/*
+ * 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.
+ */
+package android.pim.vcard;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Class which tries to detects the source of the vCard from its properties.
+ * Currently this implementation is very premature.
+ * @hide
+ */
+public class VCardSourceDetector implements VCardBuilder {
+ // Should only be used in package.
+ static final int TYPE_UNKNOWN = 0;
+ static final int TYPE_APPLE = 1;
+ static final int TYPE_JAPANESE_MOBILE_PHONE = 2; // Used in Japanese mobile phones.
+ static final int TYPE_FOMA = 3; // Used in some Japanese FOMA mobile phones.
+ static final int TYPE_WINDOWS_MOBILE_JP = 4;
+ // TODO: Excel, etc.
+
+ private static Set<String> APPLE_SIGNS = new HashSet<String>(Arrays.asList(
+ "X-PHONETIC-FIRST-NAME", "X-PHONETIC-MIDDLE-NAME", "X-PHONETIC-LAST-NAME",
+ "X-ABADR", "X-ABUID"));
+
+ private static Set<String> JAPANESE_MOBILE_PHONE_SIGNS = new HashSet<String>(Arrays.asList(
+ "X-GNO", "X-GN", "X-REDUCTION"));
+
+ private static Set<String> WINDOWS_MOBILE_PHONE_SIGNS = new HashSet<String>(Arrays.asList(
+ "X-MICROSOFT-ASST_TEL", "X-MICROSOFT-ASSISTANT", "X-MICROSOFT-OFFICELOC"));
+
+ // Note: these signes appears before the signs of the other type (e.g. "X-GN").
+ // In other words, Japanese FOMA mobile phones are detected as FOMA, not JAPANESE_MOBILE_PHONES.
+ private static Set<String> FOMA_SIGNS = new HashSet<String>(Arrays.asList(
+ "X-SD-VERN", "X-SD-FORMAT_VER", "X-SD-CATEGORIES", "X-SD-CLASS", "X-SD-DCREATED",
+ "X-SD-DESCRIPTION"));
+ private static String TYPE_FOMA_CHARSET_SIGN = "X-SD-CHAR_CODE";
+
+ private int mType = TYPE_UNKNOWN;
+ // Some mobile phones (like FOMA) tells us the charset of the data.
+ private boolean mNeedParseSpecifiedCharset;
+ private String mSpecifiedCharset;
+
+ public void start() {
+ }
+
+ public void end() {
+ }
+
+ public void startRecord(String type) {
+ }
+
+ public void startProperty() {
+ mNeedParseSpecifiedCharset = false;
+ }
+
+ public void endProperty() {
+ }
+
+ public void endRecord() {
+ }
+
+ public void propertyGroup(String group) {
+ }
+
+ public void propertyName(String name) {
+ if (name.equalsIgnoreCase(TYPE_FOMA_CHARSET_SIGN)) {
+ mType = TYPE_FOMA;
+ mNeedParseSpecifiedCharset = true;
+ return;
+ }
+ if (mType != TYPE_UNKNOWN) {
+ return;
+ }
+ if (WINDOWS_MOBILE_PHONE_SIGNS.contains(name)) {
+ mType = TYPE_WINDOWS_MOBILE_JP;
+ } else if (FOMA_SIGNS.contains(name)) {
+ mType = TYPE_FOMA;
+ } else if (JAPANESE_MOBILE_PHONE_SIGNS.contains(name)) {
+ mType = TYPE_JAPANESE_MOBILE_PHONE;
+ } else if (APPLE_SIGNS.contains(name)) {
+ mType = TYPE_APPLE;
+ }
+ }
+
+ public void propertyParamType(String type) {
+ }
+
+ public void propertyParamValue(String value) {
+ }
+
+ public void propertyValues(List<String> values) {
+ if (mNeedParseSpecifiedCharset && values.size() > 0) {
+ mSpecifiedCharset = values.get(0);
+ }
+ }
+
+ int getType() {
+ return mType;
+ }
+
+ /**
+ * Return charset String guessed from the source's properties.
+ * This method must be called after parsing target file(s).
+ * @return Charset String. Null is returned if guessing the source fails.
+ */
+ public String getEstimatedCharset() {
+ if (mSpecifiedCharset != null) {
+ return mSpecifiedCharset;
+ }
+ switch (mType) {
+ case TYPE_WINDOWS_MOBILE_JP:
+ case TYPE_FOMA:
+ case TYPE_JAPANESE_MOBILE_PHONE:
+ return "SHIFT_JIS";
+ case TYPE_APPLE:
+ return "UTF-8";
+ default:
+ return null;
+ }
+ }
+}
diff --git a/core/java/android/pim/vcard/exception/VCardException.java b/core/java/android/pim/vcard/exception/VCardException.java
new file mode 100644
index 000000000000..e557219f235a
--- /dev/null
+++ b/core/java/android/pim/vcard/exception/VCardException.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+package android.pim.vcard.exception;
+
+public class VCardException extends java.lang.Exception {
+ /**
+ * Constructs a VCardException object
+ */
+ public VCardException() {
+ super();
+ }
+
+ /**
+ * Constructs a VCardException object
+ *
+ * @param message the error message
+ */
+ public VCardException(String message) {
+ super(message);
+ }
+
+}
diff --git a/core/java/android/pim/vcard/exception/VCardNestedException.java b/core/java/android/pim/vcard/exception/VCardNestedException.java
new file mode 100644
index 000000000000..503c2fbcf536
--- /dev/null
+++ b/core/java/android/pim/vcard/exception/VCardNestedException.java
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+package android.pim.vcard.exception;
+
+/**
+ * VCardException thrown when VCard is nested without VCardParser's being notified.
+ */
+public class VCardNestedException extends VCardNotSupportedException {
+ public VCardNestedException() {
+ super();
+ }
+ public VCardNestedException(String message) {
+ super(message);
+ }
+}
diff --git a/core/java/android/pim/vcard/exception/VCardNotSupportedException.java b/core/java/android/pim/vcard/exception/VCardNotSupportedException.java
new file mode 100644
index 000000000000..616aa7763b0e
--- /dev/null
+++ b/core/java/android/pim/vcard/exception/VCardNotSupportedException.java
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+package android.pim.vcard.exception;
+
+/**
+ * The exception which tells that the input VCard is probably valid from the view of
+ * specification but not supported in the current framework for now.
+ *
+ * This is a kind of a good news from the view of development.
+ * It may be good to ask users to send a report with the VCard example
+ * for the future development.
+ */
+public class VCardNotSupportedException extends VCardException {
+ public VCardNotSupportedException() {
+ super();
+ }
+ public VCardNotSupportedException(String message) {
+ super(message);
+ }
+} \ No newline at end of file
diff --git a/core/java/android/pim/vcard/exception/VCardVersionException.java b/core/java/android/pim/vcard/exception/VCardVersionException.java
new file mode 100644
index 000000000000..9fe8b7f92af9
--- /dev/null
+++ b/core/java/android/pim/vcard/exception/VCardVersionException.java
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+package android.pim.vcard.exception;
+
+/**
+ * VCardException used only when the version of the vCard is different.
+ */
+public class VCardVersionException extends VCardException {
+ public VCardVersionException() {
+ super();
+ }
+ public VCardVersionException(String message) {
+ super(message);
+ }
+}
diff --git a/core/java/android/pim/vcard/exception/package.html b/core/java/android/pim/vcard/exception/package.html
new file mode 100644
index 000000000000..26b8a328b132
--- /dev/null
+++ b/core/java/android/pim/vcard/exception/package.html
@@ -0,0 +1,5 @@
+<HTML>
+<BODY>
+{@hide}
+</BODY>
+</HTML> \ No newline at end of file
diff --git a/core/java/android/pim/vcard/package.html b/core/java/android/pim/vcard/package.html
new file mode 100644
index 000000000000..26b8a328b132
--- /dev/null
+++ b/core/java/android/pim/vcard/package.html
@@ -0,0 +1,5 @@
+<HTML>
+<BODY>
+{@hide}
+</BODY>
+</HTML> \ No newline at end of file
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 718a120ded94..b779d59b533a 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -79,13 +79,7 @@ public final class ContactsContract {
}
}
- public interface AggregatesColumns {
- /**
- * The display name for the contact.
- * <P>Type: TEXT</P>
- */
- public static final String DISPLAY_NAME = "display_name";
-
+ public interface ContactOptionsColumns {
/**
* The number of times a person has been contacted
* <P>Type: INTEGER</P>
@@ -116,6 +110,14 @@ public final class ContactsContract {
* <P>Type: INTEGER (0 for false, 1 for true)</P>
*/
public static final String SEND_TO_VOICEMAIL = "send_to_voicemail";
+ }
+
+ public interface AggregatesColumns {
+ /**
+ * The display name for the contact.
+ * <P>Type: TEXT</P>
+ */
+ public static final String DISPLAY_NAME = "display_name";
/**
* Reference to the row in the data table holding the primary phone number.
@@ -146,7 +148,8 @@ public final class ContactsContract {
* Constants for the aggregates table, which contains a record per group
* of contact representing the same person.
*/
- public static final class Aggregates implements BaseColumns, AggregatesColumns {
+ public static final class Aggregates implements BaseColumns, AggregatesColumns,
+ ContactOptionsColumns {
/**
* This utility class cannot be instantiated
*/
@@ -187,6 +190,8 @@ public final class ContactsContract {
public static final Uri CONTENT_SUMMARY_STREQUENT_FILTER_URI = Uri.withAppendedPath(
CONTENT_SUMMARY_STREQUENT_URI, "filter");
+ public static final Uri CONTENT_SUMMARY_GROUP_URI = Uri.withAppendedPath(
+ CONTENT_SUMMARY_URI, "group");
/**
* The MIME type of {@link #CONTENT_URI} providing a directory of
* people.
@@ -244,7 +249,7 @@ public final class ContactsContract {
/**
* Constants for the contacts table, which contains the base contact information.
*/
- public static final class Contacts implements BaseColumns {
+ public static final class Contacts implements BaseColumns, ContactOptionsColumns {
/**
* This utility class cannot be instantiated
*/
@@ -322,6 +327,29 @@ public final class ContactsContract {
public static final String DIRTY = "dirty";
/**
+ * The aggregation mode for this contact.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String AGGREGATION_MODE = "aggregation_mode";
+
+ /**
+ * Aggregation mode: aggregate asynchronously.
+ */
+ public static final int AGGREGATION_MODE_DEFAULT = 0;
+
+ /**
+ * Aggregation mode: aggregate at the time the contact is inserted/updated.
+ */
+ public static final int AGGREGATION_MODE_IMMEDITATE = 1;
+
+ /**
+ * Aggregation mode: never aggregate this contact (note that the contact will not
+ * have a corresponding Aggregate and therefore will not be included in Aggregates
+ * query results.)
+ */
+ public static final int AGGREGATION_MODE_DISABLED = 2;
+
+ /**
* A sub-directory of a single contact that contains all of their {@link Data} rows.
* To access this directory append
*/
@@ -572,9 +600,20 @@ public final class ContactsContract {
}
/**
+ * The base types that all "Typed" data kinds support.
+ */
+ public interface BaseTypes {
+
+ /**
+ * A custom type. The custom label should be supplied by user.
+ */
+ public static int TYPE_CUSTOM = 0;
+ }
+
+ /**
* Columns common across the specific types.
*/
- private interface CommonColumns {
+ private interface CommonColumns extends BaseTypes{
/**
* The type of data, for example Home or Work.
* <P>Type: INTEGER</P>
@@ -595,17 +634,6 @@ public final class ContactsContract {
}
/**
- * The base types that all "Typed" data kinds support.
- */
- public interface BaseTypes {
-
- /**
- * A custom type. The custom label should be supplied by user.
- */
- public static int TYPE_CUSTOM = 0;
- }
-
- /**
* Parts of the name.
*/
public static final class StructuredName {
@@ -671,18 +699,12 @@ public final class ContactsContract {
/**
* A nickname.
*/
- public static final class Nickname implements BaseTypes {
+ public static final class Nickname implements CommonColumns {
private Nickname() {}
/** Mime-type used when storing this in data table. */
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/nickname";
- /**
- * The type of data, for example Home or Work.
- * <P>Type: INTEGER</P>
- */
- public static final String TYPE = "data1";
-
public static final int TYPE_DEFAULT = 1;
public static final int TYPE_OTHER_NAME = 2;
public static final int TYPE_MAINDEN_NAME = 3;
@@ -692,19 +714,13 @@ public final class ContactsContract {
/**
* The name itself
*/
- public static final String NAME = "data2";
-
- /**
- * The user provided label, only used if TYPE is {@link #TYPE_CUSTOM}.
- * <P>Type: TEXT</P>
- */
- public static final String LABEL = "data3";
+ public static final String NAME = DATA;
}
/**
* Common data definition for telephone numbers.
*/
- public static final class Phone implements BaseCommonColumns, CommonColumns, BaseTypes {
+ public static final class Phone implements BaseCommonColumns, CommonColumns {
private Phone() {}
/** Mime-type used when storing this in data table. */
@@ -745,13 +761,13 @@ public final class ContactsContract {
* The phone number as the user entered it.
* <P>Type: TEXT</P>
*/
- public static final String NUMBER = "data2";
+ public static final String NUMBER = DATA;
}
/**
* Common data definition for email addresses.
*/
- public static final class Email implements BaseCommonColumns, CommonColumns, BaseTypes {
+ public static final class Email implements BaseCommonColumns, CommonColumns {
private Email() {}
/** Mime-type used when storing this in data table. */
@@ -765,7 +781,7 @@ public final class ContactsContract {
/**
* Common data definition for postal addresses.
*/
- public static final class Postal implements BaseCommonColumns, CommonColumns, BaseTypes {
+ public static final class Postal implements BaseCommonColumns, CommonColumns {
private Postal() {}
/** Mime-type used when storing this in data table. */
@@ -793,7 +809,7 @@ public final class ContactsContract {
/**
* Common data definition for IM addresses.
*/
- public static final class Im implements BaseCommonColumns, CommonColumns, BaseTypes {
+ public static final class Im implements BaseCommonColumns, CommonColumns {
private Im() {}
/** Mime-type used when storing this in data table. */
@@ -853,33 +869,20 @@ public final class ContactsContract {
/**
* Common data definition for organizations.
*/
- public static final class Organization implements BaseCommonColumns, BaseTypes {
+ public static final class Organization implements BaseCommonColumns, CommonColumns {
private Organization() {}
/** Mime-type used when storing this in data table. */
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/organization";
- /**
- * The type of data, for example Home or Work.
- * <P>Type: INTEGER</P>
- */
- public static final String TYPE = "data1";
-
- public static final int TYPE_HOME = 1;
- public static final int TYPE_WORK = 2;
- public static final int TYPE_OTHER = 3;
-
- /**
- * The user provided label, only used if TYPE is {@link #TYPE_CUSTOM}.
- * <P>Type: TEXT</P>
- */
- public static final String LABEL = "data2";
+ public static final int TYPE_WORK = 1;
+ public static final int TYPE_OTHER = 2;
/**
* The company as the user entered it.
* <P>Type: TEXT</P>
*/
- public static final String COMPANY = "data3";
+ public static final String COMPANY = DATA;
/**
* The position title at this company as the user entered it.
@@ -933,20 +936,18 @@ public final class ContactsContract {
"vnd.android.cursor.item/group_membership";
/**
- * The row id of the group that this group membership refers to. Either this or the
- * GROUP_SOURCE_ID must be set. If they are both set then they must refer to the same
- * group.
+ * The row id of the group that this group membership refers to. Exactly one of
+ * this or {@link #GROUP_SOURCE_ID} must be set when inserting a row.
* <P>Type: INTEGER</P>
*/
public static final String GROUP_ROW_ID = "data1";
/**
- * The source id of the group that this membership refers to. Either this or the
- * GROUP_ROW_ID must be set. If they are both set then they must refer to the same
- * group.
+ * The sourceid of the group that this group membership refers to. Exactly one of
+ * this or {@link #GROUP_ROW_ID} must be set when inserting a row.
* <P>Type: STRING</P>
*/
- public static final String GROUP_SOURCE_ID = "data2";
+ public static final String GROUP_SOURCE_ID = "group_sourceid";
}
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 6f707cb84dc8..df3001d37698 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2714,6 +2714,12 @@ public final class Settings {
public static final String GMAIL_NUM_RETRY_UPHILL_OP = "gmail_discard_error_uphill_op";
/**
+ * Controls if the protocol buffer version of the protocol will use a multipart request for
+ * attachment uploads. Value must be an integer where non-zero means true. Defaults to 0.
+ */
+ public static final String GMAIL_USE_MULTIPART_PROTOBUF = "gmail_use_multipart_protobuf";
+
+ /**
* the transcoder URL for mobile devices.
*/
public static final String TRANSCODER_URL = "mobile_transcoder_url";
@@ -2814,7 +2820,7 @@ public final class Settings {
"gtalk_nosync_heartbeat_ping_interval_ms";
/**
- * The maximum heartbeat interval used while on the WIFI network.
+ * The maximum heartbeat interval used while on the WIFI network.
*/
public static final String GTALK_SERVICE_WIFI_MAX_HEARTBEAT_INTERVAL_MS =
"gtalk_wifi_max_heartbeat_ping_interval_ms";
@@ -2885,7 +2891,38 @@ public final class Settings {
*/
public static final String PUSH_MESSAGING_REGISTRATION_URL =
"push_messaging_registration_url";
-
+
+ /**
+ * This is gdata url to lookup album and picture info from picasa web.
+ */
+ public static final String GTALK_PICASA_ALBUM_URL =
+ "gtalk_picasa_album_url";
+
+ /**
+ * This is the url to lookup picture info from flickr.
+ */
+ public static final String GTALK_FLICKR_PHOTO_INFO_URL =
+ "gtalk_flickr_photo_info_url";
+
+ /**
+ * This is the url to lookup an actual picture from flickr.
+ */
+ public static final String GTALK_FLICKR_PHOTO_URL =
+ "gtalk_flickr_photo_url";
+
+ /**
+ * This is the gdata url to lookup info on a youtube video.
+ */
+ public static final String GTALK_YOUTUBE_VIDEO_URL =
+ "gtalk_youtube_video_url";
+
+
+ /**
+ * This is the url for getting the app token for server-to-device data messaging.
+ */
+ public static final String DATA_MESSAGE_GET_APP_TOKEN_URL =
+ "data_messaging_get_app_token_url";
+
/**
* Enable use of ssl session caching.
* 'db' - save each session in a (per process) database
diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java
index 1cf7be9c157c..722a7cc0b4a8 100644
--- a/core/java/android/server/BluetoothA2dpService.java
+++ b/core/java/android/server/BluetoothA2dpService.java
@@ -104,7 +104,8 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
break;
}
} else if (action.equals(BluetoothIntent.REMOTE_DEVICE_CONNECTED_ACTION)) {
- if (getSinkPriority(address) > BluetoothA2dp.PRIORITY_OFF) {
+ if (getSinkPriority(address) > BluetoothA2dp.PRIORITY_OFF &&
+ isSinkDevice(address)) {
// This device is a preferred sink. Make an A2DP connection
// after a delay. We delay to avoid connection collisions,
// and to give other profiles such as HFP a chance to
@@ -185,6 +186,18 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
return -1;
}
+ private boolean isSinkDevice(String address) {
+ String uuids[] = mBluetoothService.getRemoteUuids(address);
+ UUID uuid;
+ for (String deviceUuid: uuids) {
+ uuid = UUID.fromString(deviceUuid);
+ if (BluetoothUuid.isAudioSink(uuid)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private synchronized boolean addAudioSink (String address) {
String path = mBluetoothService.getObjectPathFromAddress(address);
String propValues[] = (String []) getSinkPropertiesNative(path);
diff --git a/core/java/android/server/BluetoothDeviceService.java b/core/java/android/server/BluetoothDeviceService.java
index 77b1b1d5e513..b780f419504e 100644
--- a/core/java/android/server/BluetoothDeviceService.java
+++ b/core/java/android/server/BluetoothDeviceService.java
@@ -285,6 +285,7 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
if (isEnabled()) {
SystemService.start("hsag");
SystemService.start("hfag");
+ SystemService.start("opush");
}
break;
case MESSAGE_FINISH_DISABLE:
@@ -958,7 +959,38 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
return setPinNative(address, pinString, data.intValue());
}
- public synchronized boolean cancelPin(String address) {
+ public synchronized boolean setPasskey(String address, int passkey) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH_ADMIN permission");
+ if (passkey < 0 || passkey > 999999 || !BluetoothDevice.checkBluetoothAddress(address)) {
+ return false;
+ }
+ address = address.toUpperCase();
+ Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
+ if (data == null) {
+ Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
+ "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
+ " or by bluez.\n");
+ return false;
+ }
+ return setPasskeyNative(address, passkey, data.intValue());
+ }
+
+ public synchronized boolean setPairingConfirmation(String address, boolean confirm) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH_ADMIN permission");
+ address = address.toUpperCase();
+ Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
+ if (data == null) {
+ Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
+ "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
+ " or by bluez.\n");
+ return false;
+ }
+ return setPairingConfirmationNative(address, confirm, data.intValue());
+ }
+
+ public synchronized boolean cancelPairingUserInput(String address) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
if (!BluetoothDevice.checkBluetoothAddress(address)) {
@@ -967,12 +999,12 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
address = address.toUpperCase();
Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
if (data == null) {
- Log.w(TAG, "cancelPin(" + address + ") called but no native data available, " +
- "ignoring. Maybe the PasskeyAgent Request was already cancelled by the remote " +
- "or by bluez.\n");
+ Log.w(TAG, "cancelUserInputNative(" + address + ") called but no native data " +
+ "available, ignoring. Maybe the PasskeyAgent Request was already cancelled " +
+ "by the remote or by bluez.\n");
return false;
}
- return cancelPinNative(address, data.intValue());
+ return cancelPairingUserInputNative(address, data.intValue());
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@@ -1159,7 +1191,10 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
private native int getDeviceServiceChannelNative(String objectPath, String uuid,
int attributeId);
- private native boolean cancelPinNative(String address, int nativeData);
+ private native boolean cancelPairingUserInputNative(String address, int nativeData);
private native boolean setPinNative(String address, String pin, int nativeData);
+ private native boolean setPasskeyNative(String address, int passkey, int nativeData);
+ private native boolean setPairingConfirmationNative(String address, boolean confirm,
+ int nativeData);
}
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index 38eb4d72a495..dc84d1f376c9 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -317,23 +317,53 @@ class BluetoothEventLoop {
}
mBluetoothService.setRemoteDeviceProperty(address, name, uuid);
}
-
}
- private void onRequestPinCode(String objectPath, int nativeData) {
+ private String checkPairingRequestAndGetAddress(String objectPath, int nativeData) {
String address = mBluetoothService.getAddressFromObjectPath(objectPath);
if (address == null) {
- Log.e(TAG, "Unable to get device address in onRequestPinCode, returning null");
- return;
+ Log.e(TAG, "Unable to get device address in checkPairingRequestAndGetAddress, " +
+ "returning null");
+ return null;
}
address = address.toUpperCase();
mPasskeyAgentRequestData.put(address, new Integer(nativeData));
if (mBluetoothService.getBluetoothState() == BluetoothDevice.BLUETOOTH_STATE_TURNING_OFF) {
// shutdown path
- mBluetoothService.cancelPin(address);
- return;
+ mBluetoothService.cancelPairingUserInput(address);
+ return null;
}
+ return address;
+ }
+
+ private void onRequestConfirmation(String objectPath, int passkey, int nativeData) {
+ String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
+ if (address == null) return;
+
+ Intent intent = new Intent(BluetoothIntent.PAIRING_REQUEST_ACTION);
+ intent.putExtra(BluetoothIntent.ADDRESS, address);
+ intent.putExtra(BluetoothIntent.PASSKEY, passkey);
+ intent.putExtra(BluetoothIntent.PAIRING_VARIANT,
+ BluetoothDevice.PAIRING_VARIANT_CONFIRMATION);
+ mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
+ return;
+ }
+
+ private void onRequestPasskey(String objectPath, int nativeData) {
+ String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
+ if (address == null) return;
+
+ Intent intent = new Intent(BluetoothIntent.PAIRING_REQUEST_ACTION);
+ intent.putExtra(BluetoothIntent.ADDRESS, address);
+ intent.putExtra(BluetoothIntent.PAIRING_VARIANT, BluetoothDevice.PAIRING_VARIANT_PASSKEY);
+ mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
+ return;
+ }
+
+ private void onRequestPinCode(String objectPath, int nativeData) {
+ String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
+ if (address == null) return;
if (mBluetoothService.getBondState().getBondState(address) ==
BluetoothDevice.BOND_BONDING) {
@@ -358,6 +388,7 @@ class BluetoothEventLoop {
}
Intent intent = new Intent(BluetoothIntent.PAIRING_REQUEST_ACTION);
intent.putExtra(BluetoothIntent.ADDRESS, address);
+ intent.putExtra(BluetoothIntent.PAIRING_VARIANT, BluetoothDevice.PAIRING_VARIANT_PIN);
mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
return;
}
@@ -371,9 +402,7 @@ class BluetoothEventLoop {
boolean authorized = false;
UUID uuid = UUID.fromString(deviceUuid);
- if (mBluetoothService.isEnabled() && (BluetoothUuid.isAudioSink(uuid) ||
- BluetoothUuid.isAudioSource(uuid) ||
- BluetoothUuid.isAdvAudioDist(uuid))) {
+ if (mBluetoothService.isEnabled() && BluetoothUuid.isAudioSink(uuid)) {
BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
authorized = a2dp.getSinkPriority(address) > BluetoothA2dp.PRIORITY_OFF;
if (authorized) {
@@ -388,9 +417,9 @@ class BluetoothEventLoop {
}
private void onAgentCancel() {
- // We immediately response to DBUS Authorize() so this should not
- // usually happen
- log("onAgentCancel");
+ Intent intent = new Intent(BluetoothIntent.PAIRING_CANCEL_ACTION);
+ mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
+ return;
}
private void onRestartRequired() {
diff --git a/core/java/android/server/search/SearchDialogWrapper.java b/core/java/android/server/search/SearchDialogWrapper.java
index 67be6a663514..d3ef5de8634f 100644
--- a/core/java/android/server/search/SearchDialogWrapper.java
+++ b/core/java/android/server/search/SearchDialogWrapper.java
@@ -45,11 +45,9 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
private static final String TAG = "SearchManagerService";
private static final boolean DBG = false;
- private static final String DISABLE_SEARCH_PROPERTY = "dev.disablesearchdialog";
-
private static final String SEARCH_UI_THREAD_NAME = "SearchDialog";
private static final int SEARCH_UI_THREAD_PRIORITY =
- android.os.Process.THREAD_PRIORITY_FOREGROUND;
+ android.os.Process.THREAD_PRIORITY_DEFAULT;
// Takes no arguments
private static final int MSG_INIT = 0;
@@ -88,12 +86,11 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
// Identity of currently resumed activity.
private int mResumedIdent = 0;
-
- // Allows disabling of search dialog for stress testing runs
- private final boolean mDisabledOnBoot;
// True if we have registered our receivers.
private boolean mReceiverRegistered;
+
+ private volatile boolean mVisible = false;
/**
* Creates a new search dialog wrapper and a search UI thread. The search dialog itself will
@@ -104,8 +101,6 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
public SearchDialogWrapper(Context context) {
mContext = context;
- mDisabledOnBoot = !TextUtils.isEmpty(SystemProperties.get(DISABLE_SEARCH_PROPERTY));
-
// Create the search UI thread
HandlerThread t = new HandlerThread(SEARCH_UI_THREAD_NAME, SEARCH_UI_THREAD_PRIORITY);
t.start();
@@ -115,6 +110,10 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
mSearchUiThread.sendEmptyMessage(MSG_INIT);
}
+ public boolean isVisible() {
+ return mVisible;
+ }
+
/**
* Initializes the search UI.
* Must be called from the search UI thread.
@@ -151,8 +150,10 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
- if (DBG) debug(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
- performStopSearch();
+ if (!"search".equals(intent.getStringExtra("reason"))) {
+ if (DBG) debug(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+ performStopSearch();
+ }
} else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
if (DBG) debug(Intent.ACTION_CONFIGURATION_CHANGED);
performOnConfigurationChanged();
@@ -205,7 +206,7 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
* Can be called from any thread.
*/
public void activityResuming(int ident) {
- if (DBG) debug("startSearch()");
+ if (DBG) debug("activityResuming(ident=" + ident + ")");
Message msg = Message.obtain();
msg.what = MSG_ACTIVITY_RESUMING;
msg.arg1 = ident;
@@ -256,20 +257,6 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
}
- void updateDialogVisibility() {
- if (mStartedIdent != 0) {
- // mResumedIdent == 0 means we have just booted and the user
- // hasn't yet gone anywhere.
- if (mResumedIdent == 0 || mStartedIdent == mResumedIdent) {
- if (DBG) Log.v(TAG, "******************* DIALOG: show");
- mSearchDialog.show();
- } else {
- if (DBG) Log.v(TAG, "******************* DIALOG: hide");
- mSearchDialog.hide();
- }
- }
- }
-
/**
* Actually launches the search UI.
* This must be called on the search UI thread.
@@ -283,19 +270,20 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
int ident) {
if (DBG) debug("performStartSearch()");
- if (mDisabledOnBoot) {
- Log.d(TAG, "ignoring start search request because " + DISABLE_SEARCH_PROPERTY
- + " system property is set.");
- return;
- }
-
registerBroadcastReceiver();
mCallback = searchManagerCallback;
+
+ // clean up any hidden dialog that we were waiting to resume
+ if (mStartedIdent != 0) {
+ mSearchDialog.dismiss();
+ }
+
mStartedIdent = ident;
if (DBG) Log.v(TAG, "******************* DIALOG: start");
+
mSearchDialog.show(initialQuery, selectInitialQuery, launchActivity, appSearchData,
globalSearch);
- updateDialogVisibility();
+ mVisible = true;
}
/**
@@ -306,6 +294,7 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
if (DBG) debug("performStopSearch()");
if (DBG) Log.v(TAG, "******************* DIALOG: cancel");
mSearchDialog.cancel();
+ mVisible = false;
mStartedIdent = 0;
}
@@ -317,7 +306,21 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
if (DBG) debug("performResumingActivity(): mStartedIdent="
+ mStartedIdent + ", resuming: " + ident);
this.mResumedIdent = ident;
- updateDialogVisibility();
+ if (mStartedIdent != 0) {
+ if (mStartedIdent == mResumedIdent) {
+ // we are resuming into the activity where we previously hid the dialog, bring it
+ // back
+ if (DBG) Log.v(TAG, "******************* DIALOG: show");
+ mSearchDialog.show();
+ mVisible = true;
+ } else {
+ // resuming into some other activity; hide ourselves in case we ever come back
+ // so we can show ourselves quickly again
+ if (DBG) Log.v(TAG, "******************* DIALOG: hide");
+ mSearchDialog.hide();
+ mVisible = false;
+ }
+ }
}
/**
@@ -333,27 +336,38 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
*/
public void onDismiss(DialogInterface dialog) {
if (DBG) debug("onDismiss()");
- if (mCallback != null) {
- try {
- // should be safe to do on the search UI thread, since it's a oneway interface
- mCallback.onDismiss();
- } catch (DeadObjectException ex) {
- // The process that hosted the callback has died, do nothing
- } catch (RemoteException ex) {
- Log.e(TAG, "onDismiss() failed: " + ex);
- }
- // we don't need the callback anymore, release it
- mCallback = null;
- }
+ mStartedIdent = 0;
+ mVisible = false;
+ callOnDismiss();
+
+ // we don't need the callback anymore, release it
+ mCallback = null;
unregisterBroadcastReceiver();
}
+
/**
* Called by {@link SearchDialog} when the user or activity cancels search.
* Whenever this method is called, {@link #onDismiss} is always called afterwards.
*/
public void onCancel(DialogInterface dialog) {
if (DBG) debug("onCancel()");
+ callOnCancel();
+ }
+
+ private void callOnDismiss() {
+ if (mCallback == null) return;
+ try {
+ // should be safe to do on the search UI thread, since it's a oneway interface
+ mCallback.onDismiss();
+ } catch (DeadObjectException ex) {
+ // The process that hosted the callback has died, do nothing
+ } catch (RemoteException ex) {
+ Log.e(TAG, "onDismiss() failed: " + ex);
+ }
+ }
+
+ private void callOnCancel() {
if (mCallback != null) {
try {
// should be safe to do on the search UI thread, since it's a oneway interface
diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java
index 762991260dad..fdeb8f9e3fc2 100644
--- a/core/java/android/server/search/SearchManagerService.java
+++ b/core/java/android/server/search/SearchManagerService.java
@@ -238,4 +238,8 @@ public class SearchManagerService extends ISearchManager.Stub {
getSearchDialog().stopSearch();
}
+ public boolean isVisible() {
+ return mSearchDialog != null && mSearchDialog.isVisible();
+ }
+
}
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index a2e70b8193f7..bb6b4b0385d8 100755
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -113,43 +113,131 @@ public class TextToSpeech {
/**
* Internal constants for the TTS functionality
*
- * {@hide}
*/
public class Engine {
// default values for a TTS engine when settings are not found in the provider
+ /**
+ * {@hide}
+ */
public static final int FALLBACK_TTS_DEFAULT_RATE = 100; // 1x
+ /**
+ * {@hide}
+ */
public static final int FALLBACK_TTS_DEFAULT_PITCH = 100;// 1x
+ /**
+ * {@hide}
+ */
public static final int FALLBACK_TTS_USE_DEFAULTS = 0; // false
+ /**
+ * {@hide}
+ */
public static final String FALLBACK_TTS_DEFAULT_SYNTH = "com.svox.pico";
// default values for rendering
public static final int TTS_DEFAULT_STREAM = AudioManager.STREAM_MUSIC;
// return codes for a TTS engine's check data activity
+ /**
+ * Indicates success when checking the installation status of the resources used by the
+ * text-to-speech engine with the android.intent.action.CHECK_TTS_DATA intent.
+ */
public static final int CHECK_VOICE_DATA_PASS = 1;
+ /**
+ * Indicates failure when checking the installation status of the resources used by the
+ * text-to-speech engine with the android.intent.action.CHECK_TTS_DATA intent.
+ */
public static final int CHECK_VOICE_DATA_FAIL = 0;
+ /**
+ * Indicates erroneous data when checking the installation status of the resources used by
+ * the text-to-speech engine with the android.intent.action.CHECK_TTS_DATA intent.
+ */
public static final int CHECK_VOICE_DATA_BAD_DATA = -1;
+ /**
+ * Indicates missing resources when checking the installation status of the resources used
+ * by the text-to-speech engine with the android.intent.action.CHECK_TTS_DATA intent.
+ */
public static final int CHECK_VOICE_DATA_MISSING_DATA = -2;
- public static final int CHECK_VOICE_DATA_MISSING_DATA_NO_SDCARD = -3;
+ /**
+ * Indicates missing storage volume when checking the installation status of the resources
+ * used by the text-to-speech engine with the android.intent.action.CHECK_TTS_DATA intent.
+ */
+ public static final int CHECK_VOICE_DATA_MISSING_VOLUME = -3;
// return codes for a TTS engine's check data activity
+ /**
+ * Extra information received with the android.intent.action.CHECK_TTS_DATA intent where
+ * the text-to-speech engine specifies the path to its resources.
+ */
public static final String VOICE_DATA_ROOT_DIRECTORY = "dataRoot";
+ /**
+ * Extra information received with the android.intent.action.CHECK_TTS_DATA intent where
+ * the text-to-speech engine specifies the file names of its resources under the
+ * resource path.
+ */
public static final String VOICE_DATA_FILES = "dataFiles";
+ /**
+ * Extra information received with the android.intent.action.CHECK_TTS_DATA intent where
+ * the text-to-speech engine specifies the locale associated with each resource file.
+ */
public static final String VOICE_DATA_FILES_INFO = "dataFilesInfo";
- // keys for the parameters passed with speak commands
+ // keys for the parameters passed with speak commands. Hidden keys are used internally
+ // to maintain engine state for each TextToSpeech instance.
+ /**
+ * {@hide}
+ */
public static final String TTS_KEY_PARAM_RATE = "rate";
+ /**
+ * {@hide}
+ */
public static final String TTS_KEY_PARAM_LANGUAGE = "language";
+ /**
+ * {@hide}
+ */
public static final String TTS_KEY_PARAM_COUNTRY = "country";
+ /**
+ * {@hide}
+ */
public static final String TTS_KEY_PARAM_VARIANT = "variant";
+ /**
+ * Parameter key to specify the audio stream type to be used when speaking text
+ * or playing back a file.
+ */
public static final String TTS_KEY_PARAM_STREAM = "streamType";
+ /**
+ * Parameter key to identify an utterance in the completion listener after text has been
+ * spoken, a file has been played back or a silence duration has elapsed.
+ */
public static final String TTS_KEY_PARAM_UTTERANCE_ID = "utteranceId";
+
+ // key positions in the array of cached parameters
+ /**
+ * {@hide}
+ */
protected static final int TTS_PARAM_POSITION_RATE = 0;
+ /**
+ * {@hide}
+ */
protected static final int TTS_PARAM_POSITION_LANGUAGE = 2;
+ /**
+ * {@hide}
+ */
protected static final int TTS_PARAM_POSITION_COUNTRY = 4;
+ /**
+ * {@hide}
+ */
protected static final int TTS_PARAM_POSITION_VARIANT = 6;
+ /**
+ * {@hide}
+ */
protected static final int TTS_PARAM_POSITION_STREAM = 8;
+ /**
+ * {@hide}
+ */
protected static final int TTS_PARAM_POSITION_UTTERANCE_ID = 10;
+ /**
+ * {@hide}
+ */
protected static final int TTS_NB_CACHED_PARAMS = 6;
}
@@ -362,6 +450,109 @@ public class TextToSpeech {
/**
+ * Adds a mapping between a string of text and a sound resource in a
+ * package.
+ *
+ * @see #TTS.playEarcon(String earcon, int queueMode, String[] params)
+ *
+ * @param earcon The name of the earcon
+ * Example: <b><code>"[tick]"</code></b><br/>
+ *
+ * @param packagename
+ * Pass the packagename of the application that contains the
+ * resource. If the resource is in your own application (this is
+ * the most common case), then put the packagename of your
+ * application here.<br/>
+ * Example: <b>"com.google.marvin.compass"</b><br/>
+ * The packagename can be found in the AndroidManifest.xml of
+ * your application.
+ * <p>
+ * <code>&lt;manifest xmlns:android=&quot;...&quot;
+ * package=&quot;<b>com.google.marvin.compass</b>&quot;&gt;</code>
+ * </p>
+ *
+ * @param resourceId
+ * Example: <b><code>R.raw.tick_snd</code></b>
+ *
+ * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
+ */
+ public int addEarcon(String earcon, String packagename, int resourceId) {
+ synchronized(mStartLock) {
+ if (!mStarted) {
+ return TTS_ERROR;
+ }
+ try {
+ mITts.addEarcon(mPackageName, earcon, packagename, resourceId);
+ return TTS_SUCCESS;
+ } catch (RemoteException e) {
+ // TTS died; restart it.
+ Log.e("TextToSpeech.java - addEarcon", "RemoteException");
+ e.printStackTrace();
+ mStarted = false;
+ initTts();
+ } catch (NullPointerException e) {
+ // TTS died; restart it.
+ Log.e("TextToSpeech.java - addEarcon", "NullPointerException");
+ e.printStackTrace();
+ mStarted = false;
+ initTts();
+ } catch (IllegalStateException e) {
+ // TTS died; restart it.
+ Log.e("TextToSpeech.java - addEarcon", "IllegalStateException");
+ e.printStackTrace();
+ mStarted = false;
+ initTts();
+ }
+ return TTS_ERROR;
+ }
+ }
+
+
+ /**
+ * Adds a mapping between a string of text and a sound file. Using this, it
+ * is possible to add custom earcons.
+ *
+ * @param earcon
+ * The name of the earcon
+ * @param filename
+ * The full path to the sound file (for example:
+ * "/sdcard/mysounds/tick.wav")
+ *
+ * @return Code indicating success or failure. See TTS_ERROR and TTS_SUCCESS.
+ */
+ public int addEarcon(String earcon, String filename) {
+ synchronized (mStartLock) {
+ if (!mStarted) {
+ return TTS_ERROR;
+ }
+ try {
+ mITts.addEarconFile(mPackageName, earcon, filename);
+ return TTS_SUCCESS;
+ } catch (RemoteException e) {
+ // TTS died; restart it.
+ Log.e("TextToSpeech.java - addEarcon", "RemoteException");
+ e.printStackTrace();
+ mStarted = false;
+ initTts();
+ } catch (NullPointerException e) {
+ // TTS died; restart it.
+ Log.e("TextToSpeech.java - addEarcon", "NullPointerException");
+ e.printStackTrace();
+ mStarted = false;
+ initTts();
+ } catch (IllegalStateException e) {
+ // TTS died; restart it.
+ Log.e("TextToSpeech.java - addEarcon", "IllegalStateException");
+ e.printStackTrace();
+ mStarted = false;
+ initTts();
+ }
+ return TTS_ERROR;
+ }
+ }
+
+
+ /**
* Speaks the string using the specified queuing strategy and speech
* parameters. Note that the speech parameters are not universally supported
* by all engines and will be treated as a hint. The TTS library will try to
@@ -629,14 +820,14 @@ public class TextToSpeech {
if (speechRate > 0) {
int rate = (int)(speechRate*100);
mCachedParams[Engine.TTS_PARAM_POSITION_RATE + 1] = String.valueOf(rate);
- result = mITts.setSpeechRate(mPackageName, rate);
+ // the rate is not set here, instead it is cached so it will be associated
+ // with all upcoming utterances.
+ if (speechRate > 0.0f) {
+ result = TTS_SUCCESS;
+ } else {
+ result = TTS_ERROR;
+ }
}
- } catch (RemoteException e) {
- // TTS died; restart it.
- Log.e("TextToSpeech.java - setSpeechRate", "RemoteException");
- e.printStackTrace();
- mStarted = false;
- initTts();
} catch (NullPointerException e) {
// TTS died; restart it.
Log.e("TextToSpeech.java - setSpeechRate", "NullPointerException");
@@ -716,7 +907,9 @@ public class TextToSpeech {
* @param loc
* The locale describing the language to be used.
*
- * @return Code indicating the support status for the locale. See the TTS_LANG_ codes.
+ * @return code indicating the support status for the locale. See {@link #TTS_LANG_AVAILABLE},
+ * {@link #TTS_LANG_COUNTRY_AVAILABLE}, {@link #TTS_LANG_COUNTRY_VAR_AVAILABLE},
+ * {@link #TTS_LANG_MISSING_DATA} and {@link #TTS_LANG_NOT_SUPPORTED}.
*/
public int setLanguage(Locale loc) {
synchronized (mStartLock) {
@@ -728,6 +921,7 @@ public class TextToSpeech {
mCachedParams[Engine.TTS_PARAM_POSITION_LANGUAGE + 1] = loc.getISO3Language();
mCachedParams[Engine.TTS_PARAM_POSITION_COUNTRY + 1] = loc.getISO3Country();
mCachedParams[Engine.TTS_PARAM_POSITION_VARIANT + 1] = loc.getVariant();
+
result = mITts.setLanguage(mPackageName,
mCachedParams[Engine.TTS_PARAM_POSITION_LANGUAGE + 1],
mCachedParams[Engine.TTS_PARAM_POSITION_COUNTRY + 1],
@@ -803,8 +997,9 @@ public class TextToSpeech {
* @param loc
* The Locale describing the language to be used.
*
- * @return one of TTS_LANG_NOT_SUPPORTED, TTS_LANG_MISSING_DATA, TTS_LANG_AVAILABLE,
- * TTS_LANG_COUNTRY_AVAILABLE, TTS_LANG_COUNTRY_VAR_AVAILABLE.
+ * @return code indicating the support status for the locale. See {@link #TTS_LANG_AVAILABLE},
+ * {@link #TTS_LANG_COUNTRY_AVAILABLE}, {@link #TTS_LANG_COUNTRY_VAR_AVAILABLE},
+ * {@link #TTS_LANG_MISSING_DATA} and {@link #TTS_LANG_NOT_SUPPORTED}.
*/
public int isLanguageAvailable(Locale loc) {
synchronized (mStartLock) {
diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java
index 70e12970e6c2..380e5fd8d197 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -25,6 +25,7 @@ import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
@@ -40,6 +41,7 @@ import android.text.style.StrikethroughSpan;
import android.text.style.StyleSpan;
import android.text.style.SubscriptSpan;
import android.text.style.SuperscriptSpan;
+import android.text.style.TextAppearanceSpan;
import android.text.style.TypefaceSpan;
import android.text.style.URLSpan;
import android.text.style.UnderlineSpan;
@@ -49,6 +51,7 @@ import com.android.internal.util.XmlUtils;
import java.io.IOException;
import java.io.StringReader;
import java.nio.CharBuffer;
+import java.util.HashMap;
/**
* This class processes HTML strings into displayable styled text.
@@ -633,54 +636,25 @@ class HtmlToSpannedConverter implements ContentHandler {
if (where != len) {
Font f = (Font) obj;
- if (f.mColor != null) {
- int c = -1;
-
- if (f.mColor.equalsIgnoreCase("aqua")) {
- c = 0x00FFFF;
- } else if (f.mColor.equalsIgnoreCase("black")) {
- c = 0x000000;
- } else if (f.mColor.equalsIgnoreCase("blue")) {
- c = 0x0000FF;
- } else if (f.mColor.equalsIgnoreCase("fuchsia")) {
- c = 0xFF00FF;
- } else if (f.mColor.equalsIgnoreCase("green")) {
- c = 0x008000;
- } else if (f.mColor.equalsIgnoreCase("grey")) {
- c = 0x808080;
- } else if (f.mColor.equalsIgnoreCase("lime")) {
- c = 0x00FF00;
- } else if (f.mColor.equalsIgnoreCase("maroon")) {
- c = 0x800000;
- } else if (f.mColor.equalsIgnoreCase("navy")) {
- c = 0x000080;
- } else if (f.mColor.equalsIgnoreCase("olive")) {
- c = 0x808000;
- } else if (f.mColor.equalsIgnoreCase("purple")) {
- c = 0x800080;
- } else if (f.mColor.equalsIgnoreCase("red")) {
- c = 0xFF0000;
- } else if (f.mColor.equalsIgnoreCase("silver")) {
- c = 0xC0C0C0;
- } else if (f.mColor.equalsIgnoreCase("teal")) {
- c = 0x008080;
- } else if (f.mColor.equalsIgnoreCase("white")) {
- c = 0xFFFFFF;
- } else if (f.mColor.equalsIgnoreCase("yellow")) {
- c = 0xFFFF00;
+ if (!TextUtils.isEmpty(f.mColor)) {
+ if (f.mColor.startsWith("@")) {
+ Resources res = Resources.getSystem();
+ String name = f.mColor.substring(1);
+ int colorRes = res.getIdentifier(name, "color", "android");
+ if (colorRes != 0) {
+ ColorStateList colors = res.getColorStateList(colorRes);
+ text.setSpan(new TextAppearanceSpan(null, 0, 0, colors, null),
+ where, len,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
} else {
- try {
- c = XmlUtils.convertValueToInt(f.mColor, -1);
- } catch (NumberFormatException nfe) {
- // Can't understand the color, so just drop it.
+ int c = getHtmlColor(f.mColor);
+ if (c != -1) {
+ text.setSpan(new ForegroundColorSpan(c | 0xFF000000),
+ where, len,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
-
- if (c != -1) {
- text.setSpan(new ForegroundColorSpan(c | 0xFF000000),
- where, len,
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
}
if (f.mFace != null) {
@@ -843,4 +817,47 @@ class HtmlToSpannedConverter implements ContentHandler {
mLevel = level;
}
}
+
+ private static HashMap<String,Integer> COLORS = buildColorMap();
+
+ private static HashMap<String,Integer> buildColorMap() {
+ HashMap<String,Integer> map = new HashMap<String,Integer>();
+ map.put("aqua", 0x00FFFF);
+ map.put("black", 0x000000);
+ map.put("blue", 0x0000FF);
+ map.put("fuchsia", 0xFF00FF);
+ map.put("green", 0x008000);
+ map.put("grey", 0x808080);
+ map.put("lime", 0x00FF00);
+ map.put("maroon", 0x800000);
+ map.put("navy", 0x000080);
+ map.put("olive", 0x808000);
+ map.put("purple", 0x800080);
+ map.put("red", 0xFF0000);
+ map.put("silver", 0xC0C0C0);
+ map.put("teal", 0x008080);
+ map.put("white", 0xFFFFFF);
+ map.put("yellow", 0xFFFF00);
+ return map;
+ }
+
+ /**
+ * Converts an HTML color (named or numeric) to an integer RGB value.
+ *
+ * @param color Non-null color string.
+ * @return A color value, or {@code -1} if the color string could not be interpreted.
+ */
+ private static int getHtmlColor(String color) {
+ Integer i = COLORS.get(color.toLowerCase());
+ if (i != null) {
+ return i;
+ } else {
+ try {
+ return XmlUtils.convertValueToInt(color, -1);
+ } catch (NumberFormatException nfe) {
+ return -1;
+ }
+ }
+ }
+
}
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index 9071bf029f9e..bfab49d60fa1 100644
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -27,17 +27,31 @@ import android.os.*;
*/
public class DisplayMetrics {
/**
+ * Standard quantized DPI for low-density screens.
+ */
+ public static final int DENSITY_LOW = 120;
+
+ /**
+ * Standard quantized DPI for medium-density screens.
+ */
+ public static final int DENSITY_MEDIUM = 160;
+
+ /**
+ * Standard quantized DPI for high-density screens.
+ */
+ public static final int DENSITY_HIGH = 240;
+
+ /**
* The reference density used throughout the system.
- *
- * @hide Pending API council approval
*/
- public static final int DEFAULT_DENSITY = 160;
+ public static final int DENSITY_DEFAULT = DENSITY_MEDIUM;
/**
* The device's density.
- * @hide
+ * @hide becase eventually this should be able to change while
+ * running, so shouldn't be a constant.
*/
- public static final int DEVICE_DENSITY = getDeviceDensity();
+ public static final int DENSITY_DEVICE = getDeviceDensity();
/**
* The absolute width of the display in pixels.
@@ -62,7 +76,7 @@ public class DisplayMetrics {
* 320x480 but the screen size remained 1.5"x2" then the density would be
* increased (probably to 1.5).
*
- * @see #DEFAULT_DENSITY
+ * @see #DENSITY_DEFAULT
*/
public float density;
/**
@@ -95,10 +109,10 @@ public class DisplayMetrics {
public void setToDefaults() {
widthPixels = 0;
heightPixels = 0;
- density = DEVICE_DENSITY / (float) DEFAULT_DENSITY;
+ density = DENSITY_DEVICE / (float) DENSITY_DEFAULT;
scaledDensity = density;
- xdpi = DEVICE_DENSITY;
- ydpi = DEVICE_DENSITY;
+ xdpi = DENSITY_DEVICE;
+ ydpi = DENSITY_DEVICE;
}
/**
@@ -176,6 +190,6 @@ public class DisplayMetrics {
// The reason for this is that ro.sf.lcd_density is write-once and is
// set by the init process when it parses build.prop before anything else.
return SystemProperties.getInt("qemu.sf.lcd_density",
- SystemProperties.getInt("ro.sf.lcd_density", DEFAULT_DENSITY));
+ SystemProperties.getInt("ro.sf.lcd_density", DENSITY_DEFAULT));
}
}
diff --git a/core/java/android/util/TypedValue.java b/core/java/android/util/TypedValue.java
index d4ba9e21ac13..ed4529851d8f 100644
--- a/core/java/android/util/TypedValue.java
+++ b/core/java/android/util/TypedValue.java
@@ -140,12 +140,16 @@ public class TypedValue {
/**
* If {@link #density} is equal to this value, then the density should be
- * treated as the system's default density value: {@link DisplayMetrics#DEFAULT_DENSITY}.
- *
- * @hide Pending API council approval
+ * treated as the system's default density value: {@link DisplayMetrics#DENSITY_DEFAULT}.
*/
public static final int DENSITY_DEFAULT = 0;
+ /**
+ * If {@link #density} is equal to this value, then there is no density
+ * associated with the resource and it should not be scaled.
+ */
+ public static final int DENSITY_NONE = 0xffff;
+
/* ------------------------------------------------------------ */
/** The type held by this value, as defined by the constants here.
@@ -171,8 +175,6 @@ public class TypedValue {
/**
* If the Value came from a resource, this holds the corresponding pixel density.
- *
- * @hide Pending API council approval
* */
public int density;
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 09ebeed543e1..5551f64bcde3 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -117,5 +117,32 @@ public class Display
private static final Object mStaticInit = new Object();
private static boolean mInitialized = false;
+
+ /**
+ * Returns a display object which uses the metric's width/height instead.
+ * @hide
+ */
+ public static Display createMetricsBasedDisplay(int displayId, DisplayMetrics metrics) {
+ return new CompatibleDisplay(displayId, metrics);
+ }
+
+ private static class CompatibleDisplay extends Display {
+ private final DisplayMetrics mMetrics;
+
+ private CompatibleDisplay(int displayId, DisplayMetrics metrics) {
+ super(displayId);
+ mMetrics = metrics;
+ }
+
+ @Override
+ public int getWidth() {
+ return mMetrics.widthPixels;
+ }
+
+ @Override
+ public int getHeight() {
+ return mMetrics.heightPixels;
+ }
+ }
}
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 0e37b2665b9b..c41ee324945e 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -396,7 +396,6 @@ public final class MotionEvent implements Parcelable {
}
/**
-<<<<<<< HEAD:core/java/android/view/MotionEvent.java
* Returns the time (in ns) when this specific event was generated.
* The value is in nanosecond precision but it may not have nanosecond accuracy.
*
@@ -409,13 +408,6 @@ public final class MotionEvent implements Parcelable {
/**
* Returns the X coordinate of this event. Whole numbers are pixels; the
* value may have a fraction for input devices that are sub-pixel precise.
-|||||||
- * Returns the X coordinate of this event. Whole numbers are pixels; the
- * value may have a fraction for input devices that are sub-pixel precise.
-=======
- * Returns the X coordinate of this event. Whole numbers are pixels; the
- * value may have a fraction for input devices that are sub-pixel precise.
->>>>>>> cafdea61a85c8f5d0646cc9413a09346c637f43f:core/java/android/view/MotionEvent.java
*/
public final float getX() {
return mX;
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 13a6e7aacf19..25bbb6a3bf5d 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -17,7 +17,6 @@
package android.view;
import android.content.Context;
-import android.content.res.CompatibilityInfo;
import android.content.res.CompatibilityInfo.Translator;
import android.graphics.Canvas;
import android.graphics.PixelFormat;
@@ -257,9 +256,9 @@ public class SurfaceView extends View {
public boolean dispatchTouchEvent(MotionEvent event) {
// SurfaceView uses pre-scaled size unless fixed size is requested. This hook
// scales the event back to the pre-scaled coordinates for such surface.
- if (mRequestedWidth < 0 && mTranslator != null) {
+ if (mScaled) {
MotionEvent scaledBack = MotionEvent.obtain(event);
- scaledBack.scale(mTranslator.applicationScale);
+ mTranslator.translateEventInScreenToAppWindow(event);
try {
return super.dispatchTouchEvent(scaledBack);
} finally {
@@ -291,12 +290,15 @@ public class SurfaceView extends View {
public void setWindowType(int type) {
mWindowType = type;
}
+
+ boolean mScaled = false;
private void updateWindow(boolean force) {
if (!mHaveFrame) {
return;
}
- mTranslator = ((ViewRoot)getRootView().getParent()).mTranslator;
+ ViewRoot viewRoot = (ViewRoot) getRootView().getParent();
+ mTranslator = viewRoot.mTranslator;
float appScale = mTranslator == null ? 1.0f : mTranslator.applicationScale;
@@ -310,6 +312,9 @@ public class SurfaceView extends View {
if (mRequestedWidth <= 0 && mTranslator != null) {
myWidth *= appScale;
myHeight *= appScale;
+ mScaled = true;
+ } else {
+ mScaled = false;
}
getLocationInWindow(mLocation);
@@ -353,8 +358,10 @@ public class SurfaceView extends View {
| WindowManager.LayoutParams.FLAG_SCALED
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
- | WindowManager.LayoutParams.FLAG_NO_COMPATIBILITY_SCALING
;
+ if (!getContext().getResources().getCompatibilityInfo().supportsScreen()) {
+ mLayout.flags |= WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
+ }
mLayout.memoryType = mRequestedType;
@@ -534,6 +541,7 @@ public class SurfaceView extends View {
private SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
private static final String LOG_TAG = "SurfaceHolder";
+ private int mSaveCount;
public boolean isCreating() {
return mIsCreating;
@@ -628,6 +636,10 @@ public class SurfaceView extends View {
if (localLOGV) Log.i(TAG, "Returned canvas: " + c);
if (c != null) {
mLastLockTime = SystemClock.uptimeMillis();
+ if (mScaled) {
+ mSaveCount = c.save();
+ mTranslator.translateCanvas(c);
+ }
return c;
}
@@ -650,6 +662,9 @@ public class SurfaceView extends View {
}
public void unlockCanvasAndPost(Canvas canvas) {
+ if (mScaled) {
+ canvas.restoreToCount(mSaveCount);
+ }
mSurface.unlockCanvasAndPost(canvas);
mSurfaceLock.unlock();
}
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 1e3cdb34f436..2acf79081306 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -504,8 +504,12 @@ public final class ViewRoot extends Handler implements ViewParent,
void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
synchronized (this) {
int oldSoftInputMode = mWindowAttributes.softInputMode;
+ // preserve compatible window flag if exists.
+ int compatibleWindowFlag =
+ mWindowAttributes.flags & WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
mWindowAttributes.copyFrom(attrs);
-
+ mWindowAttributes.flags |= compatibleWindowFlag;
+
if (newView) {
mSoftInputMode = attrs.softInputMode;
requestLayout();
@@ -1308,7 +1312,8 @@ public final class ViewRoot extends Handler implements ViewParent,
if (DEBUG_DRAW) {
Context cxt = mView.getContext();
Log.i(TAG, "Drawing: package:" + cxt.getPackageName() +
- ", metrics=" + mView.getContext().getResources().getDisplayMetrics());
+ ", metrics=" + cxt.getResources().getDisplayMetrics() +
+ ", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
}
int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
try {
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 2c32d8b54af6..02e0515d6f28 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -358,6 +358,8 @@ public abstract class Window {
private class LocalWindowManager implements WindowManager {
LocalWindowManager(WindowManager wm) {
mWindowManager = wm;
+ mDefaultDisplay = mContext.getResources().getDefaultDisplay(
+ mWindowManager.getDefaultDisplay());
}
public final void addView(View view, ViewGroup.LayoutParams params) {
@@ -420,10 +422,12 @@ public abstract class Window {
}
public Display getDefaultDisplay() {
- return mWindowManager.getDefaultDisplay();
+ return mDefaultDisplay;
}
- WindowManager mWindowManager;
+ private final WindowManager mWindowManager;
+
+ private final Display mDefaultDisplay;
}
/**
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index e96a15ba05f2..6a26a3144128 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -483,19 +483,12 @@ public interface WindowManager extends ViewManager {
* {@hide} */
public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000;
- /** Window flag: special flag to let a window ignore the compatibility scaling.
- * This is used by SurfaceView to pass this info into ViewRoot, and not used
- * by WindowManager.
- *
- * {@hide} */
- public static final int FLAG_NO_COMPATIBILITY_SCALING = 0x00100000;
-
/** Window flag: special flag to limit the size of the window to be
* original size ([320x480] x density). Used to create window for applications
* running under compatibility mode.
*
* {@hide} */
- public static final int FLAG_COMPATIBLE_WINDOW = 0x00200000;
+ public static final int FLAG_COMPATIBLE_WINDOW = 0x00100000;
/** Window flag: a special option intended for system dialogs. When
* this flag is set, the window will demand focus unconditionally when
@@ -986,6 +979,9 @@ public interface WindowManager extends ViewManager {
sb.append(" or=");
sb.append(screenOrientation);
}
+ if ((flags & FLAG_COMPATIBLE_WINDOW) != 0) {
+ sb.append(" compatible=true");
+ }
sb.append('}');
return sb.toString();
}
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index e04ae720a6da..e6ccd70ab74c 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -465,8 +465,6 @@ class BrowserFrame extends Handler {
* @param postData If the method is "POST" postData is sent as the request
* body. Is null when empty.
* @param cacheMode The cache mode to use when loading this resource.
- * @param isHighPriority True if this resource needs to be put at the front
- * of the network queue.
* @param synchronous True if the load is synchronous.
* @return A newly created LoadListener object.
*/
@@ -476,7 +474,6 @@ class BrowserFrame extends Handler {
HashMap headers,
byte[] postData,
int cacheMode,
- boolean isHighPriority,
boolean synchronous) {
PerfChecker checker = new PerfChecker();
@@ -542,8 +539,8 @@ class BrowserFrame extends Handler {
if (DebugFlags.BROWSER_FRAME) {
Log.v(LOGTAG, "startLoadingResource: url=" + url + ", method="
- + method + ", postData=" + postData + ", isHighPriority="
- + isHighPriority + ", isMainFramePage=" + isMainFramePage);
+ + method + ", postData=" + postData + ", isMainFramePage="
+ + isMainFramePage);
}
// Create a LoadListener
@@ -553,12 +550,14 @@ class BrowserFrame extends Handler {
mCallbackProxy.onLoadResource(url);
if (LoadListener.getNativeLoaderCount() > MAX_OUTSTANDING_REQUESTS) {
+ // send an error message, so that loadListener can be deleted
+ // after this is returned. This is important as LoadListener's
+ // nativeError will remove the request from its DocLoader's request
+ // list. But the set up is not done until this method is returned.
loadListener.error(
android.net.http.EventHandler.ERROR, mContext.getString(
com.android.internal.R.string.httpErrorTooManyRequests));
- loadListener.notifyError();
- loadListener.tearDown();
- return null;
+ return loadListener;
}
// during synchronous load, the WebViewCore thread is blocked, so we
@@ -568,8 +567,7 @@ class BrowserFrame extends Handler {
CacheManager.endCacheTransaction();
}
- FrameLoader loader = new FrameLoader(loadListener, mSettings,
- method, isHighPriority);
+ FrameLoader loader = new FrameLoader(loadListener, mSettings, method);
loader.setHeaders(headers);
loader.setPostData(postData);
// Set the load mode to the mode used for the current page.
diff --git a/core/java/android/webkit/FrameLoader.java b/core/java/android/webkit/FrameLoader.java
index f98c5d38dfd1..829872962921 100644
--- a/core/java/android/webkit/FrameLoader.java
+++ b/core/java/android/webkit/FrameLoader.java
@@ -28,7 +28,6 @@ class FrameLoader {
private final LoadListener mListener;
private final String mMethod;
- private final boolean mIsHighPriority;
private final WebSettings mSettings;
private Map<String, String> mHeaders;
private byte[] mPostData;
@@ -52,11 +51,10 @@ class FrameLoader {
private static final String LOGTAG = "webkit";
FrameLoader(LoadListener listener, WebSettings settings,
- String method, boolean highPriority) {
+ String method) {
mListener = listener;
mHeaders = null;
mMethod = method;
- mIsHighPriority = highPriority;
mCacheMode = WebSettings.LOAD_NORMAL;
mSettings = settings;
}
@@ -175,8 +173,7 @@ class FrameLoader {
// as response from the cache could be a redirect
// and we may need to initiate a network request if the cache
// can't satisfy redirect URL
- mListener.setRequestData(mMethod, mHeaders, mPostData,
- mIsHighPriority);
+ mListener.setRequestData(mMethod, mHeaders, mPostData);
return true;
}
@@ -190,7 +187,7 @@ class FrameLoader {
try {
ret = mNetwork.requestURL(mMethod, mHeaders,
- mPostData, mListener, mIsHighPriority);
+ mPostData, mListener);
} catch (android.net.ParseException ex) {
error = EventHandler.ERROR_BAD_URL;
} catch (java.lang.RuntimeException ex) {
diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java
index ea12f3601d4a..4fe4036d3b87 100644
--- a/core/java/android/webkit/LoadListener.java
+++ b/core/java/android/webkit/LoadListener.java
@@ -38,6 +38,7 @@ import com.android.internal.R;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
import java.util.Vector;
import java.util.regex.Pattern;
@@ -70,7 +71,12 @@ class LoadListener extends Handler implements EventHandler {
private static final int HTTP_NOT_FOUND = 404;
private static final int HTTP_PROXY_AUTH = 407;
- private static final String CERT_MIMETYPE = "application/x-x509-ca-cert";
+ private static HashSet<String> sCertificateMimeTypeMap;
+ static {
+ sCertificateMimeTypeMap = new HashSet<String>();
+ sCertificateMimeTypeMap.add("application/x-x509-ca-cert");
+ sCertificateMimeTypeMap.add("application/x-pkcs12");
+ }
private static int sNativeLoaderCount;
@@ -104,7 +110,6 @@ class LoadListener extends Handler implements EventHandler {
private String mMethod;
private Map<String, String> mRequestHeaders;
private byte[] mPostData;
- private boolean mIsHighPriority;
// Flag to indicate that this load is synchronous.
private boolean mSynchronous;
private Vector<Message> mMessageQueue;
@@ -312,7 +317,17 @@ class LoadListener extends Handler implements EventHandler {
if (mMimeType.equals("text/plain") ||
mMimeType.equals("application/octet-stream")) {
- String newMimeType = guessMimeTypeFromExtension();
+ // for attachment, use the filename in the Content-Disposition
+ // to guess the mimetype
+ String contentDisposition = headers.getContentDisposition();
+ String url = null;
+ if (contentDisposition != null) {
+ url = URLUtil.parseContentDisposition(contentDisposition);
+ }
+ if (url == null) {
+ url = mUrl;
+ }
+ String newMimeType = guessMimeTypeFromExtension(url);
if (newMimeType != null) {
mMimeType = newMimeType;
}
@@ -775,14 +790,12 @@ class LoadListener extends Handler implements EventHandler {
* @param method
* @param headers
* @param postData
- * @param isHighPriority
*/
void setRequestData(String method, Map<String, String> headers,
- byte[] postData, boolean isHighPriority) {
+ byte[] postData) {
mMethod = method;
mRequestHeaders = headers;
mPostData = postData;
- mIsHighPriority = isHighPriority;
}
/**
@@ -921,7 +934,7 @@ class LoadListener extends Handler implements EventHandler {
// This commits the headers without checking the response status code.
private void commitHeaders() {
- if (mIsMainPageLoader && CERT_MIMETYPE.equals(mMimeType)) {
+ if (mIsMainPageLoader && sCertificateMimeTypeMap.contains(mMimeType)) {
// In the case of downloading certificate, we will save it to the
// Keystore in commitLoad. Do not call webcore.
return;
@@ -966,7 +979,7 @@ class LoadListener extends Handler implements EventHandler {
private void commitLoad() {
if (mCancelled) return;
- if (mIsMainPageLoader && CERT_MIMETYPE.equals(mMimeType)) {
+ if (mIsMainPageLoader && sCertificateMimeTypeMap.contains(mMimeType)) {
// In the case of downloading certificate, we will save it to the
// Keystore and stop the current loading so that it will not
// generate a new history page
@@ -1178,7 +1191,7 @@ class LoadListener extends Handler implements EventHandler {
// Network.requestURL.
Network network = Network.getInstance(getContext());
if (!network.requestURL(mMethod, mRequestHeaders,
- mPostData, this, mIsHighPriority)) {
+ mPostData, this)) {
// Signal a bad url error if we could not load the
// redirection.
handleError(EventHandler.ERROR_BAD_URL,
@@ -1357,7 +1370,8 @@ class LoadListener extends Handler implements EventHandler {
*/
private boolean ignoreCallbacks() {
return (mCancelled || mAuthHeader != null ||
- (mStatusCode > 300 && mStatusCode < 400));
+ // Allow 305 (Use Proxy) to call through.
+ (mStatusCode > 300 && mStatusCode < 400 && mStatusCode != 305));
}
/**
@@ -1396,7 +1410,7 @@ class LoadListener extends Handler implements EventHandler {
// of frames. If no content-type was specified, it is fine to
// default to text/html.
mMimeType = "text/html";
- String newMimeType = guessMimeTypeFromExtension();
+ String newMimeType = guessMimeTypeFromExtension(mUrl);
if (newMimeType != null) {
mMimeType = newMimeType;
}
@@ -1406,14 +1420,14 @@ class LoadListener extends Handler implements EventHandler {
/**
* guess MIME type based on the file extension.
*/
- private String guessMimeTypeFromExtension() {
+ private String guessMimeTypeFromExtension(String url) {
// PENDING: need to normalize url
if (DebugFlags.LOAD_LISTENER) {
- Log.v(LOGTAG, "guessMimeTypeFromExtension: mURL = " + mUrl);
+ Log.v(LOGTAG, "guessMimeTypeFromExtension: url = " + url);
}
return MimeTypeMap.getSingleton().getMimeTypeFromExtension(
- MimeTypeMap.getFileExtensionFromUrl(mUrl));
+ MimeTypeMap.getFileExtensionFromUrl(url));
}
/**
diff --git a/core/java/android/webkit/MimeTypeMap.java b/core/java/android/webkit/MimeTypeMap.java
index 096f38add65b..b2aa524ede6d 100644
--- a/core/java/android/webkit/MimeTypeMap.java
+++ b/core/java/android/webkit/MimeTypeMap.java
@@ -22,7 +22,7 @@ import java.util.regex.Pattern;
/**
* Two-way map that maps MIME-types to file extensions and vice versa.
*/
-public /* package */ class MimeTypeMap {
+public class MimeTypeMap {
/**
* Singleton MIME-type map instance:
@@ -39,7 +39,6 @@ public /* package */ class MimeTypeMap {
*/
private HashMap<String, String> mExtensionToMimeTypeMap;
-
/**
* Creates a new MIME-type map.
*/
@@ -50,7 +49,10 @@ public /* package */ class MimeTypeMap {
/**
* Returns the file extension or an empty string iff there is no
- * extension.
+ * extension. This method is a convenience method for obtaining the
+ * extension of a url and has undefined results for other Strings.
+ * @param url
+ * @return The file extension of the given url.
*/
public static String getFileExtensionFromUrl(String url) {
if (url != null && url.length() > 0) {
@@ -80,8 +82,7 @@ public /* package */ class MimeTypeMap {
* Load an entry into the map. This does not check if the item already
* exists, it trusts the caller!
*/
- private void loadEntry(String mimeType, String extension,
- boolean textType) {
+ private void loadEntry(String mimeType, String extension) {
//
// if we have an existing x --> y mapping, we do not want to
// override it with another mapping x --> ?
@@ -94,18 +95,12 @@ public /* package */ class MimeTypeMap {
mMimeTypeToExtensionMap.put(mimeType, extension);
}
- //
- // here, we don't want to map extensions to text MIME types;
- // otherwise, we will start replacing generic text/plain and
- // text/html with text MIME types that our platform does not
- // understand.
- //
- if (!textType) {
- mExtensionToMimeTypeMap.put(extension, mimeType);
- }
+ mExtensionToMimeTypeMap.put(extension, mimeType);
}
/**
+ * Return true if the given MIME type has an entry in the map.
+ * @param mimeType A MIME type (i.e. text/plain)
* @return True iff there is a mimeType entry in the map.
*/
public boolean hasMimeType(String mimeType) {
@@ -117,7 +112,9 @@ public /* package */ class MimeTypeMap {
}
/**
- * @return The extension for the MIME type or null iff there is none.
+ * Return the MIME type for the given extension.
+ * @param extension A file extension without the leading '.'
+ * @return The MIME type for the given extension or null iff there is none.
*/
public String getMimeTypeFromExtension(String extension) {
if (extension != null && extension.length() > 0) {
@@ -128,18 +125,23 @@ public /* package */ class MimeTypeMap {
}
/**
+ * Return true if the given extension has a registered MIME type.
+ * @param extension A file extension without the leading '.'
* @return True iff there is an extension entry in the map.
*/
public boolean hasExtension(String extension) {
if (extension != null && extension.length() > 0) {
return mExtensionToMimeTypeMap.containsKey(extension);
}
-
return false;
}
/**
- * @return The MIME type for the extension or null iff there is none.
+ * Return the registered extension for the given MIME type. Note that some
+ * MIME types map to multiple extensions. This call will return the most
+ * common extension for the given MIME type.
+ * @param mimeType A MIME type (i.e. text/plain)
+ * @return The extension for the given MIME type or null iff there is none.
*/
public String getExtensionFromMimeType(String mimeType) {
if (mimeType != null && mimeType.length() > 0) {
@@ -150,6 +152,7 @@ public /* package */ class MimeTypeMap {
}
/**
+ * Get the singleton instance of MimeTypeMap.
* @return The singleton instance of the MIME-type map.
*/
public static MimeTypeMap getSingleton() {
@@ -164,340 +167,311 @@ public /* package */ class MimeTypeMap {
// mail.google.com/a/google.com
//
// and "active" MIME types (due to potential security issues).
- //
- // Also, notice that not all data from this table is actually
- // added (see loadEntry method for more details).
- sMimeTypeMap.loadEntry("application/andrew-inset", "ez", false);
- sMimeTypeMap.loadEntry("application/dsptype", "tsp", false);
- sMimeTypeMap.loadEntry("application/futuresplash", "spl", false);
- sMimeTypeMap.loadEntry("application/hta", "hta", false);
- sMimeTypeMap.loadEntry("application/mac-binhex40", "hqx", false);
- sMimeTypeMap.loadEntry("application/mac-compactpro", "cpt", false);
- sMimeTypeMap.loadEntry("application/mathematica", "nb", false);
- sMimeTypeMap.loadEntry("application/msaccess", "mdb", false);
- sMimeTypeMap.loadEntry("application/oda", "oda", false);
- sMimeTypeMap.loadEntry("application/ogg", "ogg", false);
- sMimeTypeMap.loadEntry("application/pdf", "pdf", false);
- sMimeTypeMap.loadEntry("application/pgp-keys", "key", false);
- sMimeTypeMap.loadEntry("application/pgp-signature", "pgp", false);
- sMimeTypeMap.loadEntry("application/pics-rules", "prf", false);
- sMimeTypeMap.loadEntry("application/rar", "rar", false);
- sMimeTypeMap.loadEntry("application/rdf+xml", "rdf", false);
- sMimeTypeMap.loadEntry("application/rss+xml", "rss", false);
- sMimeTypeMap.loadEntry("application/zip", "zip", false);
+ sMimeTypeMap.loadEntry("application/andrew-inset", "ez");
+ sMimeTypeMap.loadEntry("application/dsptype", "tsp");
+ sMimeTypeMap.loadEntry("application/futuresplash", "spl");
+ sMimeTypeMap.loadEntry("application/hta", "hta");
+ sMimeTypeMap.loadEntry("application/mac-binhex40", "hqx");
+ sMimeTypeMap.loadEntry("application/mac-compactpro", "cpt");
+ sMimeTypeMap.loadEntry("application/mathematica", "nb");
+ sMimeTypeMap.loadEntry("application/msaccess", "mdb");
+ sMimeTypeMap.loadEntry("application/oda", "oda");
+ sMimeTypeMap.loadEntry("application/ogg", "ogg");
+ sMimeTypeMap.loadEntry("application/pdf", "pdf");
+ sMimeTypeMap.loadEntry("application/pgp-keys", "key");
+ sMimeTypeMap.loadEntry("application/pgp-signature", "pgp");
+ sMimeTypeMap.loadEntry("application/pics-rules", "prf");
+ sMimeTypeMap.loadEntry("application/rar", "rar");
+ sMimeTypeMap.loadEntry("application/rdf+xml", "rdf");
+ sMimeTypeMap.loadEntry("application/rss+xml", "rss");
+ sMimeTypeMap.loadEntry("application/zip", "zip");
sMimeTypeMap.loadEntry("application/vnd.android.package-archive",
- "apk", false);
- sMimeTypeMap.loadEntry("application/vnd.cinderella", "cdy", false);
- sMimeTypeMap.loadEntry("application/vnd.ms-pki.stl", "stl", false);
+ "apk");
+ sMimeTypeMap.loadEntry("application/vnd.cinderella", "cdy");
+ sMimeTypeMap.loadEntry("application/vnd.ms-pki.stl", "stl");
sMimeTypeMap.loadEntry(
- "application/vnd.oasis.opendocument.database", "odb",
- false);
+ "application/vnd.oasis.opendocument.database", "odb");
sMimeTypeMap.loadEntry(
- "application/vnd.oasis.opendocument.formula", "odf",
- false);
+ "application/vnd.oasis.opendocument.formula", "odf");
sMimeTypeMap.loadEntry(
- "application/vnd.oasis.opendocument.graphics", "odg",
- false);
+ "application/vnd.oasis.opendocument.graphics", "odg");
sMimeTypeMap.loadEntry(
"application/vnd.oasis.opendocument.graphics-template",
- "otg", false);
+ "otg");
sMimeTypeMap.loadEntry(
- "application/vnd.oasis.opendocument.image", "odi", false);
+ "application/vnd.oasis.opendocument.image", "odi");
sMimeTypeMap.loadEntry(
- "application/vnd.oasis.opendocument.spreadsheet", "ods",
- false);
+ "application/vnd.oasis.opendocument.spreadsheet", "ods");
sMimeTypeMap.loadEntry(
"application/vnd.oasis.opendocument.spreadsheet-template",
- "ots", false);
- sMimeTypeMap.loadEntry(
- "application/vnd.oasis.opendocument.text", "odt", false);
+ "ots");
sMimeTypeMap.loadEntry(
- "application/vnd.oasis.opendocument.text-master", "odm",
- false);
+ "application/vnd.oasis.opendocument.text", "odt");
sMimeTypeMap.loadEntry(
- "application/vnd.oasis.opendocument.text-template", "ott",
- false);
+ "application/vnd.oasis.opendocument.text-master", "odm");
sMimeTypeMap.loadEntry(
- "application/vnd.oasis.opendocument.text-web", "oth",
- false);
- sMimeTypeMap.loadEntry("application/vnd.rim.cod", "cod", false);
- sMimeTypeMap.loadEntry("application/vnd.smaf", "mmf", false);
- sMimeTypeMap.loadEntry("application/vnd.stardivision.calc", "sdc",
- false);
- sMimeTypeMap.loadEntry("application/vnd.stardivision.draw", "sda",
- false);
+ "application/vnd.oasis.opendocument.text-template", "ott");
sMimeTypeMap.loadEntry(
- "application/vnd.stardivision.impress", "sdd", false);
+ "application/vnd.oasis.opendocument.text-web", "oth");
+ sMimeTypeMap.loadEntry("application/vnd.rim.cod", "cod");
+ sMimeTypeMap.loadEntry("application/vnd.smaf", "mmf");
+ sMimeTypeMap.loadEntry("application/vnd.stardivision.calc", "sdc");
+ sMimeTypeMap.loadEntry("application/vnd.stardivision.draw", "sda");
sMimeTypeMap.loadEntry(
- "application/vnd.stardivision.impress", "sdp", false);
- sMimeTypeMap.loadEntry("application/vnd.stardivision.math", "smf",
- false);
- sMimeTypeMap.loadEntry("application/vnd.stardivision.writer", "sdw",
- false);
- sMimeTypeMap.loadEntry("application/vnd.stardivision.writer", "vor",
- false);
+ "application/vnd.stardivision.impress", "sdd");
sMimeTypeMap.loadEntry(
- "application/vnd.stardivision.writer-global", "sgl", false);
- sMimeTypeMap.loadEntry("application/vnd.sun.xml.calc", "sxc",
- false);
+ "application/vnd.stardivision.impress", "sdp");
+ sMimeTypeMap.loadEntry("application/vnd.stardivision.math", "smf");
+ sMimeTypeMap.loadEntry("application/vnd.stardivision.writer",
+ "sdw");
+ sMimeTypeMap.loadEntry("application/vnd.stardivision.writer",
+ "vor");
sMimeTypeMap.loadEntry(
- "application/vnd.sun.xml.calc.template", "stc", false);
- sMimeTypeMap.loadEntry("application/vnd.sun.xml.draw", "sxd",
- false);
+ "application/vnd.stardivision.writer-global", "sgl");
+ sMimeTypeMap.loadEntry("application/vnd.sun.xml.calc", "sxc");
sMimeTypeMap.loadEntry(
- "application/vnd.sun.xml.draw.template", "std", false);
- sMimeTypeMap.loadEntry("application/vnd.sun.xml.impress", "sxi",
- false);
+ "application/vnd.sun.xml.calc.template", "stc");
+ sMimeTypeMap.loadEntry("application/vnd.sun.xml.draw", "sxd");
sMimeTypeMap.loadEntry(
- "application/vnd.sun.xml.impress.template", "sti", false);
- sMimeTypeMap.loadEntry("application/vnd.sun.xml.math", "sxm",
- false);
- sMimeTypeMap.loadEntry("application/vnd.sun.xml.writer", "sxw",
- false);
+ "application/vnd.sun.xml.draw.template", "std");
+ sMimeTypeMap.loadEntry("application/vnd.sun.xml.impress", "sxi");
sMimeTypeMap.loadEntry(
- "application/vnd.sun.xml.writer.global", "sxg", false);
+ "application/vnd.sun.xml.impress.template", "sti");
+ sMimeTypeMap.loadEntry("application/vnd.sun.xml.math", "sxm");
+ sMimeTypeMap.loadEntry("application/vnd.sun.xml.writer", "sxw");
sMimeTypeMap.loadEntry(
- "application/vnd.sun.xml.writer.template", "stw", false);
- sMimeTypeMap.loadEntry("application/vnd.visio", "vsd", false);
- sMimeTypeMap.loadEntry("application/x-abiword", "abw", false);
- sMimeTypeMap.loadEntry("application/x-apple-diskimage", "dmg",
- false);
- sMimeTypeMap.loadEntry("application/x-bcpio", "bcpio", false);
- sMimeTypeMap.loadEntry("application/x-bittorrent", "torrent",
- false);
- sMimeTypeMap.loadEntry("application/x-cdf", "cdf", false);
- sMimeTypeMap.loadEntry("application/x-cdlink", "vcd", false);
- sMimeTypeMap.loadEntry("application/x-chess-pgn", "pgn", false);
- sMimeTypeMap.loadEntry("application/x-cpio", "cpio", false);
- sMimeTypeMap.loadEntry("application/x-debian-package", "deb",
- false);
- sMimeTypeMap.loadEntry("application/x-debian-package", "udeb",
- false);
- sMimeTypeMap.loadEntry("application/x-director", "dcr", false);
- sMimeTypeMap.loadEntry("application/x-director", "dir", false);
- sMimeTypeMap.loadEntry("application/x-director", "dxr", false);
- sMimeTypeMap.loadEntry("application/x-dms", "dms", false);
- sMimeTypeMap.loadEntry("application/x-doom", "wad", false);
- sMimeTypeMap.loadEntry("application/x-dvi", "dvi", false);
- sMimeTypeMap.loadEntry("application/x-flac", "flac", false);
- sMimeTypeMap.loadEntry("application/x-font", "pfa", false);
- sMimeTypeMap.loadEntry("application/x-font", "pfb", false);
- sMimeTypeMap.loadEntry("application/x-font", "gsf", false);
- sMimeTypeMap.loadEntry("application/x-font", "pcf", false);
- sMimeTypeMap.loadEntry("application/x-font", "pcf.Z", false);
- sMimeTypeMap.loadEntry("application/x-freemind", "mm", false);
- sMimeTypeMap.loadEntry("application/x-futuresplash", "spl", false);
- sMimeTypeMap.loadEntry("application/x-gnumeric", "gnumeric", false);
- sMimeTypeMap.loadEntry("application/x-go-sgf", "sgf", false);
- sMimeTypeMap.loadEntry("application/x-graphing-calculator", "gcf",
- false);
- sMimeTypeMap.loadEntry("application/x-gtar", "gtar", false);
- sMimeTypeMap.loadEntry("application/x-gtar", "tgz", false);
- sMimeTypeMap.loadEntry("application/x-gtar", "taz", false);
- sMimeTypeMap.loadEntry("application/x-hdf", "hdf", false);
- sMimeTypeMap.loadEntry("application/x-ica", "ica", false);
- sMimeTypeMap.loadEntry("application/x-internet-signup", "ins",
- false);
- sMimeTypeMap.loadEntry("application/x-internet-signup", "isp",
- false);
- sMimeTypeMap.loadEntry("application/x-iphone", "iii", false);
- sMimeTypeMap.loadEntry("application/x-iso9660-image", "iso", false);
- sMimeTypeMap.loadEntry("application/x-jmol", "jmz", false);
- sMimeTypeMap.loadEntry("application/x-kchart", "chrt", false);
- sMimeTypeMap.loadEntry("application/x-killustrator", "kil", false);
- sMimeTypeMap.loadEntry("application/x-koan", "skp", false);
- sMimeTypeMap.loadEntry("application/x-koan", "skd", false);
- sMimeTypeMap.loadEntry("application/x-koan", "skt", false);
- sMimeTypeMap.loadEntry("application/x-koan", "skm", false);
- sMimeTypeMap.loadEntry("application/x-kpresenter", "kpr", false);
- sMimeTypeMap.loadEntry("application/x-kpresenter", "kpt", false);
- sMimeTypeMap.loadEntry("application/x-kspread", "ksp", false);
- sMimeTypeMap.loadEntry("application/x-kword", "kwd", false);
- sMimeTypeMap.loadEntry("application/x-kword", "kwt", false);
- sMimeTypeMap.loadEntry("application/x-latex", "latex", false);
- sMimeTypeMap.loadEntry("application/x-lha", "lha", false);
- sMimeTypeMap.loadEntry("application/x-lzh", "lzh", false);
- sMimeTypeMap.loadEntry("application/x-lzx", "lzx", false);
- sMimeTypeMap.loadEntry("application/x-maker", "frm", false);
- sMimeTypeMap.loadEntry("application/x-maker", "maker", false);
- sMimeTypeMap.loadEntry("application/x-maker", "frame", false);
- sMimeTypeMap.loadEntry("application/x-maker", "fb", false);
- sMimeTypeMap.loadEntry("application/x-maker", "book", false);
- sMimeTypeMap.loadEntry("application/x-maker", "fbdoc", false);
- sMimeTypeMap.loadEntry("application/x-mif", "mif", false);
- sMimeTypeMap.loadEntry("application/x-ms-wmd", "wmd", false);
- sMimeTypeMap.loadEntry("application/x-ms-wmz", "wmz", false);
- sMimeTypeMap.loadEntry("application/x-msi", "msi", false);
- sMimeTypeMap.loadEntry("application/x-ns-proxy-autoconfig", "pac",
- false);
- sMimeTypeMap.loadEntry("application/x-nwc", "nwc", false);
- sMimeTypeMap.loadEntry("application/x-object", "o", false);
- sMimeTypeMap.loadEntry("application/x-oz-application", "oza",
- false);
- sMimeTypeMap.loadEntry("application/x-pkcs7-certreqresp", "p7r",
- false);
- sMimeTypeMap.loadEntry("application/x-pkcs7-crl", "crl", false);
- sMimeTypeMap.loadEntry("application/x-quicktimeplayer", "qtl",
- false);
- sMimeTypeMap.loadEntry("application/x-shar", "shar", false);
- sMimeTypeMap.loadEntry("application/x-stuffit", "sit", false);
- sMimeTypeMap.loadEntry("application/x-sv4cpio", "sv4cpio", false);
- sMimeTypeMap.loadEntry("application/x-sv4crc", "sv4crc", false);
- sMimeTypeMap.loadEntry("application/x-tar", "tar", false);
- sMimeTypeMap.loadEntry("application/x-texinfo", "texinfo", false);
- sMimeTypeMap.loadEntry("application/x-texinfo", "texi", false);
- sMimeTypeMap.loadEntry("application/x-troff", "t", false);
- sMimeTypeMap.loadEntry("application/x-troff", "roff", false);
- sMimeTypeMap.loadEntry("application/x-troff-man", "man", false);
- sMimeTypeMap.loadEntry("application/x-ustar", "ustar", false);
- sMimeTypeMap.loadEntry("application/x-wais-source", "src", false);
- sMimeTypeMap.loadEntry("application/x-wingz", "wz", false);
+ "application/vnd.sun.xml.writer.global", "sxg");
sMimeTypeMap.loadEntry(
- "application/x-webarchive", "webarchive", false); // added
- sMimeTypeMap.loadEntry("application/x-x509-ca-cert", "crt", false);
- sMimeTypeMap.loadEntry("application/x-xcf", "xcf", false);
- sMimeTypeMap.loadEntry("application/x-xfig", "fig", false);
- sMimeTypeMap.loadEntry("application/xhtml+xml", "xhtml", false);
- sMimeTypeMap.loadEntry("audio/basic", "snd", false);
- sMimeTypeMap.loadEntry("audio/midi", "mid", false);
- sMimeTypeMap.loadEntry("audio/midi", "midi", false);
- sMimeTypeMap.loadEntry("audio/midi", "kar", false);
- sMimeTypeMap.loadEntry("audio/mpeg", "mpga", false);
- sMimeTypeMap.loadEntry("audio/mpeg", "mpega", false);
- sMimeTypeMap.loadEntry("audio/mpeg", "mp2", false);
- sMimeTypeMap.loadEntry("audio/mpeg", "mp3", false);
- sMimeTypeMap.loadEntry("audio/mpeg", "m4a", false);
- sMimeTypeMap.loadEntry("audio/mpegurl", "m3u", false);
- sMimeTypeMap.loadEntry("audio/prs.sid", "sid", false);
- sMimeTypeMap.loadEntry("audio/x-aiff", "aif", false);
- sMimeTypeMap.loadEntry("audio/x-aiff", "aiff", false);
- sMimeTypeMap.loadEntry("audio/x-aiff", "aifc", false);
- sMimeTypeMap.loadEntry("audio/x-gsm", "gsm", false);
- sMimeTypeMap.loadEntry("audio/x-mpegurl", "m3u", false);
- sMimeTypeMap.loadEntry("audio/x-ms-wma", "wma", false);
- sMimeTypeMap.loadEntry("audio/x-ms-wax", "wax", false);
- sMimeTypeMap.loadEntry("audio/x-pn-realaudio", "ra", false);
- sMimeTypeMap.loadEntry("audio/x-pn-realaudio", "rm", false);
- sMimeTypeMap.loadEntry("audio/x-pn-realaudio", "ram", false);
- sMimeTypeMap.loadEntry("audio/x-realaudio", "ra", false);
- sMimeTypeMap.loadEntry("audio/x-scpls", "pls", false);
- sMimeTypeMap.loadEntry("audio/x-sd2", "sd2", false);
- sMimeTypeMap.loadEntry("audio/x-wav", "wav", false);
- sMimeTypeMap.loadEntry("image/bmp", "bmp", false); // added
- sMimeTypeMap.loadEntry("image/gif", "gif", false);
- sMimeTypeMap.loadEntry("image/ico", "cur", false); // added
- sMimeTypeMap.loadEntry("image/ico", "ico", false); // added
- sMimeTypeMap.loadEntry("image/ief", "ief", false);
- sMimeTypeMap.loadEntry("image/jpeg", "jpeg", false);
- sMimeTypeMap.loadEntry("image/jpeg", "jpg", false);
- sMimeTypeMap.loadEntry("image/jpeg", "jpe", false);
- sMimeTypeMap.loadEntry("image/pcx", "pcx", false);
- sMimeTypeMap.loadEntry("image/png", "png", false);
- sMimeTypeMap.loadEntry("image/svg+xml", "svg", false);
- sMimeTypeMap.loadEntry("image/svg+xml", "svgz", false);
- sMimeTypeMap.loadEntry("image/tiff", "tiff", false);
- sMimeTypeMap.loadEntry("image/tiff", "tif", false);
- sMimeTypeMap.loadEntry("image/vnd.djvu", "djvu", false);
- sMimeTypeMap.loadEntry("image/vnd.djvu", "djv", false);
- sMimeTypeMap.loadEntry("image/vnd.wap.wbmp", "wbmp", false);
- sMimeTypeMap.loadEntry("image/x-cmu-raster", "ras", false);
- sMimeTypeMap.loadEntry("image/x-coreldraw", "cdr", false);
- sMimeTypeMap.loadEntry("image/x-coreldrawpattern", "pat", false);
- sMimeTypeMap.loadEntry("image/x-coreldrawtemplate", "cdt", false);
- sMimeTypeMap.loadEntry("image/x-corelphotopaint", "cpt", false);
- sMimeTypeMap.loadEntry("image/x-icon", "ico", false);
- sMimeTypeMap.loadEntry("image/x-jg", "art", false);
- sMimeTypeMap.loadEntry("image/x-jng", "jng", false);
- sMimeTypeMap.loadEntry("image/x-ms-bmp", "bmp", false);
- sMimeTypeMap.loadEntry("image/x-photoshop", "psd", false);
- sMimeTypeMap.loadEntry("image/x-portable-anymap", "pnm", false);
- sMimeTypeMap.loadEntry("image/x-portable-bitmap", "pbm", false);
- sMimeTypeMap.loadEntry("image/x-portable-graymap", "pgm", false);
- sMimeTypeMap.loadEntry("image/x-portable-pixmap", "ppm", false);
- sMimeTypeMap.loadEntry("image/x-rgb", "rgb", false);
- sMimeTypeMap.loadEntry("image/x-xbitmap", "xbm", false);
- sMimeTypeMap.loadEntry("image/x-xpixmap", "xpm", false);
- sMimeTypeMap.loadEntry("image/x-xwindowdump", "xwd", false);
- sMimeTypeMap.loadEntry("model/iges", "igs", false);
- sMimeTypeMap.loadEntry("model/iges", "iges", false);
- sMimeTypeMap.loadEntry("model/mesh", "msh", false);
- sMimeTypeMap.loadEntry("model/mesh", "mesh", false);
- sMimeTypeMap.loadEntry("model/mesh", "silo", false);
- sMimeTypeMap.loadEntry("text/calendar", "ics", true);
- sMimeTypeMap.loadEntry("text/calendar", "icz", true);
- sMimeTypeMap.loadEntry("text/comma-separated-values", "csv", true);
- sMimeTypeMap.loadEntry("text/css", "css", true);
- sMimeTypeMap.loadEntry("text/h323", "323", true);
- sMimeTypeMap.loadEntry("text/iuls", "uls", true);
- sMimeTypeMap.loadEntry("text/mathml", "mml", true);
+ "application/vnd.sun.xml.writer.template", "stw");
+ sMimeTypeMap.loadEntry("application/vnd.visio", "vsd");
+ sMimeTypeMap.loadEntry("application/x-abiword", "abw");
+ sMimeTypeMap.loadEntry("application/x-apple-diskimage", "dmg");
+ sMimeTypeMap.loadEntry("application/x-bcpio", "bcpio");
+ sMimeTypeMap.loadEntry("application/x-bittorrent", "torrent");
+ sMimeTypeMap.loadEntry("application/x-cdf", "cdf");
+ sMimeTypeMap.loadEntry("application/x-cdlink", "vcd");
+ sMimeTypeMap.loadEntry("application/x-chess-pgn", "pgn");
+ sMimeTypeMap.loadEntry("application/x-cpio", "cpio");
+ sMimeTypeMap.loadEntry("application/x-debian-package", "deb");
+ sMimeTypeMap.loadEntry("application/x-debian-package", "udeb");
+ sMimeTypeMap.loadEntry("application/x-director", "dcr");
+ sMimeTypeMap.loadEntry("application/x-director", "dir");
+ sMimeTypeMap.loadEntry("application/x-director", "dxr");
+ sMimeTypeMap.loadEntry("application/x-dms", "dms");
+ sMimeTypeMap.loadEntry("application/x-doom", "wad");
+ sMimeTypeMap.loadEntry("application/x-dvi", "dvi");
+ sMimeTypeMap.loadEntry("application/x-flac", "flac");
+ sMimeTypeMap.loadEntry("application/x-font", "pfa");
+ sMimeTypeMap.loadEntry("application/x-font", "pfb");
+ sMimeTypeMap.loadEntry("application/x-font", "gsf");
+ sMimeTypeMap.loadEntry("application/x-font", "pcf");
+ sMimeTypeMap.loadEntry("application/x-font", "pcf.Z");
+ sMimeTypeMap.loadEntry("application/x-freemind", "mm");
+ sMimeTypeMap.loadEntry("application/x-futuresplash", "spl");
+ sMimeTypeMap.loadEntry("application/x-gnumeric", "gnumeric");
+ sMimeTypeMap.loadEntry("application/x-go-sgf", "sgf");
+ sMimeTypeMap.loadEntry("application/x-graphing-calculator", "gcf");
+ sMimeTypeMap.loadEntry("application/x-gtar", "gtar");
+ sMimeTypeMap.loadEntry("application/x-gtar", "tgz");
+ sMimeTypeMap.loadEntry("application/x-gtar", "taz");
+ sMimeTypeMap.loadEntry("application/x-hdf", "hdf");
+ sMimeTypeMap.loadEntry("application/x-ica", "ica");
+ sMimeTypeMap.loadEntry("application/x-internet-signup", "ins");
+ sMimeTypeMap.loadEntry("application/x-internet-signup", "isp");
+ sMimeTypeMap.loadEntry("application/x-iphone", "iii");
+ sMimeTypeMap.loadEntry("application/x-iso9660-image", "iso");
+ sMimeTypeMap.loadEntry("application/x-jmol", "jmz");
+ sMimeTypeMap.loadEntry("application/x-kchart", "chrt");
+ sMimeTypeMap.loadEntry("application/x-killustrator", "kil");
+ sMimeTypeMap.loadEntry("application/x-koan", "skp");
+ sMimeTypeMap.loadEntry("application/x-koan", "skd");
+ sMimeTypeMap.loadEntry("application/x-koan", "skt");
+ sMimeTypeMap.loadEntry("application/x-koan", "skm");
+ sMimeTypeMap.loadEntry("application/x-kpresenter", "kpr");
+ sMimeTypeMap.loadEntry("application/x-kpresenter", "kpt");
+ sMimeTypeMap.loadEntry("application/x-kspread", "ksp");
+ sMimeTypeMap.loadEntry("application/x-kword", "kwd");
+ sMimeTypeMap.loadEntry("application/x-kword", "kwt");
+ sMimeTypeMap.loadEntry("application/x-latex", "latex");
+ sMimeTypeMap.loadEntry("application/x-lha", "lha");
+ sMimeTypeMap.loadEntry("application/x-lzh", "lzh");
+ sMimeTypeMap.loadEntry("application/x-lzx", "lzx");
+ sMimeTypeMap.loadEntry("application/x-maker", "frm");
+ sMimeTypeMap.loadEntry("application/x-maker", "maker");
+ sMimeTypeMap.loadEntry("application/x-maker", "frame");
+ sMimeTypeMap.loadEntry("application/x-maker", "fb");
+ sMimeTypeMap.loadEntry("application/x-maker", "book");
+ sMimeTypeMap.loadEntry("application/x-maker", "fbdoc");
+ sMimeTypeMap.loadEntry("application/x-mif", "mif");
+ sMimeTypeMap.loadEntry("application/x-ms-wmd", "wmd");
+ sMimeTypeMap.loadEntry("application/x-ms-wmz", "wmz");
+ sMimeTypeMap.loadEntry("application/x-msi", "msi");
+ sMimeTypeMap.loadEntry("application/x-ns-proxy-autoconfig", "pac");
+ sMimeTypeMap.loadEntry("application/x-nwc", "nwc");
+ sMimeTypeMap.loadEntry("application/x-object", "o");
+ sMimeTypeMap.loadEntry("application/x-oz-application", "oza");
+ sMimeTypeMap.loadEntry("application/x-pkcs12", "p12");
+ sMimeTypeMap.loadEntry("application/x-pkcs7-certreqresp", "p7r");
+ sMimeTypeMap.loadEntry("application/x-pkcs7-crl", "crl");
+ sMimeTypeMap.loadEntry("application/x-quicktimeplayer", "qtl");
+ sMimeTypeMap.loadEntry("application/x-shar", "shar");
+ sMimeTypeMap.loadEntry("application/x-stuffit", "sit");
+ sMimeTypeMap.loadEntry("application/x-sv4cpio", "sv4cpio");
+ sMimeTypeMap.loadEntry("application/x-sv4crc", "sv4crc");
+ sMimeTypeMap.loadEntry("application/x-tar", "tar");
+ sMimeTypeMap.loadEntry("application/x-texinfo", "texinfo");
+ sMimeTypeMap.loadEntry("application/x-texinfo", "texi");
+ sMimeTypeMap.loadEntry("application/x-troff", "t");
+ sMimeTypeMap.loadEntry("application/x-troff", "roff");
+ sMimeTypeMap.loadEntry("application/x-troff-man", "man");
+ sMimeTypeMap.loadEntry("application/x-ustar", "ustar");
+ sMimeTypeMap.loadEntry("application/x-wais-source", "src");
+ sMimeTypeMap.loadEntry("application/x-wingz", "wz");
+ sMimeTypeMap.loadEntry("application/x-webarchive", "webarchive");
+ sMimeTypeMap.loadEntry("application/x-x509-ca-cert", "crt");
+ sMimeTypeMap.loadEntry("application/x-xcf", "xcf");
+ sMimeTypeMap.loadEntry("application/x-xfig", "fig");
+ sMimeTypeMap.loadEntry("application/xhtml+xml", "xhtml");
+ sMimeTypeMap.loadEntry("audio/basic", "snd");
+ sMimeTypeMap.loadEntry("audio/midi", "mid");
+ sMimeTypeMap.loadEntry("audio/midi", "midi");
+ sMimeTypeMap.loadEntry("audio/midi", "kar");
+ sMimeTypeMap.loadEntry("audio/mpeg", "mpga");
+ sMimeTypeMap.loadEntry("audio/mpeg", "mpega");
+ sMimeTypeMap.loadEntry("audio/mpeg", "mp2");
+ sMimeTypeMap.loadEntry("audio/mpeg", "mp3");
+ sMimeTypeMap.loadEntry("audio/mpeg", "m4a");
+ sMimeTypeMap.loadEntry("audio/mpegurl", "m3u");
+ sMimeTypeMap.loadEntry("audio/prs.sid", "sid");
+ sMimeTypeMap.loadEntry("audio/x-aiff", "aif");
+ sMimeTypeMap.loadEntry("audio/x-aiff", "aiff");
+ sMimeTypeMap.loadEntry("audio/x-aiff", "aifc");
+ sMimeTypeMap.loadEntry("audio/x-gsm", "gsm");
+ sMimeTypeMap.loadEntry("audio/x-mpegurl", "m3u");
+ sMimeTypeMap.loadEntry("audio/x-ms-wma", "wma");
+ sMimeTypeMap.loadEntry("audio/x-ms-wax", "wax");
+ sMimeTypeMap.loadEntry("audio/x-pn-realaudio", "ra");
+ sMimeTypeMap.loadEntry("audio/x-pn-realaudio", "rm");
+ sMimeTypeMap.loadEntry("audio/x-pn-realaudio", "ram");
+ sMimeTypeMap.loadEntry("audio/x-realaudio", "ra");
+ sMimeTypeMap.loadEntry("audio/x-scpls", "pls");
+ sMimeTypeMap.loadEntry("audio/x-sd2", "sd2");
+ sMimeTypeMap.loadEntry("audio/x-wav", "wav");
+ sMimeTypeMap.loadEntry("image/bmp", "bmp");
+ sMimeTypeMap.loadEntry("image/gif", "gif");
+ sMimeTypeMap.loadEntry("image/ico", "cur");
+ sMimeTypeMap.loadEntry("image/ico", "ico");
+ sMimeTypeMap.loadEntry("image/ief", "ief");
+ sMimeTypeMap.loadEntry("image/jpeg", "jpeg");
+ sMimeTypeMap.loadEntry("image/jpeg", "jpg");
+ sMimeTypeMap.loadEntry("image/jpeg", "jpe");
+ sMimeTypeMap.loadEntry("image/pcx", "pcx");
+ sMimeTypeMap.loadEntry("image/png", "png");
+ sMimeTypeMap.loadEntry("image/svg+xml", "svg");
+ sMimeTypeMap.loadEntry("image/svg+xml", "svgz");
+ sMimeTypeMap.loadEntry("image/tiff", "tiff");
+ sMimeTypeMap.loadEntry("image/tiff", "tif");
+ sMimeTypeMap.loadEntry("image/vnd.djvu", "djvu");
+ sMimeTypeMap.loadEntry("image/vnd.djvu", "djv");
+ sMimeTypeMap.loadEntry("image/vnd.wap.wbmp", "wbmp");
+ sMimeTypeMap.loadEntry("image/x-cmu-raster", "ras");
+ sMimeTypeMap.loadEntry("image/x-coreldraw", "cdr");
+ sMimeTypeMap.loadEntry("image/x-coreldrawpattern", "pat");
+ sMimeTypeMap.loadEntry("image/x-coreldrawtemplate", "cdt");
+ sMimeTypeMap.loadEntry("image/x-corelphotopaint", "cpt");
+ sMimeTypeMap.loadEntry("image/x-icon", "ico");
+ sMimeTypeMap.loadEntry("image/x-jg", "art");
+ sMimeTypeMap.loadEntry("image/x-jng", "jng");
+ sMimeTypeMap.loadEntry("image/x-ms-bmp", "bmp");
+ sMimeTypeMap.loadEntry("image/x-photoshop", "psd");
+ sMimeTypeMap.loadEntry("image/x-portable-anymap", "pnm");
+ sMimeTypeMap.loadEntry("image/x-portable-bitmap", "pbm");
+ sMimeTypeMap.loadEntry("image/x-portable-graymap", "pgm");
+ sMimeTypeMap.loadEntry("image/x-portable-pixmap", "ppm");
+ sMimeTypeMap.loadEntry("image/x-rgb", "rgb");
+ sMimeTypeMap.loadEntry("image/x-xbitmap", "xbm");
+ sMimeTypeMap.loadEntry("image/x-xpixmap", "xpm");
+ sMimeTypeMap.loadEntry("image/x-xwindowdump", "xwd");
+ sMimeTypeMap.loadEntry("model/iges", "igs");
+ sMimeTypeMap.loadEntry("model/iges", "iges");
+ sMimeTypeMap.loadEntry("model/mesh", "msh");
+ sMimeTypeMap.loadEntry("model/mesh", "mesh");
+ sMimeTypeMap.loadEntry("model/mesh", "silo");
+ sMimeTypeMap.loadEntry("text/calendar", "ics");
+ sMimeTypeMap.loadEntry("text/calendar", "icz");
+ sMimeTypeMap.loadEntry("text/comma-separated-values", "csv");
+ sMimeTypeMap.loadEntry("text/css", "css");
+ sMimeTypeMap.loadEntry("text/h323", "323");
+ sMimeTypeMap.loadEntry("text/iuls", "uls");
+ sMimeTypeMap.loadEntry("text/mathml", "mml");
// add it first so it will be the default for ExtensionFromMimeType
- sMimeTypeMap.loadEntry("text/plain", "txt", true);
- sMimeTypeMap.loadEntry("text/plain", "asc", true);
- sMimeTypeMap.loadEntry("text/plain", "text", true);
- sMimeTypeMap.loadEntry("text/plain", "diff", true);
- sMimeTypeMap.loadEntry("text/plain", "pot", true);
- sMimeTypeMap.loadEntry("text/richtext", "rtx", true);
- sMimeTypeMap.loadEntry("text/rtf", "rtf", true);
- sMimeTypeMap.loadEntry("text/texmacs", "ts", true);
- sMimeTypeMap.loadEntry("text/text", "phps", true);
- sMimeTypeMap.loadEntry("text/tab-separated-values", "tsv", true);
- sMimeTypeMap.loadEntry("text/x-bibtex", "bib", true);
- sMimeTypeMap.loadEntry("text/x-boo", "boo", true);
- sMimeTypeMap.loadEntry("text/x-c++hdr", "h++", true);
- sMimeTypeMap.loadEntry("text/x-c++hdr", "hpp", true);
- sMimeTypeMap.loadEntry("text/x-c++hdr", "hxx", true);
- sMimeTypeMap.loadEntry("text/x-c++hdr", "hh", true);
- sMimeTypeMap.loadEntry("text/x-c++src", "c++", true);
- sMimeTypeMap.loadEntry("text/x-c++src", "cpp", true);
- sMimeTypeMap.loadEntry("text/x-c++src", "cxx", true);
- sMimeTypeMap.loadEntry("text/x-chdr", "h", true);
- sMimeTypeMap.loadEntry("text/x-component", "htc", true);
- sMimeTypeMap.loadEntry("text/x-csh", "csh", true);
- sMimeTypeMap.loadEntry("text/x-csrc", "c", true);
- sMimeTypeMap.loadEntry("text/x-dsrc", "d", true);
- sMimeTypeMap.loadEntry("text/x-haskell", "hs", true);
- sMimeTypeMap.loadEntry("text/x-java", "java", true);
- sMimeTypeMap.loadEntry("text/x-literate-haskell", "lhs", true);
- sMimeTypeMap.loadEntry("text/x-moc", "moc", true);
- sMimeTypeMap.loadEntry("text/x-pascal", "p", true);
- sMimeTypeMap.loadEntry("text/x-pascal", "pas", true);
- sMimeTypeMap.loadEntry("text/x-pcs-gcd", "gcd", true);
- sMimeTypeMap.loadEntry("text/x-setext", "etx", true);
- sMimeTypeMap.loadEntry("text/x-tcl", "tcl", true);
- sMimeTypeMap.loadEntry("text/x-tex", "tex", true);
- sMimeTypeMap.loadEntry("text/x-tex", "ltx", true);
- sMimeTypeMap.loadEntry("text/x-tex", "sty", true);
- sMimeTypeMap.loadEntry("text/x-tex", "cls", true);
- sMimeTypeMap.loadEntry("text/x-vcalendar", "vcs", true);
- sMimeTypeMap.loadEntry("text/x-vcard", "vcf", true);
- sMimeTypeMap.loadEntry("video/3gpp", "3gp", false);
- sMimeTypeMap.loadEntry("video/3gpp", "3g2", false);
- sMimeTypeMap.loadEntry("video/dl", "dl", false);
- sMimeTypeMap.loadEntry("video/dv", "dif", false);
- sMimeTypeMap.loadEntry("video/dv", "dv", false);
- sMimeTypeMap.loadEntry("video/fli", "fli", false);
- sMimeTypeMap.loadEntry("video/mpeg", "mpeg", false);
- sMimeTypeMap.loadEntry("video/mpeg", "mpg", false);
- sMimeTypeMap.loadEntry("video/mpeg", "mpe", false);
- sMimeTypeMap.loadEntry("video/mp4", "mp4", false);
- sMimeTypeMap.loadEntry("video/mpeg", "VOB", false);
- sMimeTypeMap.loadEntry("video/quicktime", "qt", false);
- sMimeTypeMap.loadEntry("video/quicktime", "mov", false);
- sMimeTypeMap.loadEntry("video/vnd.mpegurl", "mxu", false);
- sMimeTypeMap.loadEntry("video/x-la-asf", "lsf", false);
- sMimeTypeMap.loadEntry("video/x-la-asf", "lsx", false);
- sMimeTypeMap.loadEntry("video/x-mng", "mng", false);
- sMimeTypeMap.loadEntry("video/x-ms-asf", "asf", false);
- sMimeTypeMap.loadEntry("video/x-ms-asf", "asx", false);
- sMimeTypeMap.loadEntry("video/x-ms-wm", "wm", false);
- sMimeTypeMap.loadEntry("video/x-ms-wmv", "wmv", false);
- sMimeTypeMap.loadEntry("video/x-ms-wmx", "wmx", false);
- sMimeTypeMap.loadEntry("video/x-ms-wvx", "wvx", false);
- sMimeTypeMap.loadEntry("video/x-msvideo", "avi", false);
- sMimeTypeMap.loadEntry("video/x-sgi-movie", "movie", false);
- sMimeTypeMap.loadEntry("x-conference/x-cooltalk", "ice", false);
- sMimeTypeMap.loadEntry("x-epoc/x-sisx-app", "sisx", false);
+ sMimeTypeMap.loadEntry("text/plain", "txt");
+ sMimeTypeMap.loadEntry("text/plain", "asc");
+ sMimeTypeMap.loadEntry("text/plain", "text");
+ sMimeTypeMap.loadEntry("text/plain", "diff");
+ sMimeTypeMap.loadEntry("text/plain", "pot");
+ sMimeTypeMap.loadEntry("text/richtext", "rtx");
+ sMimeTypeMap.loadEntry("text/rtf", "rtf");
+ sMimeTypeMap.loadEntry("text/texmacs", "ts");
+ sMimeTypeMap.loadEntry("text/text", "phps");
+ sMimeTypeMap.loadEntry("text/tab-separated-values", "tsv");
+ sMimeTypeMap.loadEntry("text/x-bibtex", "bib");
+ sMimeTypeMap.loadEntry("text/x-boo", "boo");
+ sMimeTypeMap.loadEntry("text/x-c++hdr", "h++");
+ sMimeTypeMap.loadEntry("text/x-c++hdr", "hpp");
+ sMimeTypeMap.loadEntry("text/x-c++hdr", "hxx");
+ sMimeTypeMap.loadEntry("text/x-c++hdr", "hh");
+ sMimeTypeMap.loadEntry("text/x-c++src", "c++");
+ sMimeTypeMap.loadEntry("text/x-c++src", "cpp");
+ sMimeTypeMap.loadEntry("text/x-c++src", "cxx");
+ sMimeTypeMap.loadEntry("text/x-chdr", "h");
+ sMimeTypeMap.loadEntry("text/x-component", "htc");
+ sMimeTypeMap.loadEntry("text/x-csh", "csh");
+ sMimeTypeMap.loadEntry("text/x-csrc", "c");
+ sMimeTypeMap.loadEntry("text/x-dsrc", "d");
+ sMimeTypeMap.loadEntry("text/x-haskell", "hs");
+ sMimeTypeMap.loadEntry("text/x-java", "java");
+ sMimeTypeMap.loadEntry("text/x-literate-haskell", "lhs");
+ sMimeTypeMap.loadEntry("text/x-moc", "moc");
+ sMimeTypeMap.loadEntry("text/x-pascal", "p");
+ sMimeTypeMap.loadEntry("text/x-pascal", "pas");
+ sMimeTypeMap.loadEntry("text/x-pcs-gcd", "gcd");
+ sMimeTypeMap.loadEntry("text/x-setext", "etx");
+ sMimeTypeMap.loadEntry("text/x-tcl", "tcl");
+ sMimeTypeMap.loadEntry("text/x-tex", "tex");
+ sMimeTypeMap.loadEntry("text/x-tex", "ltx");
+ sMimeTypeMap.loadEntry("text/x-tex", "sty");
+ sMimeTypeMap.loadEntry("text/x-tex", "cls");
+ sMimeTypeMap.loadEntry("text/x-vcalendar", "vcs");
+ sMimeTypeMap.loadEntry("text/x-vcard", "vcf");
+ sMimeTypeMap.loadEntry("video/3gpp", "3gp");
+ sMimeTypeMap.loadEntry("video/3gpp", "3g2");
+ sMimeTypeMap.loadEntry("video/dl", "dl");
+ sMimeTypeMap.loadEntry("video/dv", "dif");
+ sMimeTypeMap.loadEntry("video/dv", "dv");
+ sMimeTypeMap.loadEntry("video/fli", "fli");
+ sMimeTypeMap.loadEntry("video/mpeg", "mpeg");
+ sMimeTypeMap.loadEntry("video/mpeg", "mpg");
+ sMimeTypeMap.loadEntry("video/mpeg", "mpe");
+ sMimeTypeMap.loadEntry("video/mp4", "mp4");
+ sMimeTypeMap.loadEntry("video/mpeg", "VOB");
+ sMimeTypeMap.loadEntry("video/quicktime", "qt");
+ sMimeTypeMap.loadEntry("video/quicktime", "mov");
+ sMimeTypeMap.loadEntry("video/vnd.mpegurl", "mxu");
+ sMimeTypeMap.loadEntry("video/x-la-asf", "lsf");
+ sMimeTypeMap.loadEntry("video/x-la-asf", "lsx");
+ sMimeTypeMap.loadEntry("video/x-mng", "mng");
+ sMimeTypeMap.loadEntry("video/x-ms-asf", "asf");
+ sMimeTypeMap.loadEntry("video/x-ms-asf", "asx");
+ sMimeTypeMap.loadEntry("video/x-ms-wm", "wm");
+ sMimeTypeMap.loadEntry("video/x-ms-wmv", "wmv");
+ sMimeTypeMap.loadEntry("video/x-ms-wmx", "wmx");
+ sMimeTypeMap.loadEntry("video/x-ms-wvx", "wvx");
+ sMimeTypeMap.loadEntry("video/x-msvideo", "avi");
+ sMimeTypeMap.loadEntry("video/x-sgi-movie", "movie");
+ sMimeTypeMap.loadEntry("x-conference/x-cooltalk", "ice");
+ sMimeTypeMap.loadEntry("x-epoc/x-sisx-app", "sisx");
}
return sMimeTypeMap;
diff --git a/core/java/android/webkit/Network.java b/core/java/android/webkit/Network.java
index 8c2b09b1a7a1..fb601098bd5a 100644
--- a/core/java/android/webkit/Network.java
+++ b/core/java/android/webkit/Network.java
@@ -149,14 +149,12 @@ class Network {
* @param headers The http headers.
* @param postData The body of the request.
* @param loader A LoadListener for receiving the results of the request.
- * @param isHighPriority True if this is high priority request.
* @return True if the request was successfully queued.
*/
public boolean requestURL(String method,
Map<String, String> headers,
byte [] postData,
- LoadListener loader,
- boolean isHighPriority) {
+ LoadListener loader) {
String url = loader.url();
@@ -188,7 +186,7 @@ class Network {
RequestHandle handle = q.queueRequest(
url, loader.getWebAddress(), method, headers, loader,
- bodyProvider, bodyLength, isHighPriority);
+ bodyProvider, bodyLength);
loader.attachRequestHandle(handle);
if (loader.isSynchronous()) {
diff --git a/core/java/android/webkit/URLUtil.java b/core/java/android/webkit/URLUtil.java
index de70fc2d2b65..1d1828999305 100644
--- a/core/java/android/webkit/URLUtil.java
+++ b/core/java/android/webkit/URLUtil.java
@@ -348,7 +348,7 @@ public final class URLUtil {
* This header provides a filename for content that is going to be
* downloaded to the file system. We only support the attachment type.
*/
- private static String parseContentDisposition(String contentDisposition) {
+ static String parseContentDisposition(String contentDisposition) {
try {
Matcher m = CONTENT_DISPOSITION_PATTERN.matcher(contentDisposition);
if (m.find()) {
diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java
index 23c7f7d6651d..da1443c6db3e 100644
--- a/core/java/android/webkit/WebTextView.java
+++ b/core/java/android/webkit/WebTextView.java
@@ -17,13 +17,22 @@
package android.webkit;
import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.text.Editable;
import android.text.InputFilter;
import android.text.Selection;
import android.text.Spannable;
+import android.text.TextPaint;
import android.text.TextUtils;
import android.text.method.MovementMethod;
import android.util.Log;
+import android.view.Gravity;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -61,6 +70,7 @@ import java.util.ArrayList;
// Keep track of the text before the change so we know whether we actually
// need to send down the DOM events.
private String mPreChange;
+ private Drawable mBackground;
// Array to store the final character added in onTextChanged, so that its
// KeyEvents may be determined.
private char[] mCharacter = new char[1];
@@ -80,9 +90,6 @@ import java.util.ArrayList;
mWebView = webView;
mMaxLength = -1;
setImeOptions(EditorInfo.IME_ACTION_NONE);
- // Allow webkit's drawing to show through
- setWillNotDraw(true);
- setCursorVisible(false);
}
@Override
@@ -111,7 +118,11 @@ import java.util.ArrayList;
if (!isArrowKey && mWebView.nativeFocusNodePointer() != mNodePointer) {
mWebView.nativeClearCursor();
- remove();
+ // Do not call remove() here, which hides the soft keyboard. If
+ // the soft keyboard is being displayed, the user will still want
+ // it there.
+ mWebView.removeView(this);
+ mWebView.requestFocus();
return mWebView.dispatchKeyEvent(event);
}
@@ -456,7 +467,65 @@ import java.util.ArrayList;
if (inPassword) {
setInputType(EditorInfo.TYPE_CLASS_TEXT | EditorInfo.
TYPE_TEXT_VARIATION_PASSWORD);
+ createBackground();
+ }
+ // For password fields, draw the WebTextView. For others, just show
+ // webkit's drawing.
+ setWillNotDraw(!inPassword);
+ setBackgroundDrawable(inPassword ? mBackground : null);
+ // For non-password fields, avoid the invals from TextView's blinking
+ // cursor
+ setCursorVisible(inPassword);
+ }
+
+ /**
+ * Private class used for the background of a password textfield.
+ */
+ private static class OutlineDrawable extends Drawable {
+ public void draw(Canvas canvas) {
+ Rect bounds = getBounds();
+ Paint paint = new Paint();
+ paint.setAntiAlias(true);
+ // Draw the background.
+ paint.setColor(Color.WHITE);
+ canvas.drawRect(bounds, paint);
+ // Draw the outline.
+ paint.setStyle(Paint.Style.STROKE);
+ paint.setColor(Color.BLACK);
+ canvas.drawRect(bounds, paint);
+ }
+ // Always want it to be opaque.
+ public int getOpacity() {
+ return PixelFormat.OPAQUE;
+ }
+ // These are needed because they are abstract in Drawable.
+ public void setAlpha(int alpha) { }
+ public void setColorFilter(ColorFilter cf) { }
+ }
+
+ /**
+ * Create a background for the WebTextView and set up the paint for drawing
+ * the text. This way, we can see the password transformation of the
+ * system, which (optionally) shows the actual text before changing to dots.
+ * The background is necessary to hide the webkit-drawn text beneath.
+ */
+ private void createBackground() {
+ if (mBackground != null) {
+ return;
}
+ mBackground = new OutlineDrawable();
+
+ setGravity(Gravity.CENTER_VERTICAL);
+ // Turn on subpixel text, and turn off kerning, so it better matches
+ // the text in webkit.
+ TextPaint paint = getPaint();
+ int flags = paint.getFlags() | Paint.SUBPIXEL_TEXT_FLAG |
+ Paint.ANTI_ALIAS_FLAG & ~Paint.DEV_KERN_TEXT_FLAG;
+ paint.setFlags(flags);
+ // Set the text color to black, regardless of the theme. This ensures
+ // that other applications that use embedded WebViews will properly
+ // display the text in password textfields.
+ setTextColor(Color.BLACK);
}
/* package */ void setMaxLength(int maxLength) {
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 899d63695ed2..c1ba690cf9d2 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1235,6 +1235,9 @@ public class WebView extends AbsoluteLayout
* @param url The url of the resource to load.
*/
public void loadUrl(String url) {
+ if (url == null) {
+ return;
+ }
switchOutDrawHistory();
mWebViewCore.sendMessage(EventHub.LOAD_URL, url);
clearTextEntry();
@@ -4915,6 +4918,11 @@ public class WebView extends AbsoluteLayout
}
}
setNewZoomScale(scale, false);
+ // As we are on a new page, remove the WebTextView. This
+ // is necessary for page loads driven by webkit, and in
+ // particular when the user was on a password field, so
+ // the WebTextView was visible.
+ clearTextEntry();
break;
case MOVE_OUT_OF_PLUGIN:
if (nativePluginEatsNavKey()) {
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 20f72396a9d1..99ceec276f49 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -32,6 +32,8 @@ import android.os.Process;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.KeyEvent;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
import java.util.ArrayList;
@@ -1826,6 +1828,76 @@ final class WebViewCore {
}
}
+ // This class looks like a SurfaceView to native code. In java, we can
+ // assume the passed in SurfaceView is this class so we can talk to the
+ // ViewManager through the ChildView.
+ private class SurfaceViewProxy extends SurfaceView
+ implements SurfaceHolder.Callback {
+ private final ViewManager.ChildView mChildView;
+ private int mPointer;
+ SurfaceViewProxy(Context context, ViewManager.ChildView childView,
+ int pointer) {
+ super(context);
+ setWillNotDraw(false); // this prevents the black box artifact
+ getHolder().addCallback(this);
+ mChildView = childView;
+ mChildView.mView = this;
+ mPointer = pointer;
+ }
+ void destroy() {
+ mPointer = 0;
+ mChildView.removeView();
+ }
+ void attach(int x, int y, int width, int height) {
+ mChildView.attachView(x, y, width, height);
+ }
+
+ // SurfaceHolder.Callback methods
+ public void surfaceCreated(SurfaceHolder holder) {
+ if (mPointer != 0) {
+ nativeSurfaceChanged(mPointer, 0, 0, 0, 0);
+ }
+ }
+ public void surfaceChanged(SurfaceHolder holder, int format, int width,
+ int height) {
+ if (mPointer != 0) {
+ nativeSurfaceChanged(mPointer, 1, format, width, height);
+ }
+ }
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ if (mPointer != 0) {
+ nativeSurfaceChanged(mPointer, 2, 0, 0, 0);
+ }
+ }
+ }
+
+ // PluginWidget functions for mainting SurfaceViews for the Surface drawing
+ // model.
+ private SurfaceView createSurface(int nativePointer) {
+ if (mWebView == null) {
+ return null;
+ }
+ return new SurfaceViewProxy(mContext,
+ mWebView.mViewManager.createView(), nativePointer);
+ }
+
+ private void destroySurface(SurfaceView surface) {
+ SurfaceViewProxy proxy = (SurfaceViewProxy) surface;
+ proxy.destroy();
+ }
+
+ private void attachSurface(SurfaceView surface, int x, int y,
+ int width, int height) {
+ SurfaceViewProxy proxy = (SurfaceViewProxy) surface;
+ proxy.attach(x, y, width, height);
+ }
+
+ // Callback for the SurfaceHolder.Callback. Called for all the surface
+ // callbacks. The state parameter is one of Created(0), Changed(1),
+ // Destroyed(2).
+ private native void nativeSurfaceChanged(int pointer, int state, int format,
+ int width, int height);
+
private native void nativePause();
private native void nativeResume();
private native void nativeFreeMemory();
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index f9ca8cb7a43b..777beed7ed13 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -720,7 +720,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
@Override
public void getFocusedRect(Rect r) {
View view = getSelectedView();
- if (view != null) {
+ if (view != null && view.getParent() == this) {
// the focused rectangle of the selected view offset into the
// coordinate space of this view.
view.getFocusedRect(r);
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index be3dd1992439..09a547ff5e11 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -31,6 +31,7 @@ import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.WindowManager;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.EditorInfo;
@@ -141,6 +142,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
mPopup = new PopupWindow(context, attrs,
com.android.internal.R.attr.autoCompleteTextViewStyle);
+ mPopup.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
TypedArray a =
context.obtainStyledAttributes(
@@ -208,8 +210,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
if (mDropDownAlwaysVisible
&& mPopup.isShowing()
&& mPopup.getInputMethodMode() == PopupWindow.INPUT_METHOD_NOT_NEEDED) {
- mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
- showDropDown();
+ ensureImeVisible();
}
}
@@ -1070,11 +1071,21 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
/**
* Issues a runnable to show the dropdown as soon as possible.
*
- * @hide internal used only by Search Dialog
+ * @hide internal used only by SearchDialog
*/
public void showDropDownAfterLayout() {
post(mShowDropDownRunnable);
}
+
+ /**
+ * Ensures that the drop down is not obscuring the IME.
+ *
+ * @hide internal used only here and SearchDialog
+ */
+ public void ensureImeVisible() {
+ mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
+ showDropDown();
+ }
/**
* <p>Displays the drop down on screen.</p>
@@ -1268,11 +1279,8 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
}
}
- // Max height available on the screen for a popup. If this AutoCompleteTextView has
- // the dropDownAlwaysVisible attribute, and the input method is not currently required,
- // we then we ask for the height ignoring any bottom decorations like the input method.
- // Otherwise we respect the input method.
- boolean ignoreBottomDecorations = mDropDownAlwaysVisible &&
+ // Max height available on the screen for a popup.
+ boolean ignoreBottomDecorations =
mPopup.getInputMethodMode() == PopupWindow.INPUT_METHOD_NOT_NEEDED;
final int maxHeight = mPopup.getMaxAvailableHeight(
getDropDownAnchorView(), mDropDownVerticalOffset, ignoreBottomDecorations);
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 54f27072b4f9..3b9f1de4c66c 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -110,6 +110,8 @@ public class DatePicker extends FrameLayout {
* subtract by one to ensure our internal state is always 0-11
*/
mMonth = newVal - 1;
+ // Adjust max day of the month
+ adjustMaxDay();
if (mOnDateChangedListener != null) {
mOnDateChangedListener.onDateChanged(DatePicker.this, mYear, mMonth, mDay);
}
@@ -121,9 +123,12 @@ public class DatePicker extends FrameLayout {
mYearPicker.setOnChangeListener(new OnChangedListener() {
public void onChanged(NumberPicker picker, int oldVal, int newVal) {
mYear = newVal;
+ // Adjust max day for leap years if needed
+ adjustMaxDay();
if (mOnDateChangedListener != null) {
mOnDateChangedListener.onDateChanged(DatePicker.this, mYear, mMonth, mDay);
}
+ updateDaySpinner();
}
});
@@ -318,4 +323,14 @@ public class DatePicker extends FrameLayout {
public int getDayOfMonth() {
return mDay;
}
+
+ private void adjustMaxDay(){
+ Calendar cal = Calendar.getInstance();
+ cal.set(Calendar.YEAR, mYear);
+ cal.set(Calendar.MONTH, mMonth);
+ int max = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
+ if (mDay > max) {
+ mDay = max;
+ }
+ }
}
diff --git a/core/java/android/widget/Filter.java b/core/java/android/widget/Filter.java
index 7e55c78a39b3..d90154027327 100644
--- a/core/java/android/widget/Filter.java
+++ b/core/java/android/widget/Filter.java
@@ -46,6 +46,8 @@ public abstract class Filter {
private Handler mThreadHandler;
private Handler mResultHandler;
+ private Delayer mDelayer;
+
private final Object mLock = new Object();
/**
@@ -56,6 +58,20 @@ public abstract class Filter {
}
/**
+ * Provide an interface that decides how long to delay the message for a given query. Useful
+ * for heuristics such as posting a delay for the delete key to avoid doing any work while the
+ * user holds down the delete key.
+ *
+ * @param delayer The delayer.
+ * @hide
+ */
+ public void setDelayer(Delayer delayer) {
+ synchronized (mLock) {
+ mDelayer = delayer;
+ }
+ }
+
+ /**
* <p>Starts an asynchronous filtering operation. Calling this method
* cancels all previous non-executed filtering requests and posts a new
* filtering request that will be executed later.</p>
@@ -85,10 +101,13 @@ public abstract class Filter {
public final void filter(CharSequence constraint, FilterListener listener) {
synchronized (mLock) {
if (mThreadHandler == null) {
- HandlerThread thread = new HandlerThread(THREAD_NAME);
+ HandlerThread thread = new HandlerThread(
+ THREAD_NAME, android.os.Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
mThreadHandler = new RequestHandler(thread.getLooper());
}
+
+ final long delay = (mDelayer == null) ? 0 : mDelayer.getPostingDelay(constraint);
Message message = mThreadHandler.obtainMessage(FILTER_TOKEN);
@@ -101,7 +120,7 @@ public abstract class Filter {
mThreadHandler.removeMessages(FILTER_TOKEN);
mThreadHandler.removeMessages(FINISH_TOKEN);
- mThreadHandler.sendMessage(message);
+ mThreadHandler.sendMessageDelayed(message, delay);
}
}
@@ -288,4 +307,17 @@ public abstract class Filter {
*/
FilterResults results;
}
+
+ /**
+ * @hide
+ */
+ public interface Delayer {
+
+ /**
+ * @param constraint The constraint passed to {@link Filter#filter(CharSequence)}
+ * @return The delay that should be used for
+ * {@link Handler#sendMessageDelayed(android.os.Message, long)}
+ */
+ long getPostingDelay(CharSequence constraint);
+ }
}
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 0c2cd55d2ee3..90fbb77d255e 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -27,7 +27,6 @@ import android.view.WindowManager;
import android.view.Gravity;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
-import android.view.WindowManagerImpl;
import android.view.ViewTreeObserver.OnScrollChangedListener;
import android.view.View.OnTouchListener;
import android.graphics.PixelFormat;
@@ -82,6 +81,7 @@ public class PopupWindow {
private View mPopupView;
private boolean mFocusable;
private int mInputMethodMode = INPUT_METHOD_FROM_FOCUSABLE;
+ private int mSoftInputMode;
private boolean mTouchable = true;
private boolean mOutsideTouchable = false;
private boolean mClippingEnabled = true;
@@ -446,6 +446,30 @@ public class PopupWindow {
public void setInputMethodMode(int mode) {
mInputMethodMode = mode;
}
+
+ /**
+ * Sets the operating mode for the soft input area.
+ *
+ * @param mode The desired mode, see
+ * {@link android.view.WindowManager.LayoutParams#softInputMode}
+ * for the full list
+ *
+ * @see android.view.WindowManager.LayoutParams#softInputMode
+ * @see #getSoftInputMode()
+ */
+ public void setSoftInputMode(int mode) {
+ mSoftInputMode = mode;
+ }
+
+ /**
+ * Returns the current value in {@link #setSoftInputMode(int)}.
+ *
+ * @see #setSoftInputMode(int)
+ * @see android.view.WindowManager.LayoutParams#softInputMode
+ */
+ public int getSoftInputMode() {
+ return mSoftInputMode;
+ }
/**
* <p>Indicates whether the popup window receives touch events.</p>
@@ -822,7 +846,7 @@ public class PopupWindow {
p.flags = computeFlags(p.flags);
p.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
p.token = token;
- p.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+ p.softInputMode = mSoftInputMode;
p.setTitle("PopupWindow:" + Integer.toHexString(hashCode()));
return p;
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index e62dda58ba42..24c0e2a4a2d9 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -1295,11 +1295,8 @@ public class RelativeLayout extends ViewGroup {
if (rule > 0) {
// The node this node depends on
final Node dependency = keyNodes.get(rule);
- if (dependency == node) {
- throw new IllegalStateException("A view cannot have a dependency" +
- " on itself");
- }
- if (dependency == null) {
+ // Skip unknowns and self dependencies
+ if (dependency == null || dependency == node) {
continue;
}
// Add the current node as a dependent
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index 80d688ef2793..bcddca18009e 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -24,7 +24,6 @@ import android.content.DialogInterface.OnClickListener;
import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.util.AttributeSet;
-import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -40,6 +39,7 @@ import android.view.ViewGroup;
public class Spinner extends AbsSpinner implements OnClickListener {
private CharSequence mPrompt;
+ private AlertDialog mPopup;
public Spinner(Context context) {
this(context, null);
@@ -78,6 +78,16 @@ public class Spinner extends AbsSpinner implements OnClickListener {
}
}
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ if (mPopup != null && mPopup.isShowing()) {
+ mPopup.dismiss();
+ mPopup = null;
+ }
+ }
+
/**
* <p>A spinner does not support item click events. Calling this method
* will raise an exception.</p>
@@ -244,7 +254,7 @@ public class Spinner extends AbsSpinner implements OnClickListener {
if (mPrompt != null) {
builder.setTitle(mPrompt);
}
- builder.setSingleChoiceItems(adapter, getSelectedItemPosition(), this).show();
+ mPopup = builder.setSingleChoiceItems(adapter, getSelectedItemPosition(), this).show();
}
return handled;
@@ -253,6 +263,7 @@ public class Spinner extends AbsSpinner implements OnClickListener {
public void onClick(DialogInterface dialog, int which) {
setSelection(which);
dialog.dismiss();
+ mPopup = null;
}
/**
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index a03802dff012..a449e5f00919 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -16,6 +16,7 @@
package com.android.internal.os;
+import android.bluetooth.BluetoothHeadset;
import android.os.BatteryStats;
import android.os.NetStat;
import android.os.Parcel;
@@ -128,7 +129,10 @@ public final class BatteryStatsImpl extends BatteryStats {
boolean mBluetoothOn;
StopwatchTimer mBluetoothOnTimer;
-
+
+ /** Bluetooth headset object */
+ BluetoothHeadset mBtHeadset;
+
/**
* These provide time bases that discount the time the device is plugged
* in to power.
@@ -160,6 +164,9 @@ public final class BatteryStatsImpl extends BatteryStats {
private long mRadioDataUptime;
private long mRadioDataStart;
+ private int mBluetoothPingCount;
+ private int mBluetoothPingStart = -1;
+
/*
* Holds a SamplingTimer associated with each kernel wakelock name being tracked.
*/
@@ -920,14 +927,18 @@ public final class BatteryStatsImpl extends BatteryStats {
dataTransfer[STATS_UNPLUGGED] = currentBytes;
}
- private long getCurrentRadioDataUptimeMs() {
+ /**
+ * Radio uptime in microseconds when transferring data. This value is very approximate.
+ * @return
+ */
+ private long getCurrentRadioDataUptime() {
try {
File awakeTimeFile = new File("/sys/devices/virtual/net/rmnet0/awake_time_ms");
if (!awakeTimeFile.exists()) return 0;
BufferedReader br = new BufferedReader(new FileReader(awakeTimeFile));
String line = br.readLine();
br.close();
- return Long.parseLong(line);
+ return Long.parseLong(line) * 1000;
} catch (NumberFormatException nfe) {
// Nothing
} catch (IOException ioe) {
@@ -936,14 +947,44 @@ public final class BatteryStatsImpl extends BatteryStats {
return 0;
}
+ /**
+ * @deprecated use getRadioDataUptime
+ */
public long getRadioDataUptimeMs() {
+ return getRadioDataUptime() / 1000;
+ }
+
+ /**
+ * Returns the duration that the cell radio was up for data transfers.
+ */
+ public long getRadioDataUptime() {
if (mRadioDataStart == -1) {
return mRadioDataUptime;
} else {
- return getCurrentRadioDataUptimeMs() - mRadioDataStart;
+ return getCurrentRadioDataUptime() - mRadioDataStart;
}
}
+ private int getCurrentBluetoothPingCount() {
+ if (mBtHeadset != null) {
+ return mBtHeadset.getBatteryUsageHint();
+ }
+ return -1;
+ }
+
+ public int getBluetoothPingCount() {
+ if (mBluetoothPingStart == -1) {
+ return mBluetoothPingCount;
+ } else if (mBtHeadset != null) {
+ return getCurrentBluetoothPingCount() - mBluetoothPingStart;
+ }
+ return -1;
+ }
+
+ public void setBtHeadset(BluetoothHeadset headset) {
+ mBtHeadset = headset;
+ }
+
public void doUnplug(long batteryUptime, long batteryRealtime) {
for (int iu = mUidStats.size() - 1; iu >= 0; iu--) {
Uid u = mUidStats.valueAt(iu);
@@ -961,8 +1002,11 @@ public final class BatteryStatsImpl extends BatteryStats {
doDataUnplug(mTotalDataRx, NetStat.getTotalRxBytes());
doDataUnplug(mTotalDataTx, NetStat.getTotalTxBytes());
// Track radio awake time
- mRadioDataStart = getCurrentRadioDataUptimeMs();
+ mRadioDataStart = getCurrentRadioDataUptime();
mRadioDataUptime = 0;
+ // Track bt headset ping count
+ mBluetoothPingStart = getCurrentBluetoothPingCount();
+ mBluetoothPingCount = 0;
}
public void doPlug(long batteryUptime, long batteryRealtime) {
@@ -985,8 +1029,12 @@ public final class BatteryStatsImpl extends BatteryStats {
doDataPlug(mTotalDataRx, NetStat.getTotalRxBytes());
doDataPlug(mTotalDataTx, NetStat.getTotalTxBytes());
// Track radio awake time
- mRadioDataUptime = getRadioDataUptimeMs();
+ mRadioDataUptime = getRadioDataUptime();
mRadioDataStart = -1;
+
+ // Track bt headset ping count
+ mBluetoothPingCount = getBluetoothPingCount();
+ mBluetoothPingStart = -1;
}
public void noteStartGps(int uid) {
@@ -3335,6 +3383,9 @@ public final class BatteryStatsImpl extends BatteryStats {
mRadioDataUptime = in.readLong();
mRadioDataStart = -1;
+ mBluetoothPingCount = in.readInt();
+ mBluetoothPingStart = -1;
+
mKernelWakelockStats.clear();
int NKW = in.readInt();
for (int ikw = 0; ikw < NKW; ikw++) {
@@ -3415,7 +3466,9 @@ public final class BatteryStatsImpl extends BatteryStats {
out.writeLong(getTotalTcpBytesSent(STATS_UNPLUGGED));
// Write radio uptime for data
- out.writeLong(getRadioDataUptimeMs());
+ out.writeLong(getRadioDataUptime());
+
+ out.writeInt(getBluetoothPingCount());
out.writeInt(mKernelWakelockStats.size());
for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) {
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index 4a8d8b182f90..94f703add469 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -87,6 +87,11 @@ public class PowerProfile {
public static final String POWER_BLUETOOTH_ACTIVE = "bluetooth.active";
/**
+ * Power consumption when Bluetooth driver gets an AT command.
+ */
+ public static final String POWER_BLUETOOTH_AT_CMD = "bluetooth.at";
+
+ /**
* Power consumption when screen is on, not including the backlight power.
*/
public static final String POWER_SCREEN_ON = "screen.on";
diff --git a/core/java/com/android/internal/widget/NumberPicker.java b/core/java/com/android/internal/widget/NumberPicker.java
index 2f08c8dd424b..0424ced5c76e 100644
--- a/core/java/com/android/internal/widget/NumberPicker.java
+++ b/core/java/com/android/internal/widget/NumberPicker.java
@@ -243,9 +243,11 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
private void validateCurrentView(CharSequence str) {
int val = getSelectedPos(str.toString());
if ((val >= mStart) && (val <= mEnd)) {
- mPrevious = mCurrent;
- mCurrent = val;
- notifyChange();
+ if (mCurrent != val) {
+ mPrevious = mCurrent;
+ mCurrent = val;
+ notifyChange();
+ }
}
updateView();
}
diff --git a/core/jni/.android_server_BluetoothEventLoop.cpp.swp b/core/jni/.android_server_BluetoothEventLoop.cpp.swp
new file mode 100644
index 000000000000..d36e403a79a8
--- /dev/null
+++ b/core/jni/.android_server_BluetoothEventLoop.cpp.swp
Binary files differ
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index c322b17e5991..63dc9e831360 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -746,6 +746,15 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
opt.optionString = "-Xincludeselectedmethod";
mOptions.add(opt);
}
+
+ /*
+ * Enable profile collection on JIT'ed code.
+ */
+ property_get("dalvik.vm.jit.profile", propBuf, "");
+ if (strlen(propBuf) > 0) {
+ opt.optionString = "-Xjitprofile";
+ mOptions.add(opt);
+ }
#endif
if (executionMode == kEMIntPortable) {
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 957b825c3cc1..002d3db3f55e 100644
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -224,7 +224,7 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,
SkBitmap bitmap;
bitmap.setConfig(config, width, height);
- if (!GraphicsJNI::setJavaPixelRef(env, &bitmap, NULL)) {
+ if (!GraphicsJNI::setJavaPixelRef(env, &bitmap, NULL, true)) {
return NULL;
}
@@ -240,7 +240,7 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,
static jobject Bitmap_copy(JNIEnv* env, jobject, const SkBitmap* src,
SkBitmap::Config dstConfig, jboolean isMutable) {
SkBitmap result;
- JavaPixelAllocator allocator(env);
+ JavaPixelAllocator allocator(env, true);
if (!src->copyTo(&result, dstConfig, &allocator)) {
return NULL;
@@ -356,7 +356,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
}
}
- if (!GraphicsJNI::setJavaPixelRef(env, bitmap, ctable)) {
+ if (!GraphicsJNI::setJavaPixelRef(env, bitmap, ctable, true)) {
ctable->safeUnref();
delete bitmap;
return NULL;
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 137707fa93bf..0c842650962f 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -23,6 +23,7 @@ static jfieldID gOptions_configFieldID;
static jfieldID gOptions_ditherFieldID;
static jfieldID gOptions_purgeableFieldID;
static jfieldID gOptions_shareableFieldID;
+static jfieldID gOptions_nativeAllocFieldID;
static jfieldID gOptions_widthFieldID;
static jfieldID gOptions_heightFieldID;
static jfieldID gOptions_mimeFieldID;
@@ -300,6 +301,11 @@ static bool optionsShareable(JNIEnv* env, jobject options) {
env->GetBooleanField(options, gOptions_shareableFieldID);
}
+static bool optionsReportSizeToVM(JNIEnv* env, jobject options) {
+ return NULL == options ||
+ !env->GetBooleanField(options, gOptions_nativeAllocFieldID);
+}
+
static jobject nullObjectReturn(const char msg[]) {
if (msg) {
SkDebugf("--- %s\n", msg);
@@ -330,6 +336,7 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
bool doDither = true;
bool isPurgeable = allowPurgeable && optionsPurgeable(env, options);
+ bool reportSizeToVM = optionsReportSizeToVM(env, options);
if (NULL != options) {
sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
@@ -355,7 +362,7 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
decoder->setDitherImage(doDither);
NinePatchPeeker peeker;
- JavaPixelAllocator javaAllocator(env);
+ JavaPixelAllocator javaAllocator(env, reportSizeToVM);
SkBitmap* bitmap = new SkBitmap;
Res_png_9patch dummy9Patch;
@@ -699,6 +706,7 @@ int register_android_graphics_BitmapFactory(JNIEnv* env) {
gOptions_ditherFieldID = getFieldIDCheck(env, gOptions_class, "inDither", "Z");
gOptions_purgeableFieldID = getFieldIDCheck(env, gOptions_class, "inPurgeable", "Z");
gOptions_shareableFieldID = getFieldIDCheck(env, gOptions_class, "inInputShareable", "Z");
+ gOptions_nativeAllocFieldID = getFieldIDCheck(env, gOptions_class, "inNativeAlloc", "Z");
gOptions_widthFieldID = getFieldIDCheck(env, gOptions_class, "outWidth", "I");
gOptions_heightFieldID = getFieldIDCheck(env, gOptions_class, "outHeight", "I");
gOptions_mimeFieldID = getFieldIDCheck(env, gOptions_class, "outMimeType", "Ljava/lang/String;");
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 6eebbdcfb563..6e159a8853d5 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -5,6 +5,7 @@
#include "SkRegion.h"
#include <android_runtime/AndroidRuntime.h>
+//#define REPORT_SIZE_TO_JVM
//#define TRACK_LOCK_COUNT
void doThrow(JNIEnv* env, const char* exc, const char* msg) {
@@ -444,7 +445,7 @@ private:
};
bool GraphicsJNI::setJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
- SkColorTable* ctable) {
+ SkColorTable* ctable, bool reportSizeToVM) {
Sk64 size64 = bitmap->getSize64();
if (size64.isNeg() || !size64.is32()) {
doThrow(env, "java/lang/IllegalArgumentException",
@@ -453,35 +454,41 @@ bool GraphicsJNI::setJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
}
size_t size = size64.get32();
- // SkDebugf("-------------- inform VM we've allocated %d bytes\n", size);
jlong jsize = size; // the VM wants longs for the size
- bool r = env->CallBooleanMethod(gVMRuntime_singleton,
- gVMRuntime_trackExternalAllocationMethodID,
- jsize);
- if (GraphicsJNI::hasException(env)) {
- return false;
- }
- if (!r) {
- LOGE("VM won't let us allocate %zd bytes\n", size);
- doThrowOOME(env, "bitmap size exceeds VM budget");
- return false;
+ if (reportSizeToVM) {
+ // SkDebugf("-------------- inform VM we've allocated %d bytes\n", size);
+ bool r = env->CallBooleanMethod(gVMRuntime_singleton,
+ gVMRuntime_trackExternalAllocationMethodID,
+ jsize);
+ if (GraphicsJNI::hasException(env)) {
+ return false;
+ }
+ if (!r) {
+ LOGE("VM won't let us allocate %zd bytes\n", size);
+ doThrowOOME(env, "bitmap size exceeds VM budget");
+ return false;
+ }
}
-
// call the version of malloc that returns null on failure
void* addr = sk_malloc_flags(size, 0);
if (NULL == addr) {
- // SkDebugf("-------------- inform VM we're releasing %d bytes which we couldn't allocate\n", size);
- // we didn't actually allocate it, so inform the VM
- env->CallVoidMethod(gVMRuntime_singleton,
- gVMRuntime_trackExternalFreeMethodID,
- jsize);
- if (!GraphicsJNI::hasException(env)) {
- doThrowOOME(env, "bitmap size too large for malloc");
+ if (reportSizeToVM) {
+ // SkDebugf("-------------- inform VM we're releasing %d bytes which we couldn't allocate\n", size);
+ // we didn't actually allocate it, so inform the VM
+ env->CallVoidMethod(gVMRuntime_singleton,
+ gVMRuntime_trackExternalFreeMethodID,
+ jsize);
+ if (!GraphicsJNI::hasException(env)) {
+ doThrowOOME(env, "bitmap size too large for malloc");
+ }
}
return false;
}
- bitmap->setPixelRef(new AndroidPixelRef(env, addr, size, ctable))->unref();
+ SkPixelRef* pr = reportSizeToVM ?
+ new AndroidPixelRef(env, addr, size, ctable) :
+ new SkMallocPixelRef(addr, size, ctable);
+ bitmap->setPixelRef(pr)->unref();
// since we're already allocated, we lockPixels right away
// HeapAllocator behaves this way too
bitmap->lockPixels();
@@ -490,12 +497,11 @@ bool GraphicsJNI::setJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
///////////////////////////////////////////////////////////////////////////////
-JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env) : fEnv(env)
-{
-}
+JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env, bool reportSizeToVM)
+ : fEnv(env), fReportSizeToVM(reportSizeToVM) {}
bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
- return GraphicsJNI::setJavaPixelRef(fEnv, bitmap, ctable);
+ return GraphicsJNI::setJavaPixelRef(fEnv, bitmap, ctable, fReportSizeToVM);
}
////////////////////////////////////////////////////////////////////////////////
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index e2dc9acf1beb..16925e41ab4f 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -59,7 +59,8 @@ public:
Returns true on success. If it returns false, then it failed, and the
appropriate exception will have been raised.
*/
- static bool setJavaPixelRef(JNIEnv*, SkBitmap*, SkColorTable* ctable);
+ static bool setJavaPixelRef(JNIEnv*, SkBitmap*, SkColorTable* ctable,
+ bool reportSizeToVM);
/** Copy the colors in colors[] to the bitmap, convert to the correct
format along the way.
@@ -71,12 +72,13 @@ public:
class JavaPixelAllocator : public SkBitmap::Allocator {
public:
- JavaPixelAllocator(JNIEnv* env);
+ JavaPixelAllocator(JNIEnv* env, bool reportSizeToVM);
// overrides
virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable);
private:
JNIEnv* fEnv;
+ bool fReportSizeToVM;
};
class AutoJavaFloatArray {
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 6776dce0de24..8a312d935d84 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -171,7 +171,8 @@ void JNICameraContext::postData(int32_t msgType, const sp<IMemory>& dataPtr)
mCameraJObjectWeak, msgType, 0, 0, NULL);
break;
default:
- LOGV("dataCallback(%d, %p)", msgType, dataPtr.get());
+ // TODO: Change to LOGV
+ LOGD("dataCallback(%d, %p)", msgType, dataPtr.get());
copyAndPost(env, dataPtr, msgType);
break;
}
@@ -222,6 +223,8 @@ static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobj
// finalizer is invoked later.
static void android_hardware_Camera_release(JNIEnv *env, jobject thiz)
{
+ // TODO: Change to LOGV
+ LOGD("release camera");
JNICameraContext* context = NULL;
sp<Camera> camera;
{
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index e71e3481862e..44a9e8cbcc58 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -212,8 +212,10 @@ android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
// failure:
native_init_failure:
+ env->DeleteGlobalRef(lpCallbackData->audioRecord_class);
+ env->DeleteGlobalRef(lpCallbackData->audioRecord_ref);
delete lpCallbackData;
-
+
native_track_failure:
delete lpRecorder;
@@ -274,6 +276,8 @@ static void android_media_AudioRecord_finalize(JNIEnv *env, jobject thiz) {
thiz, javaAudioRecordFields.nativeCallbackCookie);
if (lpCookie) {
LOGV("deleting lpCookie: %x\n", (int)lpCookie);
+ env->DeleteGlobalRef(lpCookie->audioRecord_class);
+ env->DeleteGlobalRef(lpCookie->audioRecord_ref);
delete lpCookie;
}
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index cf3ba7f502af..bc7f3f5c5daa 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -75,6 +75,9 @@ class AudioTrackJniStorage {
int mStreamType;
AudioTrackJniStorage() {
+ mCallbackData.audioTrack_class = 0;
+ mCallbackData.audioTrack_ref = 0;
+ mStreamType = AudioSystem::DEFAULT;
}
~AudioTrackJniStorage() {
@@ -318,6 +321,8 @@ native_init_failure:
env->SetIntField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, 0);
native_track_failure:
+ env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_class);
+ env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_ref);
delete lpJniStorage;
env->SetIntField(thiz, javaAudioTrackFields.jniData, 0);
return AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
@@ -415,6 +420,9 @@ static void android_media_AudioTrack_native_finalize(JNIEnv *env, jobject thiz)
AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetIntField(
thiz, javaAudioTrackFields.jniData);
if (pJniStorage) {
+ // delete global refs created in native_setup
+ env->DeleteGlobalRef(pJniStorage->mCallbackData.audioTrack_class);
+ env->DeleteGlobalRef(pJniStorage->mCallbackData.audioTrack_ref);
//LOGV("deleting pJniStorage: %x\n", (int)pJniStorage);
delete pJniStorage;
}
diff --git a/core/jni/android_server_BluetoothDeviceService.cpp b/core/jni/android_server_BluetoothDeviceService.cpp
index b02a19b135a4..444e6287a4d6 100644
--- a/core/jni/android_server_BluetoothDeviceService.cpp
+++ b/core/jni/android_server_BluetoothDeviceService.cpp
@@ -437,6 +437,65 @@ static jint isEnabledNative(JNIEnv *env, jobject object) {
return -1;
}
+static jboolean setPairingConfirmationNative(JNIEnv *env, jobject object,
+ jstring address, bool confirm,
+ int nativeData) {
+#ifdef HAVE_BLUETOOTH
+ LOGV(__FUNCTION__);
+ native_data_t *nat = get_native_data(env, object);
+ if (nat) {
+ DBusMessage *msg = (DBusMessage *)nativeData;
+ DBusMessage *reply;
+ if (confirm) {
+ reply = dbus_message_new_method_return(msg);
+ } else {
+ reply = dbus_message_new_error(msg,
+ "org.bluez.Error.Rejected", "User rejected confirmation");
+ }
+
+ if (!reply) {
+ LOGE("%s: Cannot create message reply to RequestConfirmation to "
+ "D-Bus\n", __FUNCTION__);
+ dbus_message_unref(msg);
+ return JNI_FALSE;
+ }
+
+ dbus_connection_send(nat->conn, reply, NULL);
+ dbus_message_unref(msg);
+ dbus_message_unref(reply);
+ return JNI_TRUE;
+ }
+#endif
+ return JNI_FALSE;
+}
+
+static jboolean setPasskeyNative(JNIEnv *env, jobject object, jstring address,
+ int passkey, int nativeData) {
+#ifdef HAVE_BLUETOOTH
+ LOGV(__FUNCTION__);
+ native_data_t *nat = get_native_data(env, object);
+ if (nat) {
+ DBusMessage *msg = (DBusMessage *)nativeData;
+ DBusMessage *reply = dbus_message_new_method_return(msg);
+ if (!reply) {
+ LOGE("%s: Cannot create message reply to return Passkey code to "
+ "D-Bus\n", __FUNCTION__);
+ dbus_message_unref(msg);
+ return JNI_FALSE;
+ }
+
+ dbus_message_append_args(reply, DBUS_TYPE_UINT32, (uint32_t *)&passkey,
+ DBUS_TYPE_INVALID);
+
+ dbus_connection_send(nat->conn, reply, NULL);
+ dbus_message_unref(msg);
+ dbus_message_unref(reply);
+ return JNI_TRUE;
+ }
+#endif
+ return JNI_FALSE;
+}
+
static jboolean setPinNative(JNIEnv *env, jobject object, jstring address,
jstring pin, int nativeData) {
#ifdef HAVE_BLUETOOTH
@@ -467,17 +526,17 @@ static jboolean setPinNative(JNIEnv *env, jobject object, jstring address,
return JNI_FALSE;
}
-static jboolean cancelPinNative(JNIEnv *env, jobject object, jstring address,
- int nativeData) {
+static jboolean cancelPairingUserInputNative(JNIEnv *env, jobject object,
+ jstring address, int nativeData) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
native_data_t *nat = get_native_data(env, object);
if (nat) {
DBusMessage *msg = (DBusMessage *)nativeData;
DBusMessage *reply = dbus_message_new_error(msg,
- "org.bluez.Error.Canceled", "PIN Entry was canceled");
+ "org.bluez.Error.Canceled", "Pairing User Input was canceled");
if (!reply) {
- LOGE("%s: Cannot create message reply to return PIN cancel to "
+ LOGE("%s: Cannot create message reply to return cancelUserInput to"
"D-BUS\n", __FUNCTION__);
dbus_message_unref(msg);
return JNI_FALSE;
@@ -665,8 +724,12 @@ static JNINativeMethod sMethods[] = {
{"getDeviceServiceChannelNative", "(Ljava/lang/String;Ljava/lang/String;I)I",
(void *)getDeviceServiceChannelNative},
+ {"setPairingConfirmationNative", "(Ljava/lang/String;ZI)Z",
+ (void *)setPairingConfirmationNative},
+ {"setPasskeyNative", "(Ljava/lang/String;II)Z", (void *)setPasskeyNative},
{"setPinNative", "(Ljava/lang/String;Ljava/lang/String;I)Z", (void *)setPinNative},
- {"cancelPinNative", "(Ljava/lang/String;I)Z", (void *)cancelPinNative},
+ {"cancelPairingUserInputNative", "(Ljava/lang/String;I)Z",
+ (void *)cancelPairingUserInputNative},
};
int register_android_server_BluetoothDeviceService(JNIEnv *env) {
diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp
index 0857cb3a5225..4a13e8004ea8 100644
--- a/core/jni/android_server_BluetoothEventLoop.cpp
+++ b/core/jni/android_server_BluetoothEventLoop.cpp
@@ -50,6 +50,8 @@ static jmethodID method_onCreatePairedDeviceResult;
static jmethodID method_onGetDeviceServiceChannelResult;
static jmethodID method_onRequestPinCode;
+static jmethodID method_onRequestPasskey;
+static jmethodID method_onRequestConfirmation;
static jmethodID method_onAgentAuthorize;
static jmethodID method_onAgentCancel;
@@ -89,6 +91,10 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
method_onAgentCancel = env->GetMethodID(clazz, "onAgentCancel", "()V");
method_onRequestPinCode = env->GetMethodID(clazz, "onRequestPinCode",
"(Ljava/lang/String;I)V");
+ method_onRequestPasskey = env->GetMethodID(clazz, "onRequestPasskey",
+ "(Ljava/lang/String;I)V");
+ method_onRequestConfirmation = env->GetMethodID(clazz, "onRequestConfirmation",
+ "(Ljava/lang/String;II)V");
field_mNativeData = env->GetFieldID(clazz, "mNativeData", "I");
#endif
@@ -872,6 +878,38 @@ DBusHandlerResult agent_event_filter(DBusConnection *conn,
int(msg));
return DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_method_call(msg,
+ "org.bluez.Agent", "RequestPasskey")) {
+ char *object_path;
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_OBJECT_PATH, &object_path,
+ DBUS_TYPE_INVALID)) {
+ LOGE("%s: Invalid arguments for RequestPasskey() method", __FUNCTION__);
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ dbus_message_ref(msg); // increment refcount because we pass to java
+ env->CallVoidMethod(nat->me, method_onRequestPasskey,
+ env->NewStringUTF(object_path),
+ int(msg));
+ } else if (dbus_message_is_method_call(msg,
+ "org.bluez.Agent", "RequestConfirmation")) {
+ char *object_path;
+ uint32_t passkey;
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_OBJECT_PATH, &object_path,
+ DBUS_TYPE_UINT32, &passkey,
+ DBUS_TYPE_INVALID)) {
+ LOGE("%s: Invalid arguments for RequestConfirmation() method", __FUNCTION__);
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ dbus_message_ref(msg); // increment refcount because we pass to java
+ env->CallVoidMethod(nat->me, method_onRequestConfirmation,
+ env->NewStringUTF(object_path),
+ passkey,
+ int(msg));
+ return DBUS_HANDLER_RESULT_HANDLED;
+ } else if (dbus_message_is_method_call(msg,
"org.bluez.Agent", "Release")) {
// reply
DBusMessage *reply = dbus_message_new_method_return(msg);
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 770c75525225..09a0d70bcf40 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -368,7 +368,7 @@ static int pid_compare(const void* v1, const void* v2)
return *((const jint*)v1) - *((const jint*)v2);
}
-jint android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz)
+static jlong android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz)
{
int fd = open("/proc/meminfo", O_RDONLY);
@@ -388,7 +388,7 @@ jint android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz)
buffer[len] = 0;
int numFound = 0;
- int mem = 0;
+ jlong mem = 0;
static const char* const sums[] = { "MemFree:", "Cached:", NULL };
static const int sumsLen[] = { strlen("MemFree:"), strlen("Cached:"), NULL };
@@ -407,7 +407,7 @@ jint android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz)
p++;
if (*p == 0) p--;
}
- mem += atoi(num) * 1024;
+ mem += atoll(num) * 1024;
numFound++;
break;
}
@@ -857,7 +857,7 @@ static const JNINativeMethod methods[] = {
{"setGid", "(I)I", (void*)android_os_Process_setGid},
{"sendSignal", "(II)V", (void*)android_os_Process_sendSignal},
{"supportsProcesses", "()Z", (void*)android_os_Process_supportsProcesses},
- {"getFreeMemory", "()I", (void*)android_os_Process_getFreeMemory},
+ {"getFreeMemory", "()J", (void*)android_os_Process_getFreeMemory},
{"readProcLines", "(Ljava/lang/String;[Ljava/lang/String;[J)V", (void*)android_os_Process_readProcLines},
{"getPids", "(Ljava/lang/String;[I)[I", (void*)android_os_Process_getPids},
{"readProcFile", "(Ljava/lang/String;[I[Ljava/lang/String;[J[F)Z", (void*)android_os_Process_readProcFile},
diff --git a/core/res/res/drawable/light_header.9.png b/core/res/res/drawable/light_header.9.png
new file mode 100644
index 000000000000..ad5dce1e241e
--- /dev/null
+++ b/core/res/res/drawable/light_header.9.png
Binary files differ
diff --git a/core/res/res/layout/search_dropdown_app_selector.xml b/core/res/res/layout/search_dropdown_app_selector.xml
deleted file mode 100644
index f86645fa829d..000000000000
--- a/core/res/res/layout/search_dropdown_app_selector.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* //device/apps/common/res/layout/search_dropdown_app_selector.xml
-**
-** Copyright 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.
-*/
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="?android:attr/listPreferredItemHeight"
- android:orientation="horizontal"
- android:gravity="center_vertical"
- android:baselineAligned="false"
- >
-
- <ImageView android:id="@+id/search_app_icon1"
- android:layout_width="32dip"
- android:layout_height="32dip"
- android:layout_gravity="center_vertical"
- android:scaleType="fitCenter"
- android:src="@android:drawable/ic_search_category_default" />
-
- <TextView android:id="@+id/search_app_text1"
- style="?android:attr/dropDownItemStyle"
- android:singleLine="true"
- android:layout_height="wrap_content"
- android:layout_width="0dip"
- android:layout_weight="1"
- android:layout_gravity="center_vertical" />
-
-</LinearLayout>
diff --git a/core/res/res/layout/search_dropdown_item_2line.xml b/core/res/res/layout/search_dropdown_item_2line.xml
deleted file mode 100644
index 5546b6636bb9..000000000000
--- a/core/res/res/layout/search_dropdown_item_2line.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* //device/apps/common/assets/res/any/layout/simple_spinner_item.xml
-**
-** Copyright 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.
-*/
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="?android:attr/searchResultListItemHeight"
- android:orientation="horizontal"
- android:gravity="center_vertical"
- android:baselineAligned="false"
- >
-
- <TwoLineListItem
- android:paddingTop="1dip"
- android:paddingBottom="1dip"
- android:gravity="center_vertical"
- android:layout_width="0dip"
- android:layout_weight="1"
- android:layout_height="wrap_content"
- android:mode="twoLine" >
-
- <TextView
- android:id="@android:id/text1"
- style="?android:attr/dropDownItemStyle"
- android:textAppearance="?android:attr/textAppearanceSearchResultTitle"
- android:singleLine="true"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content" />
-
- <TextView
- android:id="@android:id/text2"
- style="?android:attr/dropDownItemStyle"
- android:textAppearance="?android:attr/textAppearanceSearchResultSubtitle"
- android:textColor="?android:attr/textColorSecondaryInverse"
- android:singleLine="true"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_below="@android:id/text1"
- android:layout_alignLeft="@android:id/text1" />
-
- </TwoLineListItem>
-
-</LinearLayout>
diff --git a/core/res/res/layout/search_dropdown_item_icons_1line.xml b/core/res/res/layout/search_dropdown_item_icons_1line.xml
deleted file mode 100644
index 4f65d746b4eb..000000000000
--- a/core/res/res/layout/search_dropdown_item_icons_1line.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* //device/apps/common/assets/res/any/layout/simple_spinner_item.xml
-**
-** Copyright 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.
-*/
--->
-
- <!-- NOTE: The appearance of the inner text element must match the appearance -->
- <!-- of the text element in apps/common/res/layout/simple_dropdown_item_1line.xml -->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:paddingLeft="4dip"
- android:paddingRight="2dip"
- android:layout_width="fill_parent"
- android:layout_height="?android:attr/searchResultListItemHeight"
- android:orientation="horizontal"
- android:gravity="center_vertical"
- android:baselineAligned="false"
- >
-
- <ImageView android:id="@android:id/icon1"
- android:layout_width="48dip"
- android:layout_height="48dip"
- android:layout_gravity="center_vertical"
- android:scaleType="centerInside" />
-
- <TextView android:id="@android:id/text1"
- style="?android:attr/dropDownItemStyle"
- android:textAppearance="?android:attr/textAppearanceSearchResultTitle"
- android:singleLine="true"
- android:layout_height="wrap_content"
- android:layout_width="0dip"
- android:layout_weight="1" />
-
- <ImageView android:id="@android:id/icon2"
- android:layout_width="48dip"
- android:layout_height="48dip"
- android:layout_gravity="center_vertical"
- android:scaleType="centerInside" />
-
-</LinearLayout>
diff --git a/core/res/res/values-he-rIL/donottranslate-cldr.xml b/core/res/res/values-he-rIL/donottranslate-cldr.xml
index e3feb1e33146..3378ed7227ad 100644
--- a/core/res/res/values-he-rIL/donottranslate-cldr.xml
+++ b/core/res/res/values-he-rIL/donottranslate-cldr.xml
@@ -61,20 +61,20 @@
<string name="day_of_week_long_friday">יום שישי</string>
<string name="day_of_week_long_saturday">יום שבת</string>
- <string name="day_of_week_medium_sunday">יום א'</string>
- <string name="day_of_week_medium_monday">יום ב'</string>
- <string name="day_of_week_medium_tuesday">יום ג'</string>
- <string name="day_of_week_medium_wednesday">יום ד'</string>
- <string name="day_of_week_medium_thursday">יום ה'</string>
- <string name="day_of_week_medium_friday">יום ו'</string>
+ <string name="day_of_week_medium_sunday">יום א\'</string>
+ <string name="day_of_week_medium_monday">יום ב\'</string>
+ <string name="day_of_week_medium_tuesday">יום ג\'</string>
+ <string name="day_of_week_medium_wednesday">יום ד\'</string>
+ <string name="day_of_week_medium_thursday">יום ה\'</string>
+ <string name="day_of_week_medium_friday">יום ו\'</string>
<string name="day_of_week_medium_saturday">שבת</string>
- <string name="day_of_week_short_sunday">יום א'</string>
- <string name="day_of_week_short_monday">יום ב'</string>
- <string name="day_of_week_short_tuesday">יום ג'</string>
- <string name="day_of_week_short_wednesday">יום ד'</string>
- <string name="day_of_week_short_thursday">יום ה'</string>
- <string name="day_of_week_short_friday">יום ו'</string>
+ <string name="day_of_week_short_sunday">יום א\'</string>
+ <string name="day_of_week_short_monday">יום ב\'</string>
+ <string name="day_of_week_short_tuesday">יום ג\'</string>
+ <string name="day_of_week_short_wednesday">יום ד\'</string>
+ <string name="day_of_week_short_thursday">יום ה\'</string>
+ <string name="day_of_week_short_friday">יום ו\'</string>
<string name="day_of_week_short_saturday">שבת</string>
<string name="day_of_week_shortest_sunday">א</string>
diff --git a/core/res/res/values-pt-rBR/donottranslate-cldr.xml b/core/res/res/values-pt-rBR/donottranslate-cldr.xml
index 47290552dd2e..1111658ffb90 100644
--- a/core/res/res/values-pt-rBR/donottranslate-cldr.xml
+++ b/core/res/res/values-pt-rBR/donottranslate-cldr.xml
@@ -95,7 +95,7 @@
<string name="hour_minute_ampm">%-l:%M %p</string>
<string name="hour_minute_cap_ampm">%-l:%M %^p</string>
<string name="twelve_hour_time_format">h:mm a</string>
- <string name="twenty_four_hour_time_format">H'h'mm</string>
+ <string name="twenty_four_hour_time_format">H\'h\'mm</string>
<string name="numeric_date">%d/%m/%Y</string>
<string name="numeric_date_format">dd/MM/yyyy</string>
<string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-pt-rPT/donottranslate-cldr.xml b/core/res/res/values-pt-rPT/donottranslate-cldr.xml
index f38a2d0499d7..197cb6ed16fd 100644
--- a/core/res/res/values-pt-rPT/donottranslate-cldr.xml
+++ b/core/res/res/values-pt-rPT/donottranslate-cldr.xml
@@ -95,7 +95,7 @@
<string name="hour_minute_ampm">%-l:%M %p</string>
<string name="hour_minute_cap_ampm">%-l:%M %^p</string>
<string name="twelve_hour_time_format">h:mm a</string>
- <string name="twenty_four_hour_time_format">H'h'mm</string>
+ <string name="twenty_four_hour_time_format">H\'h\'mm</string>
<string name="numeric_date">%d/%m/%Y</string>
<string name="numeric_date_format">dd/MM/yyyy</string>
<string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-pt/donottranslate-cldr.xml b/core/res/res/values-pt/donottranslate-cldr.xml
index 47290552dd2e..1111658ffb90 100644
--- a/core/res/res/values-pt/donottranslate-cldr.xml
+++ b/core/res/res/values-pt/donottranslate-cldr.xml
@@ -95,7 +95,7 @@
<string name="hour_minute_ampm">%-l:%M %p</string>
<string name="hour_minute_cap_ampm">%-l:%M %^p</string>
<string name="twelve_hour_time_format">h:mm a</string>
- <string name="twenty_four_hour_time_format">H'h'mm</string>
+ <string name="twenty_four_hour_time_format">H\'h\'mm</string>
<string name="numeric_date">%d/%m/%Y</string>
<string name="numeric_date_format">dd/MM/yyyy</string>
<string name="numeric_date_template">"%s/%s/%s"</string>
diff --git a/core/res/res/values-vi-rVN/donottranslate-cldr.xml b/core/res/res/values-vi-rVN/donottranslate-cldr.xml
index 72ff8b68999c..6f2d3422e9fb 100644
--- a/core/res/res/values-vi-rVN/donottranslate-cldr.xml
+++ b/core/res/res/values-vi-rVN/donottranslate-cldr.xml
@@ -133,8 +133,8 @@
<string name="same_month_md1_time1_md2_time2">%3$s %2$s %5$s - %8$s %7$s %10$s</string>
<string name="same_year_wday1_md1_time1_wday2_md2_time2">%1$s %3$s %2$s %5$s - %6$s %8$s %7$s %10$s</string>
<string name="same_month_wday1_md1_time1_wday2_md2_time2">%1$s %3$s %2$s %5$s - %6$s %8$s %7$s %10$s</string>
- <string name="same_year_mdy1_time1_mdy2_time2">Ngày %3$s tháng %2$s năm %4$s %5$s - 'Ngày %8$s tháng %7$s năm %9$s %10$s</string>
- <string name="same_month_mdy1_time1_mdy2_time2">Ngày %3$s tháng %2$s năm %4$s %5$s - 'Ngày %8$s tháng %7$s năm %9$s %10$s</string>
+ <string name="same_year_mdy1_time1_mdy2_time2">Ngày %3$s tháng %2$s năm %4$s %5$s - \'Ngày %8$s tháng %7$s năm %9$s %10$s</string>
+ <string name="same_month_mdy1_time1_mdy2_time2">Ngày %3$s tháng %2$s năm %4$s %5$s - \'Ngày %8$s tháng %7$s năm %9$s %10$s</string>
<string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%1$s, %3$s %2$s %4$s %5$s - %6$s, %8$s %7$s %9$s %10$s</string>
<string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%1$s, %3$s %2$s %4$s %5$s - %6$s, %8$s %7$s %9$s %10$s</string>
<string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s %4$s - %6$s, %8$s %7$s %9$s</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 7d235ec8d4ef..8eda12e1f131 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -348,7 +348,8 @@
</style>
<style name="Widget.TextView.ListSeparator.White">
- <item name="android:textColor">?textColorSecondaryInverse</item>
+ <item name="android:textColor">?textColorPrimaryInverse</item>
+ <item name="android:background">@android:drawable/light_header</item>
</style>
<style name="Widget.EditText">
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index be836ebec4e5..e3fffb74f11a 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -325,6 +325,32 @@
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
<item name="android:windowSoftInputMode">stateUnspecified|adjustPan</item>
+
+ <item name="textAppearance">@android:style/TextAppearance</item>
+ <item name="textAppearanceInverse">@android:style/TextAppearance.Inverse</item>
+
+ <item name="textColorPrimary">@android:color/primary_text_dark</item>
+ <item name="textColorSecondary">@android:color/secondary_text_dark</item>
+ <item name="textColorTertiary">@android:color/tertiary_text_dark</item>
+ <item name="textColorPrimaryInverse">@android:color/primary_text_light</item>
+ <item name="textColorSecondaryInverse">@android:color/secondary_text_light</item>
+ <item name="textColorTertiaryInverse">@android:color/tertiary_text_light</item>
+ <item name="textColorPrimaryDisableOnly">@android:color/primary_text_dark_disable_only</item>
+ <item name="textColorPrimaryInverseDisableOnly">@android:color/primary_text_light_disable_only</item>
+ <item name="textColorPrimaryNoDisable">@android:color/primary_text_dark_nodisable</item>
+ <item name="textColorSecondaryNoDisable">@android:color/secondary_text_dark_nodisable</item>
+ <item name="textColorPrimaryInverseNoDisable">@android:color/primary_text_light_nodisable</item>
+ <item name="textColorSecondaryInverseNoDisable">@android:color/secondary_text_light_nodisable</item>
+ <item name="textColorHint">@android:color/hint_foreground_dark</item>
+ <item name="textColorHintInverse">@android:color/hint_foreground_light</item>
+ <item name="textColorSearchUrl">@android:color/search_url_text</item>
+
+ <item name="textAppearanceLarge">@android:style/TextAppearance.Large</item>
+ <item name="textAppearanceMedium">@android:style/TextAppearance.Medium</item>
+ <item name="textAppearanceSmall">@android:style/TextAppearance.Small</item>
+ <item name="textAppearanceLargeInverse">@android:style/TextAppearance.Large.Inverse</item>
+ <item name="textAppearanceMediumInverse">@android:style/TextAppearance.Medium.Inverse</item>
+ <item name="textAppearanceSmallInverse">@android:style/TextAppearance.Small.Inverse</item>
</style>
<!-- Default theme for alert dialog windows, which is used by the
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 0069fe1765da..20aa6e787640 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -148,5 +148,7 @@
file="/system/framework/android.test.runner.jar" />
<library name="com.android.im.plugin"
file="/system/framework/com.android.im.plugin.jar"/>
+ <library name="javax.obex"
+ file="/system/framework/javax.obex.jar"/>
</permissions>
diff --git a/data/sounds/AudioPackage2.mk b/data/sounds/AudioPackage2.mk
index aea3f0b3007b..649787efc511 100644
--- a/data/sounds/AudioPackage2.mk
+++ b/data/sounds/AudioPackage2.mk
@@ -23,7 +23,7 @@ PRODUCT_COPY_FILES += \
$(LOCAL_PATH)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \
$(LOCAL_PATH)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \
$(LOCAL_PATH)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \
- $(LOCAL_PATH)/Silence.ogg:system/media/audio/ringtones/Silence.ogg \
+ $(LOCAL_PATH)/Silence.ogg:system/media/audio/ringtones/notifications/Silence.ogg \
$(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:system/media/audio/ringtones/BeatPlucker.ogg \
$(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:system/media/audio/ringtones/BentleyDubs.ogg \
$(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:system/media/audio/ringtones/BirdLoop.ogg \
diff --git a/data/sounds/OriginalAudio.mk b/data/sounds/OriginalAudio.mk
index 8c8fc329c430..fc1e92109cac 100644
--- a/data/sounds/OriginalAudio.mk
+++ b/data/sounds/OriginalAudio.mk
@@ -22,7 +22,7 @@ PRODUCT_COPY_FILES += \
$(LOCAL_PATH)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \
$(LOCAL_PATH)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \
$(LOCAL_PATH)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \
- $(LOCAL_PATH)/Silence.ogg:system/media/audio/ringtones/Silence.ogg \
+ $(LOCAL_PATH)/Silence.ogg:system/media/audio/ringtones/notifications/Silence.ogg \
$(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:system/media/audio/ringtones/BeatPlucker.ogg \
$(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:system/media/audio/ringtones/BentleyDubs.ogg \
$(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:system/media/audio/ringtones/BirdLoop.ogg \
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index e2e93eb87e67..df659ef224de 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -18,6 +18,7 @@ package android.graphics;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.DisplayMetrics;
import java.io.OutputStream;
import java.nio.Buffer;
@@ -31,8 +32,6 @@ public final class Bitmap implements Parcelable {
*
* @see Bitmap#getDensityScale()
* @see Bitmap#setDensityScale(float)
- *
- * @hide pending API council approval
*/
public static final float DENSITY_SCALE_UNKNOWN = -1.0f;
@@ -84,11 +83,9 @@ public final class Bitmap implements Parcelable {
* @see #setDensityScale(float)
* @see #isAutoScalingEnabled()
* @see #setAutoScalingEnabled(boolean)
- * @see android.util.DisplayMetrics#DEFAULT_DENSITY
+ * @see android.util.DisplayMetrics#DENSITY_DEFAULT
* @see android.util.DisplayMetrics#density
* @see #DENSITY_SCALE_UNKNOWN
- *
- * @hide pending API council approval
*/
public float getDensityScale() {
return mDensityScale;
@@ -106,11 +103,9 @@ public final class Bitmap implements Parcelable {
* @see #getDensityScale()
* @see #isAutoScalingEnabled()
* @see #setAutoScalingEnabled(boolean)
- * @see android.util.DisplayMetrics#DEFAULT_DENSITY
+ * @see android.util.DisplayMetrics#DENSITY_DEFAULT
* @see android.util.DisplayMetrics#density
* @see #DENSITY_SCALE_UNKNOWN
- *
- * @hide pending API council approval
*/
public void setDensityScale(float densityScale) {
mDensityScale = densityScale;
@@ -132,8 +127,6 @@ public final class Bitmap implements Parcelable {
* @see #setAutoScalingEnabled(boolean)
* @see #getDensityScale()
* @see #setDensityScale(float)
- *
- * @hide pending API council approval
*/
public boolean isAutoScalingEnabled() {
return mAutoScaling;
@@ -150,8 +143,6 @@ public final class Bitmap implements Parcelable {
* the bitmap will never be automatically scaled at drawing time.</p>
*
* @param autoScalingEnabled True to scale the bitmap at drawing time, false otherwise.
- *
- * @hide pending API council approval
*/
public void setAutoScalingEnabled(boolean autoScalingEnabled) {
mAutoScaling = autoScalingEnabled;
@@ -465,8 +456,8 @@ public final class Bitmap implements Parcelable {
// The new bitmap was created from a known bitmap source so assume that
// they use the same density scale
- bitmap.setDensityScale(source.getDensityScale());
- bitmap.setAutoScalingEnabled(source.isAutoScalingEnabled());
+ bitmap.mDensityScale = source.mDensityScale;
+ bitmap.mAutoScaling = source.mAutoScaling;
return bitmap;
}
@@ -615,26 +606,60 @@ public final class Bitmap implements Parcelable {
* Convenience method that returns the width of this bitmap divided
* by the density scale factor.
*
+ * @param canvas The Canvas the bitmap will be drawn to.
* @return The scaled width of this bitmap, according to the density scale factor.
- *
- * @hide pending API council approval
*/
- public int getScaledWidth() {
- final float scale = getDensityScale();
- return scale == DENSITY_SCALE_UNKNOWN ? getWidth() : (int) (getWidth() / scale);
+ public int getScaledWidth(Canvas canvas) {
+ final float scale = mDensityScale;
+ if (!mAutoScaling || scale < 0) {
+ return getWidth();
+ }
+ return (int)(getWidth() * canvas.getDensityScale() / scale);
}
/**
* Convenience method that returns the height of this bitmap divided
* by the density scale factor.
*
+ * @param canvas The Canvas the bitmap will be drawn to.
* @return The scaled height of this bitmap, according to the density scale factor.
+ */
+ public int getScaledHeight(Canvas canvas) {
+ final float scale = mDensityScale;
+ if (!mAutoScaling || scale < 0) {
+ return getHeight();
+ }
+ return (int)(getHeight() * canvas.getDensityScale() / scale);
+ }
+
+ /**
+ * Convenience method that returns the width of this bitmap divided
+ * by the density scale factor.
+ *
+ * @param metrics The target display metrics.
+ * @return The scaled width of this bitmap, according to the density scale factor.
+ */
+ public int getScaledWidth(DisplayMetrics metrics) {
+ final float scale = mDensityScale;
+ if (!mAutoScaling || scale < 0) {
+ return getWidth();
+ }
+ return (int)(getWidth() * metrics.density / scale);
+ }
+
+ /**
+ * Convenience method that returns the height of this bitmap divided
+ * by the density scale factor.
*
- * @hide pending API council approval
+ * @param metrics The target display metrics.
+ * @return The scaled height of this bitmap, according to the density scale factor.
*/
- public int getScaledHeight() {
- final float scale = getDensityScale();
- return scale == DENSITY_SCALE_UNKNOWN ? getWidth() : (int) (getHeight() / scale);
+ public int getScaledHeight(DisplayMetrics metrics) {
+ final float scale = mDensityScale;
+ if (!mAutoScaling || scale < 0) {
+ return getHeight();
+ }
+ return (int)(getHeight() * metrics.density / scale);
}
/**
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 2a3998776e2a..975bc1abb90f 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -82,10 +82,8 @@ public class BitmapFactory {
/**
* The desired pixel density of the bitmap.
*
- * @see android.util.DisplayMetrics#DEFAULT_DENSITY
+ * @see android.util.DisplayMetrics#DENSITY_DEFAULT
* @see android.util.DisplayMetrics#density
- *
- * @hide pending API council approval
*/
public int inDensity;
@@ -98,8 +96,6 @@ public class BitmapFactory {
* a non-scaled version of the bitmap. In this case,
* {@link android.graphics.Bitmap#setAutoScalingEnabled(boolean)} can be used
* to properly scale the bitmap at drawing time.</p>
- *
- * @hide pending API council approval
*/
public boolean inScaled;
@@ -130,6 +126,19 @@ public class BitmapFactory {
public boolean inInputShareable;
/**
+ * Normally bitmap allocations count against the dalvik heap, which
+ * means they help trigger GCs when a lot have been allocated. However,
+ * in rare cases, the caller may want to allocate the bitmap outside of
+ * that heap. To request that, set inNativeAlloc to true. In these
+ * rare instances, it is solely up to the caller to ensure that OOM is
+ * managed explicitly by calling bitmap.recycle() as soon as such a
+ * bitmap is no longer needed.
+ *
+ * @hide pending API council approval
+ */
+ public boolean inNativeAlloc;
+
+ /**
* The resulting width of the bitmap, set independent of the state of
* inJustDecodeBounds. However, if there is an error trying to decode,
* outWidth will be set to -1.
@@ -226,8 +235,6 @@ public class BitmapFactory {
/**
* Decode a new Bitmap from an InputStream. This InputStream was obtained from
* resources, which we pass to be able to scale the bitmap accordingly.
- *
- * @hide
*/
public static Bitmap decodeStream(Resources res, TypedValue value, InputStream is,
Rect pad, Options opts) {
@@ -239,15 +246,19 @@ public class BitmapFactory {
Bitmap bm = decodeStream(is, pad, opts);
if (bm != null && res != null && value != null) {
+ final int density = value.density;
+ if (density == TypedValue.DENSITY_NONE) {
+ return bm;
+ }
+
byte[] np = bm.getNinePatchChunk();
final boolean isNinePatch = np != null && NinePatch.isNinePatchChunk(np);
- final int density = value.density;
if (opts.inDensity == 0) {
opts.inDensity = density == TypedValue.DENSITY_DEFAULT ?
- DisplayMetrics.DEFAULT_DENSITY : density;
+ DisplayMetrics.DENSITY_DEFAULT : density;
}
- float scale = opts.inDensity / (float) DisplayMetrics.DEFAULT_DENSITY;
+ float scale = opts.inDensity / (float) DisplayMetrics.DENSITY_DEFAULT;
if (opts.inScaled || isNinePatch) {
bm.setDensityScale(1.0f);
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 4498e1a2e117..da7359772ee3 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -184,8 +184,6 @@ public class Canvas {
*
* @see #setDensityScale(float)
* @see Bitmap#getDensityScale()
- *
- * @hide pending API council approval
*/
public float getDensityScale() {
if (mBitmap != null) {
@@ -205,8 +203,6 @@ public class Canvas {
*
* @see #getDensityScale()
* @see Bitmap#setDensityScale(float)
- *
- * @hide pending API council approval
*/
public void setDensityScale(float densityScale) {
if (mBitmap != null) {
diff --git a/include/media/IMediaPlayerService.h b/include/media/IMediaPlayerService.h
index f6faf144dc5e..39b5e57b1fe3 100644
--- a/include/media/IMediaPlayerService.h
+++ b/include/media/IMediaPlayerService.h
@@ -29,6 +29,7 @@
namespace android {
class IMediaRecorder;
+class IOMX;
class IMediaPlayerService: public IInterface
{
@@ -41,6 +42,7 @@ public:
virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, int fd, int64_t offset, int64_t length) = 0;
virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0;
virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0;
+ virtual sp<IOMX> createOMX() = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/media/IOMX.h b/include/media/IOMX.h
new file mode 100644
index 000000000000..5c61c50ee22b
--- /dev/null
+++ b/include/media/IOMX.h
@@ -0,0 +1,176 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_IOMX_H_
+
+#define ANDROID_IOMX_H_
+
+#include <binder/IInterface.h>
+#include <utils/List.h>
+#include <utils/String8.h>
+
+#include <OMX_Core.h>
+
+#define IOMX_USES_SOCKETS 0
+
+namespace android {
+
+class IMemory;
+class IOMXObserver;
+
+class IOMX : public IInterface {
+public:
+ DECLARE_META_INTERFACE(OMX);
+
+ typedef void *buffer_id;
+ typedef void *node_id;
+
+#if IOMX_USES_SOCKETS
+ // If successful, returns a socket descriptor used for further
+ // communication. Caller assumes ownership of "*sd".
+ virtual status_t connect(int *sd) = 0;
+#endif
+
+ virtual status_t list_nodes(List<String8> *list) = 0;
+
+ virtual status_t allocate_node(const char *name, node_id *node) = 0;
+ virtual status_t free_node(node_id node) = 0;
+
+ virtual status_t send_command(
+ node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param) = 0;
+
+ virtual status_t get_parameter(
+ node_id node, OMX_INDEXTYPE index,
+ void *params, size_t size) = 0;
+
+ virtual status_t set_parameter(
+ node_id node, OMX_INDEXTYPE index,
+ const void *params, size_t size) = 0;
+
+ virtual status_t use_buffer(
+ node_id node, OMX_U32 port_index, const sp<IMemory> &params,
+ buffer_id *buffer) = 0;
+
+ virtual status_t allocate_buffer(
+ node_id node, OMX_U32 port_index, size_t size,
+ buffer_id *buffer) = 0;
+
+ virtual status_t allocate_buffer_with_backup(
+ node_id node, OMX_U32 port_index, const sp<IMemory> &params,
+ buffer_id *buffer) = 0;
+
+ virtual status_t free_buffer(
+ node_id node, OMX_U32 port_index, buffer_id buffer) = 0;
+
+#if !IOMX_USES_SOCKETS
+ virtual status_t observe_node(
+ node_id node, const sp<IOMXObserver> &observer) = 0;
+
+ virtual void fill_buffer(node_id node, buffer_id buffer) = 0;
+
+ virtual void empty_buffer(
+ node_id node,
+ buffer_id buffer,
+ OMX_U32 range_offset, OMX_U32 range_length,
+ OMX_U32 flags, OMX_TICKS timestamp) = 0;
+#endif
+};
+
+struct omx_message {
+ enum {
+ EVENT,
+ EMPTY_BUFFER_DONE,
+ FILL_BUFFER_DONE,
+
+#if IOMX_USES_SOCKETS
+ EMPTY_BUFFER,
+ FILL_BUFFER,
+ SEND_COMMAND,
+ DISCONNECT,
+ DISCONNECTED,
+#endif
+
+ // reserved for OMXDecoder use.
+ START,
+ INITIAL_FILL_BUFFER,
+
+ // reserved for OMXObserver use.
+ QUIT_OBSERVER,
+ } type;
+
+ union {
+ // if type == EVENT
+ struct {
+ IOMX::node_id node;
+ OMX_EVENTTYPE event;
+ OMX_U32 data1;
+ OMX_U32 data2;
+ } event_data;
+
+ // if type == EMPTY_BUFFER_DONE || type == FILL_BUFFER
+ // || type == INITIAL_FILL_BUFFER
+ struct {
+ IOMX::node_id node;
+ IOMX::buffer_id buffer;
+ } buffer_data;
+
+ // if type == EMPTY_BUFFER || type == FILL_BUFFER_DONE
+ struct {
+ IOMX::node_id node;
+ IOMX::buffer_id buffer;
+ OMX_U32 range_offset;
+ OMX_U32 range_length;
+ OMX_U32 flags;
+ OMX_TICKS timestamp;
+ OMX_PTR platform_private; // ignored if type == EMPTY_BUFFER
+ } extended_buffer_data;
+
+ // if type == SEND_COMMAND
+ struct {
+ IOMX::node_id node;
+ OMX_COMMANDTYPE cmd;
+ OMX_S32 param;
+ } send_command_data;
+
+ } u;
+};
+
+class IOMXObserver : public IInterface {
+public:
+ DECLARE_META_INTERFACE(OMXObserver);
+
+ virtual void on_message(const omx_message &msg) = 0;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+class BnOMX : public BnInterface<IOMX> {
+public:
+ virtual status_t onTransact(
+ uint32_t code, const Parcel &data, Parcel *reply,
+ uint32_t flags = 0);
+};
+
+class BnOMXObserver : public BnInterface<IOMXObserver> {
+public:
+ virtual status_t onTransact(
+ uint32_t code, const Parcel &data, Parcel *reply,
+ uint32_t flags = 0);
+};
+
+} // namespace android
+
+#endif // ANDROID_IOMX_H_
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h
index 21600b2de9aa..97d55aa0537a 100644
--- a/include/media/MediaPlayerInterface.h
+++ b/include/media/MediaPlayerInterface.h
@@ -19,20 +19,30 @@
#ifdef __cplusplus
+#include <sys/types.h>
#include <ui/ISurface.h>
#include <utils/RefBase.h>
+#include <utils/Errors.h>
#include <media/mediaplayer.h>
#include <media/AudioSystem.h>
namespace android {
+typedef int32_t MetadataType;
+
class Parcel;
+template<typename T> class SortedVector;
enum player_type {
PV_PLAYER = 1,
SONIVOX_PLAYER = 2,
- VORBIS_PLAYER = 3
+ VORBIS_PLAYER = 3,
+ STAGEFRIGHT_PLAYER = 4,
+ // Test players are available only in the 'test' and 'eng' builds.
+ // The shared library with the test player is passed passed as an
+ // argument to the 'test:' url in the setDataSource call.
+ TEST_PLAYER = 5,
};
@@ -51,6 +61,9 @@ public:
// AudioSink: abstraction layer for audio output
class AudioSink : public RefBase {
public:
+ typedef void (*AudioCallback)(
+ AudioSink *audioSink, void *buffer, size_t size, void *cookie);
+
virtual ~AudioSink() {}
virtual bool ready() const = 0; // audio output is open and ready
virtual bool realtime() const = 0; // audio output is real-time output
@@ -60,7 +73,17 @@ public:
virtual ssize_t frameSize() const = 0;
virtual uint32_t latency() const = 0;
virtual float msecsPerFrame() const = 0;
- virtual status_t open(uint32_t sampleRate, int channelCount, int format=AudioSystem::PCM_16_BIT, int bufferCount=DEFAULT_AUDIOSINK_BUFFERCOUNT) = 0;
+
+ // If no callback is specified, use the "write" API below to submit
+ // audio data. Otherwise return a full buffer of audio data on each
+ // callback.
+ virtual status_t open(
+ uint32_t sampleRate, int channelCount,
+ int format=AudioSystem::PCM_16_BIT,
+ int bufferCount=DEFAULT_AUDIOSINK_BUFFERCOUNT,
+ AudioCallback cb = NULL,
+ void *cookie = NULL) = 0;
+
virtual void start() = 0;
virtual ssize_t write(const void* buffer, size_t size) = 0;
virtual void stop() = 0;
@@ -92,12 +115,23 @@ public:
mCookie = cookie; mNotify = notifyFunc; }
// Invoke a generic method on the player by using opaque parcels
// for the request and reply.
+ //
// @param request Parcel that is positioned at the start of the
// data sent by the java layer.
// @param[out] reply Parcel to hold the reply data. Cannot be null.
- // @return OK if the invocation was made successfully. A player
- // not supporting the direct API should return INVALID_OPERATION.
+ // @return OK if the call was successful.
virtual status_t invoke(const Parcel& request, Parcel *reply) = 0;
+
+ // The Client in the MetadataPlayerService calls this method on
+ // the native player to retrieve all or a subset of metadata.
+ //
+ // @param ids SortedList of metadata ID to be fetch. If empty, all
+ // the known metadata should be returned.
+ // @param[inout] records Parcel where the player appends its metadata.
+ // @return OK if the call was successful.
+ virtual status_t getMetadata(const SortedVector<MetadataType>& ids,
+ Parcel *records) = 0;
+
protected:
virtual void sendEvent(int msg, int ext1=0, int ext2=0) { if (mNotify) mNotify(mCookie, msg, ext1, ext2); }
diff --git a/include/media/PVPlayer.h b/include/media/PVPlayer.h
index eb4595ba156a..40ccc14b2ecb 100644
--- a/include/media/PVPlayer.h
+++ b/include/media/PVPlayer.h
@@ -53,6 +53,8 @@ public:
virtual status_t setLooping(int loop);
virtual player_type playerType() { return PV_PLAYER; }
virtual status_t invoke(const Parcel& request, Parcel *reply);
+ virtual status_t getMetadata(const SortedVector<MetadataType>& ids,
+ Parcel *records);
// make available to PlayerDriver
void sendEvent(int msg, int ext1=0, int ext2=0) { MediaPlayerBase::sendEvent(msg, ext1, ext2); }
@@ -63,6 +65,7 @@ private:
static void run_set_video_surface(status_t s, void *cookie, bool cancelled);
static void run_set_audio_output(status_t s, void *cookie, bool cancelled);
static void run_prepare(status_t s, void *cookie, bool cancelled);
+ static void check_for_live_streaming(status_t s, void *cookie, bool cancelled);
PlayerDriver* mPlayerDriver;
char * mDataSourcePath;
diff --git a/include/media/stagefright/AudioPlayer.h b/include/media/stagefright/AudioPlayer.h
new file mode 100644
index 000000000000..0f2e52884485
--- /dev/null
+++ b/include/media/stagefright/AudioPlayer.h
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+
+#ifndef AUDIO_PLAYER_H_
+
+#define AUDIO_PLAYER_H_
+
+#include <media/MediaPlayerInterface.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/TimeSource.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class MediaSource;
+class AudioTrack;
+
+class AudioPlayer : public TimeSource {
+public:
+ AudioPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink);
+ ~AudioPlayer();
+
+ // Caller retains ownership of "source".
+ void setSource(MediaSource *source);
+
+ // Return time in us.
+ virtual int64_t getRealTimeUs();
+
+ void start();
+
+ void pause();
+ void resume();
+
+ void stop();
+
+ // Returns the timestamp of the last buffer played (in us).
+ int64_t getMediaTimeUs();
+
+ // Returns true iff a mapping is established, i.e. the AudioPlayer
+ // has played at least one frame of audio.
+ bool getMediaTimeMapping(int64_t *realtime_us, int64_t *mediatime_us);
+
+ status_t seekTo(int64_t time_us);
+
+private:
+ MediaSource *mSource;
+ AudioTrack *mAudioTrack;
+
+ MediaBuffer *mInputBuffer;
+
+ int mSampleRate;
+ int64_t mLatencyUs;
+ size_t mFrameSize;
+
+ Mutex mLock;
+ int64_t mNumFramesPlayed;
+
+ int64_t mPositionTimeMediaUs;
+ int64_t mPositionTimeRealUs;
+
+ bool mSeeking;
+ int64_t mSeekTimeUs;
+
+ bool mStarted;
+
+ sp<MediaPlayerBase::AudioSink> mAudioSink;
+
+ static void AudioCallback(int event, void *user, void *info);
+ void AudioCallback(int event, void *info);
+
+ static void AudioSinkCallback(
+ MediaPlayerBase::AudioSink *audioSink,
+ void *data, size_t size, void *me);
+
+ void fillBuffer(void *data, size_t size);
+
+ int64_t getRealTimeUsLocked() const;
+
+ AudioPlayer(const AudioPlayer &);
+ AudioPlayer &operator=(const AudioPlayer &);
+};
+
+} // namespace android
+
+#endif // AUDIO_PLAYER_H_
diff --git a/include/media/stagefright/AudioSource.h b/include/media/stagefright/AudioSource.h
new file mode 100644
index 000000000000..e12995802ff7
--- /dev/null
+++ b/include/media/stagefright/AudioSource.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#ifndef AUDIO_SOURCE_H_
+
+#define AUDIO_SOURCE_H_
+
+#include <media/stagefright/MediaSource.h>
+
+namespace android {
+
+class AudioRecord;
+
+class AudioSource {
+public:
+ AudioSource(int inputSource);
+ virtual ~AudioSource();
+
+ status_t initCheck() const;
+
+ virtual status_t start(MetaData *params = NULL);
+ virtual status_t stop();
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+private:
+ AudioRecord *mRecord;
+ status_t mInitCheck;
+
+ AudioSource(const AudioSource &);
+ AudioSource &operator=(const AudioSource &);
+};
+
+} // namespace android
+
+#endif // AUDIO_SOURCE_H_
diff --git a/include/media/stagefright/CachingDataSource.h b/include/media/stagefright/CachingDataSource.h
new file mode 100644
index 000000000000..e275cb42672c
--- /dev/null
+++ b/include/media/stagefright/CachingDataSource.h
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#ifndef CACHING_DATASOURCE_H_
+
+#define CACHING_DATASOURCE_H_
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaErrors.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class CachingDataSource : public DataSource {
+public:
+ // Assumes ownership of "source".
+ CachingDataSource(DataSource *source, size_t pageSize, int numPages);
+ virtual ~CachingDataSource();
+
+ status_t InitCheck() const;
+
+ virtual ssize_t read_at(off_t offset, void *data, size_t size);
+
+private:
+ struct Page {
+ Page *mPrev, *mNext;
+ off_t mOffset;
+ size_t mLength;
+ void *mData;
+ };
+
+ DataSource *mSource;
+ void *mData;
+ size_t mPageSize;
+ Page *mFirst, *mLast;
+
+ Page *allocate_page();
+
+ Mutex mLock;
+
+ CachingDataSource(const CachingDataSource &);
+ CachingDataSource &operator=(const CachingDataSource &);
+};
+
+} // namespace android
+
+#endif // CACHING_DATASOURCE_H_
diff --git a/include/media/stagefright/CameraSource.h b/include/media/stagefright/CameraSource.h
new file mode 100644
index 000000000000..7042e1ad4960
--- /dev/null
+++ b/include/media/stagefright/CameraSource.h
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#ifndef CAMERA_SOURCE_H_
+
+#define CAMERA_SOURCE_H_
+
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaSource.h>
+#include <utils/List.h>
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class ICamera;
+class ICameraClient;
+class IMemory;
+
+class CameraSource : public MediaSource,
+ public MediaBufferObserver {
+public:
+ static CameraSource *Create();
+
+ virtual ~CameraSource();
+
+ virtual status_t start(MetaData *params = NULL);
+ virtual status_t stop();
+
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+ virtual void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2);
+ virtual void dataCallback(int32_t msgType, const sp<IMemory>& data);
+
+ virtual void signalBufferReturned(MediaBuffer *buffer);
+
+private:
+ CameraSource(const sp<ICamera> &camera, const sp<ICameraClient> &client);
+
+ sp<ICamera> mCamera;
+ sp<ICameraClient> mCameraClient;
+
+ Mutex mLock;
+ Condition mFrameAvailableCondition;
+ List<sp<IMemory> > mFrames;
+
+ int mNumFrames;
+ bool mStarted;
+
+ CameraSource(const CameraSource &);
+ CameraSource &operator=(const CameraSource &);
+};
+
+} // namespace android
+
+#endif // CAMERA_SOURCE_H_
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
new file mode 100644
index 000000000000..31eea27429ac
--- /dev/null
+++ b/include/media/stagefright/DataSource.h
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#ifndef DATA_SOURCE_H_
+
+#define DATA_SOURCE_H_
+
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/List.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class String8;
+
+class DataSource {
+public:
+ DataSource() {}
+ virtual ~DataSource() {}
+
+ virtual ssize_t read_at(off_t offset, void *data, size_t size) = 0;
+
+ // May return ERROR_UNSUPPORTED.
+ virtual status_t getSize(off_t *size);
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ bool sniff(String8 *mimeType, float *confidence);
+
+ typedef bool (*SnifferFunc)(
+ DataSource *source, String8 *mimeType, float *confidence);
+
+ static void RegisterSniffer(SnifferFunc func);
+ static void RegisterDefaultSniffers();
+
+private:
+ static Mutex gSnifferMutex;
+ static List<SnifferFunc> gSniffers;
+
+ DataSource(const DataSource &);
+ DataSource &operator=(const DataSource &);
+};
+
+} // namespace android
+
+#endif // DATA_SOURCE_H_
diff --git a/include/media/stagefright/ESDS.h b/include/media/stagefright/ESDS.h
new file mode 100644
index 000000000000..01bcd1831806
--- /dev/null
+++ b/include/media/stagefright/ESDS.h
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#ifndef ESDS_H_
+
+#define ESDS_H_
+
+#include <stdint.h>
+
+#include <media/stagefright/MediaErrors.h>
+
+namespace android {
+
+class ESDS {
+public:
+ ESDS(const void *data, size_t size);
+ ~ESDS();
+
+ status_t InitCheck() const;
+
+ status_t getCodecSpecificInfo(const void **data, size_t *size) const;
+
+private:
+ enum {
+ kTag_ESDescriptor = 0x03,
+ kTag_DecoderConfigDescriptor = 0x04,
+ kTag_DecoderSpecificInfo = 0x05
+ };
+
+ uint8_t *mData;
+ size_t mSize;
+
+ status_t mInitCheck;
+
+ size_t mDecoderSpecificOffset;
+ size_t mDecoderSpecificLength;
+
+ status_t skipDescriptorHeader(
+ size_t offset, size_t size,
+ uint8_t *tag, size_t *data_offset, size_t *data_size) const;
+
+ status_t parse();
+ status_t parseESDescriptor(size_t offset, size_t size);
+ status_t parseDecoderConfigDescriptor(size_t offset, size_t size);
+
+ ESDS(const ESDS &);
+ ESDS &operator=(const ESDS &);
+};
+
+} // namespace android
+#endif // ESDS_H_
diff --git a/include/media/stagefright/FileSource.h b/include/media/stagefright/FileSource.h
new file mode 100644
index 000000000000..ccbe0efa10bd
--- /dev/null
+++ b/include/media/stagefright/FileSource.h
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#ifndef FILE_SOURCE_H_
+
+#define FILE_SOURCE_H_
+
+#include <stdio.h>
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaErrors.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class FileSource : public DataSource {
+public:
+ FileSource(const char *filename);
+ virtual ~FileSource();
+
+ status_t InitCheck() const;
+
+ virtual ssize_t read_at(off_t offset, void *data, size_t size);
+
+private:
+ FILE *mFile;
+ Mutex mLock;
+
+ FileSource(const FileSource &);
+ FileSource &operator=(const FileSource &);
+};
+
+} // namespace android
+
+#endif // FILE_SOURCE_H_
+
diff --git a/include/media/stagefright/HTTPDataSource.h b/include/media/stagefright/HTTPDataSource.h
new file mode 100644
index 000000000000..0587c7cf1d50
--- /dev/null
+++ b/include/media/stagefright/HTTPDataSource.h
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+#ifndef HTTP_DATASOURCE_H_
+
+#define HTTP_DATASOURCE_H_
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/HTTPStream.h>
+
+namespace android {
+
+class HTTPDataSource : public DataSource {
+public:
+ HTTPDataSource(const char *host, int port, const char *path);
+ HTTPDataSource(const char *uri);
+
+ virtual ~HTTPDataSource();
+
+ // XXXandih
+ status_t InitCheck() const { return OK; }
+
+ virtual ssize_t read_at(off_t offset, void *data, size_t size);
+
+private:
+ enum {
+ kBufferSize = 64 * 1024
+ };
+
+ HTTPStream mHttp;
+ char *mHost;
+ int mPort;
+ char *mPath;
+
+ void *mBuffer;
+ size_t mBufferLength;
+ off_t mBufferOffset;
+
+ HTTPDataSource(const HTTPDataSource &);
+ HTTPDataSource &operator=(const HTTPDataSource &);
+};
+
+} // namespace android
+
+#endif // HTTP_DATASOURCE_H_
+
diff --git a/include/media/stagefright/HTTPStream.h b/include/media/stagefright/HTTPStream.h
new file mode 100644
index 000000000000..3d0d67a26a0d
--- /dev/null
+++ b/include/media/stagefright/HTTPStream.h
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+#ifndef HTTP_STREAM_H_
+
+#define HTTP_STREAM_H_
+
+#include <sys/types.h>
+
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/string.h>
+#include <utils/KeyedVector.h>
+
+namespace android {
+
+class HTTPStream {
+public:
+ HTTPStream();
+ ~HTTPStream();
+
+ status_t connect(const char *server, int port = 80);
+ status_t disconnect();
+
+ status_t send(const char *data, size_t size);
+
+ // Assumes data is a '\0' terminated string.
+ status_t send(const char *data);
+
+ // Receive up to "size" bytes of data.
+ ssize_t receive(void *data, size_t size);
+
+ status_t receive_header(int *http_status);
+
+ // The header key used to retrieve the status line.
+ static const char *kStatusKey;
+
+ bool find_header_value(
+ const string &key, string *value) const;
+
+private:
+ enum State {
+ READY,
+ CONNECTED
+ };
+
+ State mState;
+ int mSocket;
+
+ KeyedVector<string, string> mHeaders;
+
+ // Receive a line of data terminated by CRLF, line will be '\0' terminated
+ // _excluding_ the termianting CRLF.
+ status_t receive_line(char *line, size_t size);
+
+ HTTPStream(const HTTPStream &);
+ HTTPStream &operator=(const HTTPStream &);
+};
+
+} // namespace android
+
+#endif // HTTP_STREAM_H_
diff --git a/include/media/stagefright/MP3Extractor.h b/include/media/stagefright/MP3Extractor.h
new file mode 100644
index 000000000000..09cfb70aab8c
--- /dev/null
+++ b/include/media/stagefright/MP3Extractor.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#ifndef MP3_EXTRACTOR_H_
+
+#define MP3_EXTRACTOR_H_
+
+#include <media/stagefright/MediaExtractor.h>
+
+namespace android {
+
+class DataSource;
+class String8;
+
+class MP3Extractor : public MediaExtractor {
+public:
+ // Extractor assumes ownership of "source".
+ MP3Extractor(DataSource *source);
+
+ ~MP3Extractor();
+
+ status_t countTracks(int *num_tracks);
+ status_t getTrack(int index, MediaSource **source);
+ sp<MetaData> getTrackMetaData(int index);
+
+private:
+ DataSource *mDataSource;
+ off_t mFirstFramePos;
+ sp<MetaData> mMeta;
+ uint32_t mFixedHeader;
+
+ MP3Extractor(const MP3Extractor &);
+ MP3Extractor &operator=(const MP3Extractor &);
+};
+
+bool SniffMP3(DataSource *source, String8 *mimeType, float *confidence);
+
+} // namespace android
+
+#endif // MP3_EXTRACTOR_H_
diff --git a/include/media/stagefright/MPEG4Extractor.h b/include/media/stagefright/MPEG4Extractor.h
new file mode 100644
index 000000000000..51a7e829281e
--- /dev/null
+++ b/include/media/stagefright/MPEG4Extractor.h
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+#ifndef MPEG4_EXTRACTOR_H_
+
+#define MPEG4_EXTRACTOR_H_
+
+#include <media/stagefright/MediaExtractor.h>
+
+namespace android {
+
+class DataSource;
+class SampleTable;
+class String8;
+
+class MPEG4Extractor : public MediaExtractor {
+public:
+ // Extractor assumes ownership of "source".
+ MPEG4Extractor(DataSource *source);
+ ~MPEG4Extractor();
+
+ status_t countTracks(int *num_tracks);
+ status_t getTrack(int index, MediaSource **source);
+ sp<MetaData> getTrackMetaData(int index);
+
+private:
+ struct Track {
+ Track *next;
+ sp<MetaData> meta;
+ uint32_t timescale;
+ SampleTable *sampleTable;
+ };
+
+ DataSource *mDataSource;
+ bool mHaveMetadata;
+
+ Track *mFirstTrack, *mLastTrack;
+
+ uint32_t mHandlerType;
+
+ status_t readMetaData();
+ status_t parseChunk(off_t *offset, int depth);
+
+ MPEG4Extractor(const MPEG4Extractor &);
+ MPEG4Extractor &operator=(const MPEG4Extractor &);
+};
+
+bool SniffMPEG4(DataSource *source, String8 *mimeType, float *confidence);
+
+} // namespace android
+
+#endif // MPEG4_EXTRACTOR_H_
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
new file mode 100644
index 000000000000..40d612769375
--- /dev/null
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+#ifndef MPEG4_WRITER_H_
+
+#define MPEG4_WRITER_H_
+
+#include <stdio.h>
+
+#include <utils/List.h>
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class MediaBuffer;
+class MediaSource;
+class MetaData;
+
+class MPEG4Writer {
+public:
+ MPEG4Writer(const char *filename);
+ ~MPEG4Writer();
+
+ // Caller retains ownership of both meta and source.
+ void addSource(const sp<MetaData> &meta, MediaSource *source);
+ void start();
+ void stop();
+
+ void beginBox(const char *fourcc);
+ void writeInt8(int8_t x);
+ void writeInt16(int16_t x);
+ void writeInt32(int32_t x);
+ void writeInt64(int64_t x);
+ void writeCString(const char *s);
+ void writeFourcc(const char *fourcc);
+ void write(const void *data, size_t size);
+ void endBox();
+
+private:
+ class Track;
+
+ FILE *mFile;
+ off_t mOffset;
+ off_t mMdatOffset;
+ Mutex mLock;
+
+ List<Track *> mTracks;
+
+ List<off_t> mBoxes;
+
+ off_t addSample(MediaBuffer *buffer);
+
+ MPEG4Writer(const MPEG4Writer &);
+ MPEG4Writer &operator=(const MPEG4Writer &);
+};
+
+} // namespace android
+
+#endif // MPEG4_WRITER_H_
diff --git a/include/media/stagefright/MediaBuffer.h b/include/media/stagefright/MediaBuffer.h
new file mode 100644
index 000000000000..c72ed66d46bd
--- /dev/null
+++ b/include/media/stagefright/MediaBuffer.h
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+
+#ifndef MEDIA_BUFFER_H_
+
+#define MEDIA_BUFFER_H_
+
+#include <pthread.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+class MediaBuffer;
+class MediaBufferObserver;
+class MetaData;
+
+class MediaBufferObserver {
+public:
+ MediaBufferObserver() {}
+ virtual ~MediaBufferObserver() {}
+
+ virtual void signalBufferReturned(MediaBuffer *buffer) = 0;
+
+private:
+ MediaBufferObserver(const MediaBufferObserver &);
+ MediaBufferObserver &operator=(const MediaBufferObserver &);
+};
+
+class MediaBuffer {
+public:
+ // The underlying data remains the responsibility of the caller!
+ MediaBuffer(void *data, size_t size);
+
+ MediaBuffer(size_t size);
+
+ // Decrements the reference count and returns the buffer to its
+ // associated MediaBufferGroup if the reference count drops to 0.
+ void release();
+
+ // Increments the reference count.
+ void add_ref();
+
+ void *data() const;
+ size_t size() const;
+
+ size_t range_offset() const;
+ size_t range_length() const;
+
+ void set_range(size_t offset, size_t length);
+
+ sp<MetaData> meta_data();
+
+ // Clears meta data and resets the range to the full extent.
+ void reset();
+
+ void setObserver(MediaBufferObserver *group);
+
+ // Returns a clone of this MediaBuffer increasing its reference count.
+ // The clone references the same data but has its own range and
+ // MetaData.
+ MediaBuffer *clone();
+
+protected:
+ virtual ~MediaBuffer();
+
+private:
+ friend class MediaBufferGroup;
+ friend class OMXDecoder;
+
+ // For use by OMXDecoder, reference count must be 1, drop reference
+ // count to 0 without signalling the observer.
+ void claim();
+
+ MediaBufferObserver *mObserver;
+ MediaBuffer *mNextBuffer;
+ int mRefCount;
+
+ void *mData;
+ size_t mSize, mRangeOffset, mRangeLength;
+
+ bool mOwnsData;
+
+ sp<MetaData> mMetaData;
+
+ MediaBuffer *mOriginal;
+
+ void setNextBuffer(MediaBuffer *buffer);
+ MediaBuffer *nextBuffer();
+
+ int refcount() const;
+
+ MediaBuffer(const MediaBuffer &);
+ MediaBuffer &operator=(const MediaBuffer &);
+};
+
+} // namespace android
+
+#endif // MEDIA_BUFFER_H_
diff --git a/include/media/stagefright/MediaBufferGroup.h b/include/media/stagefright/MediaBufferGroup.h
new file mode 100644
index 000000000000..e95a9c27897c
--- /dev/null
+++ b/include/media/stagefright/MediaBufferGroup.h
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#ifndef MEDIA_BUFFER_GROUP_H_
+
+#define MEDIA_BUFFER_GROUP_H_
+
+#include <utils/Errors.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class MediaBuffer;
+class MetaData;
+
+class MediaBufferGroup : public MediaBufferObserver {
+public:
+ MediaBufferGroup();
+ ~MediaBufferGroup();
+
+ void add_buffer(MediaBuffer *buffer);
+
+ // Blocks until a buffer is available and returns it to the caller,
+ // the returned buffer will have a reference count of 1.
+ status_t acquire_buffer(MediaBuffer **buffer);
+
+protected:
+ virtual void signalBufferReturned(MediaBuffer *buffer);
+
+private:
+ friend class MediaBuffer;
+
+ Mutex mLock;
+ Condition mCondition;
+
+ MediaBuffer *mFirstBuffer, *mLastBuffer;
+
+ MediaBufferGroup(const MediaBufferGroup &);
+ MediaBufferGroup &operator=(const MediaBufferGroup &);
+};
+
+} // namespace android
+
+#endif // MEDIA_BUFFER_GROUP_H_
diff --git a/include/media/stagefright/MediaErrors.h b/include/media/stagefright/MediaErrors.h
new file mode 100644
index 000000000000..2bb0ed6ce0b9
--- /dev/null
+++ b/include/media/stagefright/MediaErrors.h
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+#ifndef MEDIA_ERRORS_H_
+
+#define MEDIA_ERRORS_H_
+
+#include <utils/Errors.h>
+
+namespace android {
+
+enum {
+ MEDIA_ERROR_BASE = -1000,
+
+ ERROR_ALREADY_CONNECTED = MEDIA_ERROR_BASE,
+ ERROR_NOT_CONNECTED = MEDIA_ERROR_BASE - 1,
+ ERROR_UNKNOWN_HOST = MEDIA_ERROR_BASE - 2,
+ ERROR_CANNOT_CONNECT = MEDIA_ERROR_BASE - 3,
+ ERROR_IO = MEDIA_ERROR_BASE - 4,
+ ERROR_CONNECTION_LOST = MEDIA_ERROR_BASE - 5,
+ ERROR_MALFORMED = MEDIA_ERROR_BASE - 7,
+ ERROR_OUT_OF_RANGE = MEDIA_ERROR_BASE - 8,
+ ERROR_BUFFER_TOO_SMALL = MEDIA_ERROR_BASE - 9,
+ ERROR_UNSUPPORTED = MEDIA_ERROR_BASE - 10,
+ ERROR_END_OF_STREAM = MEDIA_ERROR_BASE - 11,
+};
+
+} // namespace android
+
+#endif // MEDIA_ERRORS_H_
diff --git a/include/media/stagefright/MediaExtractor.h b/include/media/stagefright/MediaExtractor.h
new file mode 100644
index 000000000000..38f8e5bc3118
--- /dev/null
+++ b/include/media/stagefright/MediaExtractor.h
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#ifndef MEDIA_EXTRACTOR_H_
+
+#define MEDIA_EXTRACTOR_H_
+
+#include <utils/RefBase.h>
+
+namespace android {
+
+class DataSource;
+class MediaSource;
+class MetaData;
+
+class MediaExtractor {
+public:
+ static MediaExtractor *Create(DataSource *source, const char *mime = NULL);
+
+ virtual ~MediaExtractor() {}
+
+ virtual status_t countTracks(int *num_tracks) = 0;
+ virtual status_t getTrack(int index, MediaSource **source) = 0;
+ virtual sp<MetaData> getTrackMetaData(int index) = 0;
+
+protected:
+ MediaExtractor() {}
+
+private:
+ MediaExtractor(const MediaExtractor &);
+ MediaExtractor &operator=(const MediaExtractor &);
+};
+
+} // namespace android
+
+#endif // MEDIA_EXTRACTOR_H_
diff --git a/include/media/stagefright/MediaPlayerImpl.h b/include/media/stagefright/MediaPlayerImpl.h
new file mode 100644
index 000000000000..c48400c530e9
--- /dev/null
+++ b/include/media/stagefright/MediaPlayerImpl.h
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+#ifndef MEDIA_PLAYER_IMPL_H_
+
+#define MEDIA_PLAYER_IMPL_H_
+
+#include <pthread.h>
+
+#include <media/MediaPlayerInterface.h>
+#include <media/stagefright/OMXClient.h>
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class AudioPlayer;
+class ISurface;
+class MediaExtractor;
+class MediaBuffer;
+class MediaSource;
+class MemoryHeapPmem;
+class MetaData;
+class OMXDecoder;
+class Surface;
+class TimeSource;
+class VideoRenderer;
+
+class MediaPlayerImpl {
+public:
+ MediaPlayerImpl(const char *uri);
+
+ status_t initCheck() const;
+
+ // Assumes ownership of "fd".
+ MediaPlayerImpl(int fd, int64_t offset, int64_t length);
+
+ ~MediaPlayerImpl();
+
+ void play();
+ void pause();
+ bool isPlaying() const;
+
+ void setSurface(const sp<Surface> &surface);
+ void setISurface(const sp<ISurface> &isurface);
+
+ void setAudioSink(const sp<MediaPlayerBase::AudioSink> &audioSink);
+
+ int32_t getWidth() const { return mVideoWidth; }
+ int32_t getHeight() const { return mVideoHeight; }
+
+ int64_t getDuration();
+ int64_t getPosition();
+ status_t seekTo(int64_t time);
+
+private:
+ status_t mInitCheck;
+
+ OMXClient mClient;
+
+ MediaExtractor *mExtractor;
+
+ TimeSource *mTimeSource;
+
+ MediaSource *mAudioSource;
+ OMXDecoder *mAudioDecoder;
+ AudioPlayer *mAudioPlayer;
+
+ MediaSource *mVideoSource;
+ MediaSource *mVideoDecoder;
+ int32_t mVideoWidth, mVideoHeight;
+ int64_t mVideoPosition;
+
+ int64_t mDuration;
+
+ bool mPlaying;
+ bool mPaused;
+
+ int64_t mTimeSourceDeltaUs;
+
+ sp<Surface> mSurface;
+ sp<ISurface> mISurface;
+ VideoRenderer *mRenderer;
+
+ sp<MediaPlayerBase::AudioSink> mAudioSink;
+
+ Mutex mLock;
+ pthread_t mVideoThread;
+
+ bool mSeeking;
+ int64_t mSeekTimeUs;
+
+ size_t mFrameSize;
+ bool mUseSoftwareColorConversion;
+
+ void init();
+
+ static void *VideoWrapper(void *me);
+ void videoEntry();
+
+ void setAudioSource(MediaSource *source);
+ void setVideoSource(MediaSource *source);
+
+ MediaSource *makeShoutcastSource(const char *path);
+
+ void displayOrDiscardFrame(MediaBuffer *buffer, int64_t pts_us);
+ void populateISurface();
+ void depopulateISurface();
+ void sendFrameToISurface(MediaBuffer *buffer);
+
+ void stop();
+
+ MediaPlayerImpl(const MediaPlayerImpl &);
+ MediaPlayerImpl &operator=(const MediaPlayerImpl &);
+};
+
+} // namespace android
+
+#endif // MEDIA_PLAYER_IMPL_H_
diff --git a/include/media/stagefright/MediaSource.h b/include/media/stagefright/MediaSource.h
new file mode 100644
index 000000000000..eb07f6857b39
--- /dev/null
+++ b/include/media/stagefright/MediaSource.h
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+#ifndef MEDIA_SOURCE_H_
+
+#define MEDIA_SOURCE_H_
+
+#include <sys/types.h>
+
+#include <utils/RefBase.h>
+
+namespace android {
+
+class MediaBuffer;
+class MetaData;
+
+struct MediaSource {
+ MediaSource();
+ virtual ~MediaSource();
+
+ // To be called before any other methods on this object, except
+ // getFormat().
+ virtual status_t start(MetaData *params = NULL) = 0;
+
+ // Any blocking read call returns immediately with a result of NO_INIT.
+ // It is an error to call any methods other than start after this call
+ // returns. Any buffers the object may be holding onto at the time of
+ // the stop() call are released.
+ // Also, it is imperative that any buffers output by this object and
+ // held onto by callers be released before a call to stop() !!!
+ virtual status_t stop() = 0;
+
+ // Returns the format of the data output by this media source.
+ virtual sp<MetaData> getFormat() = 0;
+
+ struct ReadOptions;
+
+ // Returns a new buffer of data. Call blocks until a
+ // buffer is available, an error is encountered of the end of the stream
+ // is reached.
+ // End of stream is signalled by a result of ERROR_END_OF_STREAM.
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options = NULL) = 0;
+
+ // Options that modify read() behaviour. The default is to
+ // a) not request a seek
+ // b) not be late, i.e. lateness_us = 0
+ struct ReadOptions {
+ ReadOptions();
+
+ // Reset everything back to defaults.
+ void reset();
+
+ void setSeekTo(int64_t time_us);
+ void clearSeekTo();
+ bool getSeekTo(int64_t *time_us) const;
+
+ void setLateBy(int64_t lateness_us);
+ int64_t getLateBy() const;
+
+ private:
+ enum Options {
+ kSeekTo_Option = 1,
+ };
+
+ uint32_t mOptions;
+ int64_t mSeekTimeUs;
+ int64_t mLatenessUs;
+ };
+
+private:
+ MediaSource(const MediaSource &);
+ MediaSource &operator=(const MediaSource &);
+};
+
+} // namespace android
+
+#endif // MEDIA_SOURCE_H_
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
new file mode 100644
index 000000000000..04805dab5c9d
--- /dev/null
+++ b/include/media/stagefright/MetaData.h
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+#ifndef META_DATA_H_
+
+#define META_DATA_H_
+
+#include <sys/types.h>
+
+#include <stdint.h>
+
+#include <utils/RefBase.h>
+#include <utils/KeyedVector.h>
+
+namespace android {
+
+enum {
+ kKeyMIMEType = 'mime',
+ kKeyWidth = 'widt',
+ kKeyHeight = 'heig',
+ kKeyChannelCount = '#chn',
+ kKeySampleRate = 'srte',
+ kKeyBitRate = 'brte',
+ kKeyESDS = 'esds',
+ kKeyAVCC = 'avcc',
+ kKeyTimeUnits = '#tim',
+ kKeyTimeScale = 'scal',
+ kKeyNeedsNALFraming = 'NALf',
+ kKeyIsSyncFrame = 'sync',
+ kKeyDuration = 'dura',
+ kKeyColorFormat = 'colf',
+ kKeyPlatformPrivate = 'priv',
+ kKeyDecoderComponent = 'decC',
+};
+
+enum {
+ kTypeESDS = 'esds',
+ kTypeAVCC = 'avcc',
+};
+
+class MetaData : public RefBase {
+public:
+ MetaData();
+ MetaData(const MetaData &from);
+
+ enum Type {
+ TYPE_NONE = 'none',
+ TYPE_C_STRING = 'cstr',
+ TYPE_INT32 = 'in32',
+ TYPE_FLOAT = 'floa',
+ TYPE_POINTER = 'ptr ',
+ };
+
+ void clear();
+ bool remove(uint32_t key);
+
+ bool setCString(uint32_t key, const char *value);
+ bool setInt32(uint32_t key, int32_t value);
+ bool setFloat(uint32_t key, float value);
+ bool setPointer(uint32_t key, void *value);
+
+ bool findCString(uint32_t key, const char **value);
+ bool findInt32(uint32_t key, int32_t *value);
+ bool findFloat(uint32_t key, float *value);
+ bool findPointer(uint32_t key, void **value);
+
+ bool setData(uint32_t key, uint32_t type, const void *data, size_t size);
+
+ bool findData(uint32_t key, uint32_t *type,
+ const void **data, size_t *size) const;
+
+protected:
+ virtual ~MetaData();
+
+private:
+ struct typed_data {
+ typed_data();
+ ~typed_data();
+
+ typed_data(const MetaData::typed_data &);
+ typed_data &operator=(const MetaData::typed_data &);
+
+ void clear();
+ void setData(uint32_t type, const void *data, size_t size);
+ void getData(uint32_t *type, const void **data, size_t *size) const;
+
+ private:
+ uint32_t mType;
+ size_t mSize;
+
+ union {
+ void *ext_data;
+ float reservoir;
+ } u;
+
+ bool usesReservoir() const {
+ return mSize <= sizeof(u.reservoir);
+ }
+
+ void allocateStorage(size_t size);
+ void freeStorage();
+
+ void *storage() {
+ return usesReservoir() ? &u.reservoir : u.ext_data;
+ }
+
+ const void *storage() const {
+ return usesReservoir() ? &u.reservoir : u.ext_data;
+ }
+ };
+
+ KeyedVector<uint32_t, typed_data> mItems;
+
+ // MetaData &operator=(const MetaData &);
+};
+
+} // namespace android
+
+#endif // META_DATA_H_
diff --git a/include/media/stagefright/MmapSource.h b/include/media/stagefright/MmapSource.h
new file mode 100644
index 000000000000..a8bd57f0d815
--- /dev/null
+++ b/include/media/stagefright/MmapSource.h
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#ifndef MMAP_SOURCE_H_
+
+#define MMAP_SOURCE_H_
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaErrors.h>
+
+namespace android {
+
+class MmapSource : public DataSource {
+public:
+ MmapSource(const char *filename);
+
+ // Assumes ownership of "fd".
+ MmapSource(int fd, int64_t offset, int64_t length);
+
+ virtual ~MmapSource();
+
+ status_t InitCheck() const;
+
+ virtual ssize_t read_at(off_t offset, void *data, size_t size);
+ virtual status_t getSize(off_t *size);
+
+private:
+ int mFd;
+ void *mBase;
+ size_t mSize;
+
+ MmapSource(const MmapSource &);
+ MmapSource &operator=(const MmapSource &);
+};
+
+} // namespace android
+
+#endif // MMAP_SOURCE_H_
+
diff --git a/include/media/stagefright/OMXClient.h b/include/media/stagefright/OMXClient.h
new file mode 100644
index 000000000000..454c38be5e70
--- /dev/null
+++ b/include/media/stagefright/OMXClient.h
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ */
+
+#ifndef OMX_CLIENT_H_
+
+#define OMX_CLIENT_H_
+
+#include <media/IOMX.h>
+
+#include <utils/KeyedVector.h>
+#include <utils/List.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class OMXObserver {
+public:
+ OMXObserver();
+ virtual ~OMXObserver();
+
+ void postMessage(const omx_message &msg);
+
+protected:
+ virtual void onOMXMessage(const omx_message &msg) = 0;
+
+private:
+ friend class OMXClient;
+
+ pthread_t mThread;
+ Mutex mLock;
+ Condition mQueueNotEmpty;
+ List<omx_message> mQueue;
+
+ void start();
+ void stop();
+
+ static void *ThreadWrapper(void *me);
+ void threadEntry();
+
+ OMXObserver(const OMXObserver &);
+ OMXObserver &operator=(const OMXObserver &);
+};
+
+class OMXClient;
+
+class OMXClientReflector : public BnOMXObserver {
+public:
+ OMXClientReflector(OMXClient *client);
+
+ virtual void on_message(const omx_message &msg);
+ void reset();
+
+private:
+ OMXClient *mClient;
+
+ OMXClientReflector(const OMXClientReflector &);
+ OMXClientReflector &operator=(const OMXClientReflector &);
+};
+
+class OMXClient {
+public:
+ friend class OMXClientReflector;
+
+ OMXClient();
+ ~OMXClient();
+
+ status_t connect();
+ void disconnect();
+
+ sp<IOMX> interface() {
+ return mOMX;
+ }
+
+ status_t registerObserver(IOMX::node_id node, OMXObserver *observer);
+ void unregisterObserver(IOMX::node_id node);
+
+ status_t fillBuffer(IOMX::node_id node, IOMX::buffer_id buffer);
+
+ status_t emptyBuffer(
+ IOMX::node_id node, IOMX::buffer_id buffer,
+ OMX_U32 range_offset, OMX_U32 range_length,
+ OMX_U32 flags, OMX_TICKS timestamp);
+
+ status_t send_command(
+ IOMX::node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param);
+
+private:
+ sp<IOMX> mOMX;
+
+ int mSock;
+ Mutex mLock;
+ pthread_t mThread;
+
+ KeyedVector<IOMX::node_id, OMXObserver *> mObservers;
+
+ sp<OMXClientReflector> mReflector;
+
+#if IOMX_USES_SOCKETS
+ static void *ThreadWrapper(void *me);
+ void threadEntry();
+#endif
+
+ bool onOMXMessage(const omx_message &msg);
+
+ OMXClient(const OMXClient &);
+ OMXClient &operator=(const OMXClient &);
+};
+
+} // namespace android
+
+#endif // OMX_CLIENT_H_
diff --git a/include/media/stagefright/OMXDecoder.h b/include/media/stagefright/OMXDecoder.h
new file mode 100644
index 000000000000..085945710d8f
--- /dev/null
+++ b/include/media/stagefright/OMXDecoder.h
@@ -0,0 +1,158 @@
+/*
+ * 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.
+ */
+
+#ifndef OMX_DECODER_H_
+
+#define OMX_DECODER_H_
+
+#include <binder/MemoryDealer.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/OMXClient.h>
+#include <utils/KeyedVector.h>
+#include <utils/List.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class OMXMediaBuffer;
+
+class OMXDecoder : public MediaSource,
+ public OMXObserver,
+ public MediaBufferObserver {
+public:
+ static OMXDecoder *Create(
+ OMXClient *client, const sp<MetaData> &data);
+
+ static OMXDecoder *CreateEncoder(
+ OMXClient *client, const sp<MetaData> &data);
+
+ virtual ~OMXDecoder();
+
+ // Caller retains ownership of "source".
+ void setSource(MediaSource *source);
+
+ virtual status_t start(MetaData *params = NULL);
+ virtual status_t stop();
+
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+ void addCodecSpecificData(const void *data, size_t size);
+
+ // from OMXObserver
+ virtual void onOMXMessage(const omx_message &msg);
+
+ // from MediaBufferObserver
+ virtual void signalBufferReturned(MediaBuffer *buffer);
+
+private:
+ enum {
+ kPortIndexInput = 0,
+ kPortIndexOutput = 1
+ };
+
+ enum PortStatus {
+ kPortStatusActive = 0,
+ kPortStatusDisabled = 1,
+ kPortStatusShutdown = 2,
+ kPortStatusFlushing = 3
+ };
+
+ OMXClient *mClient;
+ sp<IOMX> mOMX;
+ IOMX::node_id mNode;
+ char *mComponentName;
+ bool mIsMP3;
+
+ MediaSource *mSource;
+ sp<MetaData> mOutputFormat;
+
+ Mutex mLock;
+ Condition mOutputBufferAvailable;
+
+ List<MediaBuffer *> mOutputBuffers;
+
+ struct CodecSpecificData {
+ void *data;
+ size_t size;
+ };
+
+ List<CodecSpecificData> mCodecSpecificData;
+ List<CodecSpecificData>::iterator mCodecSpecificDataIterator;
+
+ volatile OMX_STATETYPE mState;
+ OMX_U32 mPortStatusMask;
+ bool mShutdownInitiated;
+
+ typedef List<IOMX::buffer_id> BufferList;
+ Vector<BufferList> mBuffers;
+
+ KeyedVector<IOMX::buffer_id, sp<IMemory> > mBufferMap;
+ KeyedVector<IOMX::buffer_id, OMXMediaBuffer *> mMediaBufferMap;
+
+ sp<MemoryDealer> mDealer;
+
+ bool mSeeking;
+ int64_t mSeekTimeUs;
+
+ bool mStarted;
+ status_t mErrorCondition;
+ bool mReachedEndOfInput;
+
+ OMXDecoder(OMXClient *client, IOMX::node_id node,
+ const char *mime, const char *codec);
+
+ void setPortStatus(OMX_U32 port_index, PortStatus status);
+ PortStatus getPortStatus(OMX_U32 port_index) const;
+
+ void allocateBuffers(OMX_U32 port_index);
+
+ void setAMRFormat();
+ void setAACFormat();
+ void setVideoOutputFormat(OMX_U32 width, OMX_U32 height);
+ void setup();
+ void dumpPortDefinition(OMX_U32 port_index);
+
+ void onStart();
+ void onEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
+ void onEventCmdComplete(OMX_COMMANDTYPE type, OMX_U32 data);
+ void onEventPortSettingsChanged(OMX_U32 port_index);
+ void onStateChanged(OMX_STATETYPE to);
+ void onEmptyBufferDone(IOMX::buffer_id buffer);
+ void onFillBufferDone(const omx_message &msg);
+
+ void onRealEmptyBufferDone(IOMX::buffer_id buffer);
+ void onRealFillBufferDone(const omx_message &msg);
+
+ void initiateShutdown();
+
+ void freeInputBuffer(IOMX::buffer_id buffer);
+ void freeOutputBuffer(IOMX::buffer_id buffer);
+
+ void postStart();
+ void postEmptyBufferDone(IOMX::buffer_id buffer);
+ void postInitialFillBuffer(IOMX::buffer_id buffer);
+
+ OMXDecoder(const OMXDecoder &);
+ OMXDecoder &operator=(const OMXDecoder &);
+};
+
+} // namespace android
+
+#endif // OMX_DECODER_H_
diff --git a/include/media/stagefright/QComHardwareRenderer.h b/include/media/stagefright/QComHardwareRenderer.h
new file mode 100644
index 000000000000..8292dd560077
--- /dev/null
+++ b/include/media/stagefright/QComHardwareRenderer.h
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#ifndef QCOM_HARDWARE_RENDERER_H_
+
+#define QCOM_HARDWARE_RENDERER_H_
+
+#include <media/stagefright/VideoRenderer.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+class ISurface;
+class MemoryHeapPmem;
+
+class QComHardwareRenderer : public VideoRenderer {
+public:
+ QComHardwareRenderer(
+ const sp<ISurface> &surface,
+ size_t displayWidth, size_t displayHeight,
+ size_t decodedWidth, size_t decodedHeight);
+
+ virtual ~QComHardwareRenderer();
+
+ virtual void render(
+ const void *data, size_t size, void *platformPrivate);
+
+private:
+ sp<ISurface> mISurface;
+ size_t mDisplayWidth, mDisplayHeight;
+ size_t mDecodedWidth, mDecodedHeight;
+ size_t mFrameSize;
+ sp<MemoryHeapPmem> mMemoryHeap;
+
+ bool getOffset(void *platformPrivate, size_t *offset);
+ void publishBuffers(uint32_t pmem_fd);
+
+ QComHardwareRenderer(const QComHardwareRenderer &);
+ QComHardwareRenderer &operator=(const QComHardwareRenderer &);
+};
+
+} // namespace android
+
+#endif // QCOM_HARDWARE_RENDERER_H_
diff --git a/include/media/stagefright/SampleTable.h b/include/media/stagefright/SampleTable.h
new file mode 100644
index 000000000000..712da1061232
--- /dev/null
+++ b/include/media/stagefright/SampleTable.h
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+#ifndef SAMPLE_TABLE_H_
+
+#define SAMPLE_TABLE_H_
+
+#include <sys/types.h>
+#include <stdint.h>
+
+#include <media/stagefright/MediaErrors.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class DataSource;
+
+class SampleTable {
+public:
+ // Caller retains ownership of "source".
+ SampleTable(DataSource *source);
+ ~SampleTable();
+
+ // type can be 'stco' or 'co64'.
+ status_t setChunkOffsetParams(
+ uint32_t type, off_t data_offset, off_t data_size);
+
+ status_t setSampleToChunkParams(off_t data_offset, off_t data_size);
+
+ // type can be 'stsz' or 'stz2'.
+ status_t setSampleSizeParams(
+ uint32_t type, off_t data_offset, off_t data_size);
+
+ status_t setTimeToSampleParams(off_t data_offset, off_t data_size);
+
+ status_t setSyncSampleParams(off_t data_offset, off_t data_size);
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ uint32_t countChunkOffsets() const;
+ status_t getChunkOffset(uint32_t chunk_index, off_t *offset);
+
+ status_t getChunkForSample(
+ uint32_t sample_index, uint32_t *chunk_index,
+ uint32_t *chunk_relative_sample_index, uint32_t *desc_index);
+
+ uint32_t countSamples() const;
+ status_t getSampleSize(uint32_t sample_index, size_t *sample_size);
+
+ status_t getSampleOffsetAndSize(
+ uint32_t sample_index, off_t *offset, size_t *size);
+
+ status_t getMaxSampleSize(size_t *size);
+
+ status_t getDecodingTime(uint32_t sample_index, uint32_t *time);
+
+ enum {
+ kSyncSample_Flag = 1
+ };
+ status_t findClosestSample(
+ uint32_t req_time, uint32_t *sample_index, uint32_t flags);
+
+ status_t findClosestSyncSample(
+ uint32_t start_sample_index, uint32_t *sample_index);
+
+private:
+ DataSource *mDataSource;
+ Mutex mLock;
+
+ off_t mChunkOffsetOffset;
+ uint32_t mChunkOffsetType;
+ uint32_t mNumChunkOffsets;
+
+ off_t mSampleToChunkOffset;
+ uint32_t mNumSampleToChunkOffsets;
+
+ off_t mSampleSizeOffset;
+ uint32_t mSampleSizeFieldSize;
+ uint32_t mDefaultSampleSize;
+ uint32_t mNumSampleSizes;
+
+ uint32_t mTimeToSampleCount;
+ uint32_t *mTimeToSample;
+
+ off_t mSyncSampleOffset;
+ uint32_t mNumSyncSamples;
+
+ SampleTable(const SampleTable &);
+ SampleTable &operator=(const SampleTable &);
+};
+
+} // namespace android
+
+#endif // SAMPLE_TABLE_H_
diff --git a/include/media/stagefright/ShoutcastSource.h b/include/media/stagefright/ShoutcastSource.h
new file mode 100644
index 000000000000..352857acedf6
--- /dev/null
+++ b/include/media/stagefright/ShoutcastSource.h
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+#ifndef SHOUTCAST_SOURCE_H_
+
+#define SHOUTCAST_SOURCE_H_
+
+#include <sys/types.h>
+
+#include <media/stagefright/MediaSource.h>
+
+namespace android {
+
+class HTTPStream;
+class MediaBufferGroup;
+
+class ShoutcastSource : public MediaSource {
+public:
+ // Assumes ownership of "http".
+ ShoutcastSource(HTTPStream *http);
+ virtual ~ShoutcastSource();
+
+ virtual status_t start(MetaData *params = NULL);
+ virtual status_t stop();
+
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+private:
+ HTTPStream *mHttp;
+ size_t mMetaDataOffset;
+ size_t mBytesUntilMetaData;
+
+ MediaBufferGroup *mGroup;
+ bool mStarted;
+
+ ShoutcastSource(const ShoutcastSource &);
+ ShoutcastSource &operator= (const ShoutcastSource &);
+};
+
+} // namespace android
+
+#endif // SHOUTCAST_SOURCE_H_
+
diff --git a/include/media/stagefright/SoftwareRenderer.h b/include/media/stagefright/SoftwareRenderer.h
new file mode 100644
index 000000000000..705b91478d6b
--- /dev/null
+++ b/include/media/stagefright/SoftwareRenderer.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#ifndef SOFTWARE_RENDERER_H_
+
+#define SOFTWARE_RENDERER_H_
+
+#include <media/stagefright/VideoRenderer.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+class ISurface;
+class MemoryHeapBase;
+
+class SoftwareRenderer : public VideoRenderer {
+public:
+ SoftwareRenderer(
+ const sp<ISurface> &surface,
+ size_t displayWidth, size_t displayHeight,
+ size_t decodedWidth, size_t decodedHeight);
+
+ virtual ~SoftwareRenderer();
+
+ virtual void render(
+ const void *data, size_t size, void *platformPrivate);
+
+private:
+ sp<ISurface> mISurface;
+ size_t mDisplayWidth, mDisplayHeight;
+ size_t mDecodedWidth, mDecodedHeight;
+ size_t mFrameSize;
+ sp<MemoryHeapBase> mMemoryHeap;
+ int mIndex;
+
+ SoftwareRenderer(const SoftwareRenderer &);
+ SoftwareRenderer &operator=(const SoftwareRenderer &);
+};
+
+} // namespace android
+
+#endif // SOFTWARE_RENDERER_H_
diff --git a/include/media/stagefright/SurfaceRenderer.h b/include/media/stagefright/SurfaceRenderer.h
new file mode 100644
index 000000000000..298ab50a87d4
--- /dev/null
+++ b/include/media/stagefright/SurfaceRenderer.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#ifndef SURFACE_RENDERER_H_
+
+#define SURFACE_RENDERER_H_
+
+#include <media/stagefright/VideoRenderer.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+class Surface;
+
+class SurfaceRenderer : public VideoRenderer {
+public:
+ SurfaceRenderer(
+ const sp<Surface> &surface,
+ size_t displayWidth, size_t displayHeight,
+ size_t decodedWidth, size_t decodedHeight);
+
+ virtual ~SurfaceRenderer();
+
+ virtual void render(
+ const void *data, size_t size, void *platformPrivate);
+
+private:
+ sp<Surface> mSurface;
+ size_t mDisplayWidth, mDisplayHeight;
+ size_t mDecodedWidth, mDecodedHeight;
+
+ SurfaceRenderer(const SurfaceRenderer &);
+ SurfaceRenderer &operator=(const SurfaceRenderer &);
+};
+
+} // namespace android
+
+#endif // SURFACE_RENDERER_H_
diff --git a/include/media/stagefright/TimeSource.h b/include/media/stagefright/TimeSource.h
new file mode 100644
index 000000000000..443673de4692
--- /dev/null
+++ b/include/media/stagefright/TimeSource.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#ifndef TIME_SOURCE_H_
+
+#define TIME_SOURCE_H_
+
+#include <stdint.h>
+
+namespace android {
+
+class TimeSource {
+public:
+ TimeSource() {}
+ virtual ~TimeSource() {}
+
+ virtual int64_t getRealTimeUs() = 0;
+
+private:
+ TimeSource(const TimeSource &);
+ TimeSource &operator=(const TimeSource &);
+};
+
+class SystemTimeSource : public TimeSource {
+public:
+ SystemTimeSource();
+
+ virtual int64_t getRealTimeUs();
+
+private:
+ static int64_t GetSystemTimeUs();
+
+ int64_t mStartTimeUs;
+};
+
+} // namespace android
+
+#endif // TIME_SOURCE_H_
diff --git a/include/media/stagefright/TimedEventQueue.h b/include/media/stagefright/TimedEventQueue.h
new file mode 100644
index 000000000000..a2644219cec5
--- /dev/null
+++ b/include/media/stagefright/TimedEventQueue.h
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ */
+
+#ifndef TIMED_EVENT_QUEUE_H_
+
+#define TIMED_EVENT_QUEUE_H_
+
+#include <pthread.h>
+
+#include <utils/List.h>
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+
+namespace android {
+
+struct TimedEventQueue {
+
+ struct Event : public RefBase {
+ Event() {}
+ virtual ~Event() {}
+
+ protected:
+ virtual void fire(TimedEventQueue *queue, int64_t now_us) = 0;
+
+ private:
+ friend class TimedEventQueue;
+
+ Event(const Event &);
+ Event &operator=(const Event &);
+ };
+
+ TimedEventQueue();
+ ~TimedEventQueue();
+
+ // Start executing the event loop.
+ void start();
+
+ // Stop executing the event loop, if flush is false, any pending
+ // events are discarded, otherwise the queue will stop (and this call
+ // return) once all pending events have been handled.
+ void stop(bool flush = false);
+
+ // Posts an event to the front of the queue (after all events that
+ // have previously been posted to the front but before timed events).
+ void postEvent(const sp<Event> &event);
+
+ void postEventToBack(const sp<Event> &event);
+
+ // It is an error to post an event with a negative delay.
+ void postEventWithDelay(const sp<Event> &event, int64_t delay_us);
+
+ // If the event is to be posted at a time that has already passed,
+ // it will fire as soon as possible.
+ void postTimedEvent(const sp<Event> &event, int64_t realtime_us);
+
+ // Returns true iff event is currently in the queue and has been
+ // successfully cancelled. In this case the event will have been
+ // removed from the queue and won't fire.
+ bool cancelEvent(const sp<Event> &event);
+
+ static int64_t getRealTimeUs();
+
+private:
+ struct QueueItem {
+ sp<Event> event;
+ int64_t realtime_us;
+ };
+
+ struct StopEvent : public TimedEventQueue::Event {
+ virtual void fire(TimedEventQueue *queue, int64_t now_us) {
+ queue->mStopped = true;
+ }
+ };
+
+ pthread_t mThread;
+ List<QueueItem> mQueue;
+ Mutex mLock;
+ Condition mQueueNotEmptyCondition;
+ Condition mQueueHeadChangedCondition;
+
+ bool mRunning;
+ bool mStopped;
+
+ static void *ThreadWrapper(void *me);
+ void threadEntry();
+
+ TimedEventQueue(const TimedEventQueue &);
+ TimedEventQueue &operator=(const TimedEventQueue &);
+};
+
+} // namespace android
+
+#endif // TIMED_EVENT_QUEUE_H_
diff --git a/include/media/stagefright/Utils.h b/include/media/stagefright/Utils.h
new file mode 100644
index 000000000000..30c7f1158138
--- /dev/null
+++ b/include/media/stagefright/Utils.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#ifndef UTILS_H_
+
+#define UTILS_H_
+
+#include <stdint.h>
+
+namespace android {
+
+#define FOURCC(c1, c2, c3, c4) \
+ (c1 << 24 | c2 << 16 | c3 << 8 | c4)
+
+uint16_t U16_AT(const uint8_t *ptr);
+uint32_t U32_AT(const uint8_t *ptr);
+uint64_t U64_AT(const uint8_t *ptr);
+
+uint64_t ntoh64(uint64_t x);
+uint64_t hton64(uint64_t x);
+
+} // namespace android
+
+#endif // UTILS_H_
diff --git a/include/media/stagefright/VideoRenderer.h b/include/media/stagefright/VideoRenderer.h
new file mode 100644
index 000000000000..f80b277b47fd
--- /dev/null
+++ b/include/media/stagefright/VideoRenderer.h
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+#ifndef VIDEO_RENDERER_H_
+
+#define VIDEO_RENDERER_H_
+
+#include <sys/types.h>
+
+namespace android {
+
+class VideoRenderer {
+public:
+ virtual ~VideoRenderer() {}
+
+ virtual void render(
+ const void *data, size_t size, void *platformPrivate) = 0;
+
+protected:
+ VideoRenderer() {}
+
+ VideoRenderer(const VideoRenderer &);
+ VideoRenderer &operator=(const VideoRenderer &);
+};
+
+} // namespace android
+
+#endif // VIDEO_RENDERER_H_
diff --git a/include/media/stagefright/string.h b/include/media/stagefright/string.h
new file mode 100644
index 000000000000..5dc711653f04
--- /dev/null
+++ b/include/media/stagefright/string.h
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#ifndef STRING_H_
+
+#define STRING_H_
+
+#include <utils/String8.h>
+
+namespace android {
+
+class string {
+public:
+ typedef size_t size_type;
+ static size_type npos;
+
+ string();
+ string(const char *s);
+ string(const char *s, size_t length);
+ string(const string &from, size_type start, size_type length = npos);
+
+ const char *c_str() const;
+ size_type size() const;
+
+ void clear();
+ void erase(size_type from, size_type length);
+
+ size_type find(char c) const;
+
+ bool operator<(const string &other) const;
+ bool operator==(const string &other) const;
+
+ string &operator+=(char c);
+
+private:
+ String8 mString;
+};
+
+} // namespace android
+
+#endif // STRING_H_
diff --git a/include/private/ui/SharedState.h b/include/private/ui/SharedState.h
index 3bc7979ca810..c9f6b5edb9ab 100644
--- a/include/private/ui/SharedState.h
+++ b/include/private/ui/SharedState.h
@@ -98,6 +98,8 @@ struct layer_cblk_t // (128 bytes)
struct per_client_cblk_t // 4KB max
{
+ per_client_cblk_t() : lock(Mutex::SHARED) { }
+
Mutex lock;
Condition cv;
layer_cblk_t layers[NUM_LAYERS_MAX] __attribute__((aligned(32)));
diff --git a/include/ui/EventHub.h b/include/ui/EventHub.h
index e12c4f13ea0d..d62fd7d1af6a 100644
--- a/include/ui/EventHub.h
+++ b/include/ui/EventHub.h
@@ -73,6 +73,13 @@ public:
int getKeycodeState(int key) const;
int getKeycodeState(int32_t deviceId, int key) const;
+ status_t scancodeToKeycode(int32_t deviceId, int scancode,
+ int32_t* outKeycode, uint32_t* outFlags) const;
+
+ // exclude a particular device from opening
+ // this can be used to ignore input devices for sensors
+ void addExcludedDevice(const char* deviceName);
+
// special type codes when devices are added/removed.
enum {
DEVICE_ADDED = 0x10000000,
@@ -85,10 +92,9 @@ public:
virtual bool getEvent(int32_t* outDeviceId, int32_t* outType,
int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
int32_t* outValue, nsecs_t* outWhen);
-
+
protected:
virtual ~EventHub();
- virtual void onFirstRef();
private:
bool openPlatformInput(void);
@@ -121,7 +127,7 @@ private:
mutable Mutex mLock;
bool mHaveFirstKeyboard;
- int32_t mFirstKeyboardId; // the API is that the build in keyboard is id 0, so map it
+ int32_t mFirstKeyboardId; // the API is that the built-in keyboard is id 0, so map it
struct device_ent {
device_t* device;
@@ -136,7 +142,10 @@ private:
device_t **mDevices;
struct pollfd *mFDs;
int mFDCount;
-
+
+ bool mOpened;
+ List<String8> mExcludedDevices;
+
// device ids that report particular switches.
#ifdef EV_SW
int32_t mSwitches[SW_MAX+1];
diff --git a/include/ui/Overlay.h b/include/ui/Overlay.h
index 9ba2f7b6fd1c..acc9bea4e4b5 100644
--- a/include/ui/Overlay.h
+++ b/include/ui/Overlay.h
@@ -82,6 +82,10 @@ public:
/* release the overlay buffer and post it */
status_t queueBuffer(overlay_buffer_t buffer);
+ status_t setCrop(uint32_t x, uint32_t y, uint32_t w, uint32_t h) ;
+
+ status_t getCrop(uint32_t* x, uint32_t* y, uint32_t* w, uint32_t* h) ;
+
/* returns the address of a given buffer if supported, NULL otherwise. */
void* getBufferAddress(overlay_buffer_t buffer);
diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h
index 93bca4aefc08..381933556d2f 100644
--- a/include/utils/ResourceTypes.h
+++ b/include/utils/ResourceTypes.h
@@ -825,7 +825,8 @@ struct ResTable_config
};
enum {
- DENSITY_ANY = 0
+ DENSITY_DEFAULT = 0,
+ DENSITY_NONE = 0xffff
};
union {
diff --git a/include/utils/threads.h b/include/utils/threads.h
index 5c0396596074..e9b0788895e2 100644
--- a/include/utils/threads.h
+++ b/include/utils/threads.h
@@ -190,8 +190,14 @@ inline thread_id_t getThreadId() {
*/
class Mutex {
public:
+ enum {
+ NORMAL = 0,
+ SHARED = 1
+ };
+
Mutex();
Mutex(const char* name);
+ Mutex(int type, const char* name = NULL);
~Mutex();
// lock or unlock the mutex
@@ -235,6 +241,17 @@ inline Mutex::Mutex() {
inline Mutex::Mutex(const char* name) {
pthread_mutex_init(&mMutex, NULL);
}
+inline Mutex::Mutex(int type, const char* name) {
+ if (type == SHARED) {
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
+ pthread_mutex_init(&mMutex, &attr);
+ pthread_mutexattr_destroy(&attr);
+ } else {
+ pthread_mutex_init(&mMutex, NULL);
+ }
+}
inline Mutex::~Mutex() {
pthread_mutex_destroy(&mMutex);
}
diff --git a/keystore/java/android/security/CertTool.java b/keystore/java/android/security/CertTool.java
index 26d22aec57fb..c96cd4f4d907 100644
--- a/keystore/java/android/security/CertTool.java
+++ b/keystore/java/android/security/CertTool.java
@@ -16,11 +16,19 @@
package android.security;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+
import android.content.Context;
import android.content.Intent;
import android.security.Keystore;
import android.text.TextUtils;
-
+import android.util.Log;
/**
* The CertTool class provides the functions to list the certs/keys,
@@ -41,12 +49,12 @@ public class CertTool {
public static final String KEY_NAMESPACE = "namespace";
public static final String KEY_DESCRIPTION = "description";
- private static final String TAG = "CertTool";
+ public static final String TITLE_CA_CERT = "CA Certificate";
+ public static final String TITLE_USER_CERT = "User Certificate";
+ public static final String TITLE_PKCS12_KEYSTORE = "PKCS12 Keystore";
+ public static final String TITLE_PRIVATE_KEY = "Private Key";
- private static final String TITLE_CA_CERT = "CA Certificate";
- private static final String TITLE_USER_CERT = "User Certificate";
- private static final String TITLE_PKCS12_KEYSTORE = "PKCS12 Keystore";
- private static final String TITLE_PRIVATE_KEY = "Private Key";
+ private static final String TAG = "CertTool";
private static final String UNKNOWN = "Unknown";
private static final String ISSUER_NAME = "Issuer Name:";
private static final String DISTINCT_NAME = "Distinct Name:";
@@ -58,6 +66,11 @@ public class CertTool {
private static final String KEYNAME_DELIMITER = "_";
private static final Keystore sKeystore = Keystore.getInstance();
+ private native int getPkcs12Handle(byte[] data, String password);
+ private native String getPkcs12Certificate(int handle);
+ private native String getPkcs12PrivateKey(int handle);
+ private native String popPkcs12CertificateStack(int handle);
+ private native void freePkcs12Handle(int handle);
private native String generateCertificateRequest(int bits, String subject);
private native boolean isPkcs12Keystore(byte[] data);
private native int generateX509Certificate(byte[] data);
@@ -130,10 +143,35 @@ public class CertTool {
intent.putExtra(KEY_NAMESPACE + "1", namespace);
}
+ public int addPkcs12Keystore(byte[] p12Data, String password,
+ String keyname) {
+ int handle, i = 0;
+ String pemData;
+ Log.i("CertTool", "addPkcs12Keystore()");
+
+ if ((handle = getPkcs12Handle(p12Data, password)) == 0) return -1;
+ if ((pemData = getPkcs12Certificate(handle)) != null) {
+ sKeystore.put(USER_CERTIFICATE, keyname, pemData);
+ }
+ if ((pemData = getPkcs12PrivateKey(handle)) != null) {
+ sKeystore.put(USER_KEY, keyname, pemData);
+ }
+ while ((pemData = this.popPkcs12CertificateStack(handle)) != null) {
+ if (i++ > 0) {
+ sKeystore.put(CA_CERTIFICATE, keyname + i, pemData);
+ } else {
+ sKeystore.put(CA_CERTIFICATE, keyname, pemData);
+ }
+ }
+ freePkcs12Handle(handle);
+ return 0;
+ }
+
public synchronized void addCertificate(byte[] data, Context context) {
int handle;
Intent intent = null;
+ Log.i("CertTool", "addCertificate()");
if (isPkcs12Keystore(data)) {
intent = prepareIntent(TITLE_PKCS12_KEYSTORE, data, USER_KEY,
UNKNOWN, UNKNOWN);
diff --git a/keystore/jni/cert.c b/keystore/jni/cert.c
index cc36b84e99a3..0db28fd0e822 100644
--- a/keystore/jni/cert.c
+++ b/keystore/jni/cert.c
@@ -136,30 +136,126 @@ err:
return ret_code;
}
-int is_pkcs12(const char *buf, int bufLen)
+PKCS12 *get_p12_handle(const char *buf, int bufLen)
{
- int ret = 0;
BIO *bp = NULL;
PKCS12 *p12 = NULL;
- if (!buf || bufLen < 1) goto err;
+ if (!buf || (bufLen < 1) || (buf[0] != 48)) goto err;
bp = BIO_new(BIO_s_mem());
if (!bp) goto err;
- if (buf[0] != 48) goto err; // it is not DER.
-
if (!BIO_write(bp, buf, bufLen)) goto err;
- if ((p12 = d2i_PKCS12_bio(bp, NULL)) != NULL) {
- PKCS12_free(p12);
- ret = 1;
- }
+ p12 = d2i_PKCS12_bio(bp, NULL);
+
err:
if (bp) BIO_free(bp);
+ return p12;
+}
+
+PKCS12_KEYSTORE *get_pkcs12_keystore_handle(const char *buf, int bufLen,
+ const char *passwd)
+{
+ PKCS12_KEYSTORE *p12store = NULL;
+ EVP_PKEY *pkey = NULL;
+ X509 *cert = NULL;
+ STACK_OF(X509) *certs = NULL;
+ PKCS12 *p12 = get_p12_handle(buf, bufLen);
+
+ if (p12 == NULL) return NULL;
+ if (!PKCS12_parse(p12, passwd, &pkey, &cert, &certs)) {
+ LOGE("Can not parse PKCS12 content");
+ PKCS12_free(p12);
+ return NULL;
+ }
+ if ((p12store = malloc(sizeof(PKCS12_KEYSTORE))) == NULL) {
+ if (cert) X509_free(cert);
+ if (pkey) EVP_PKEY_free(pkey);
+ if (certs) sk_X509_free(certs);
+ }
+ p12store->p12 = p12;
+ p12store->pkey = pkey;
+ p12store->cert = cert;
+ p12store->certs = certs;
+ return p12store;
+}
+
+void free_pkcs12_keystore(PKCS12_KEYSTORE *p12store)
+{
+ if (p12store != NULL) {
+ if (p12store->cert) X509_free(p12store->cert);
+ if (p12store->pkey) EVP_PKEY_free(p12store->pkey);
+ if (p12store->certs) sk_X509_free(p12store->certs);
+ free(p12store);
+ }
+}
+
+int is_pkcs12(const char *buf, int bufLen)
+{
+ int ret = 0;
+ PKCS12 *p12 = get_p12_handle(buf, bufLen);
+ if (p12 != NULL) ret = 1;
+ PKCS12_free(p12);
return ret;
}
+static int convert_to_pem(void *data, int is_cert, char *buf, int size)
+{
+ int len = 0;
+ BIO *bio = NULL;
+
+ if (data == NULL) return -1;
+
+ if ((bio = BIO_new(BIO_s_mem())) == NULL) goto err;
+ if (is_cert) {
+ if ((len = PEM_write_bio_X509(bio, (X509*)data)) == 0) {
+ goto err;
+ }
+ } else {
+ if ((len = PEM_write_bio_PrivateKey(bio, (EVP_PKEY *)data, NULL,
+ NULL, 0, NULL, NULL)) == 0) {
+ goto err;
+ }
+ }
+ if (len < size && (len = BIO_read(bio, buf, size - 1)) > 0) {
+ buf[len] = 0;
+ }
+err:
+ if (bio) BIO_free(bio);
+ return (len == 0) ? -1 : 0;
+}
+
+int get_pkcs12_certificate(PKCS12_KEYSTORE *p12store, char *buf, int size)
+{
+ if ((p12store != NULL) && (p12store->cert != NULL)) {
+ return convert_to_pem((void*)p12store->cert, 1, buf, size);
+ }
+ return -1;
+}
+
+int get_pkcs12_private_key(PKCS12_KEYSTORE *p12store, char *buf, int size)
+{
+ if ((p12store != NULL) && (p12store->pkey != NULL)) {
+ return convert_to_pem((void*)p12store->pkey, 0, buf, size);
+ }
+ return -1;
+}
+
+int pop_pkcs12_certs_stack(PKCS12_KEYSTORE *p12store, char *buf, int size)
+{
+ X509 *cert = NULL;
+
+ if ((p12store != NULL) && (p12store->certs != NULL) &&
+ ((cert = sk_X509_pop(p12store->certs)) != NULL)) {
+ int ret = convert_to_pem((void*)cert, 1, buf, size);
+ X509_free(cert);
+ return ret;
+ }
+ return -1;
+}
+
X509* parse_cert(const char *buf, int bufLen)
{
X509 *cert = NULL;
diff --git a/keystore/jni/cert.h b/keystore/jni/cert.h
index a9807b1b20f0..aaa7602d8126 100644
--- a/keystore/jni/cert.h
+++ b/keystore/jni/cert.h
@@ -41,6 +41,13 @@ typedef struct {
int key_len;
} PKEY_STORE;
+typedef struct {
+ PKCS12 *p12;
+ EVP_PKEY *pkey;
+ X509 *cert;
+ STACK_OF(X509) *certs;
+} PKCS12_KEYSTORE;
+
#define PKEY_STORE_free(x) { \
if(x.pkey) EVP_PKEY_free(x.pkey); \
if(x.public_key) free(x.public_key); \
@@ -49,8 +56,14 @@ typedef struct {
#define nelem(x) (sizeof (x) / sizeof *(x))
int gen_csr(int bits, const char *organizations, char reply[REPLY_MAX]);
+PKCS12_KEYSTORE *get_pkcs12_keystore_handle(const char *buf, int bufLen,
+ const char *passwd);
+int get_pkcs12_certificate(PKCS12_KEYSTORE *p12store, char *buf, int size);
+int get_pkcs12_private_key(PKCS12_KEYSTORE *p12store, char *buf, int size);
+int pop_pkcs12_certs_stack(PKCS12_KEYSTORE *p12store, char *buf, int size);
+void free_pkcs12_keystore(PKCS12_KEYSTORE *p12store);
int is_pkcs12(const char *buf, int bufLen);
-X509* parse_cert(const char *buf, int bufLen);
+X509 *parse_cert(const char *buf, int bufLen);
int get_cert_name(X509 *cert, char *buf, int size);
int get_issuer_name(X509 *cert, char *buf, int size);
int is_ca_cert(X509 *cert);
diff --git a/keystore/jni/certtool.c b/keystore/jni/certtool.c
index fabf5cdf3fb7..1ae8dab4281f 100644
--- a/keystore/jni/certtool.c
+++ b/keystore/jni/certtool.c
@@ -19,10 +19,13 @@
#include <string.h>
#include <jni.h>
#include <cutils/log.h>
+#include <openssl/pkcs12.h>
#include <openssl/x509v3.h>
#include "cert.h"
+typedef int PKCS12_KEYSTORE_FUNC(PKCS12_KEYSTORE *store, char *buf, int size);
+
jstring
android_security_CertTool_generateCertificateRequest(JNIEnv* env,
jobject thiz,
@@ -42,12 +45,88 @@ android_security_CertTool_isPkcs12Keystore(JNIEnv* env,
jobject thiz,
jbyteArray data)
{
- char buf[REPLY_MAX];
int len = (*env)->GetArrayLength(env, data);
- if (len > REPLY_MAX) return 0;
- (*env)->GetByteArrayRegion(env, data, 0, len, (jbyte*)buf);
- return (jboolean) is_pkcs12(buf, len);
+ if (len > 0) {
+ PKCS12 *handle = NULL;
+ char buf[len];
+
+ (*env)->GetByteArrayRegion(env, data, 0, len, (jbyte*)buf);
+ return (jboolean)is_pkcs12(buf, len);
+ } else {
+ return 0;
+ }
+}
+
+jint
+android_security_CertTool_getPkcs12Handle(JNIEnv* env,
+ jobject thiz,
+ jbyteArray data,
+ jstring jPassword)
+{
+ jboolean bIsCopy;
+ int len = (*env)->GetArrayLength(env, data);
+ const char* passwd = (*env)->GetStringUTFChars(env, jPassword , &bIsCopy);
+
+ if (len > 0) {
+ PKCS12_KEYSTORE *handle = NULL;
+ char buf[len];
+
+ (*env)->GetByteArrayRegion(env, data, 0, len, (jbyte*)buf);
+ handle = get_pkcs12_keystore_handle(buf, len, passwd);
+ (*env)->ReleaseStringUTFChars(env, jPassword, passwd);
+ return (jint)handle;
+ } else {
+ return 0;
+ }
+}
+
+jstring call_pkcs12_ks_func(PKCS12_KEYSTORE_FUNC *func,
+ JNIEnv* env,
+ jobject thiz,
+ jint phandle)
+{
+ char buf[REPLY_MAX];
+
+ if (phandle == 0) return NULL;
+ if (func((PKCS12_KEYSTORE*)phandle, buf, sizeof(buf)) == 0) {
+ return (*env)->NewStringUTF(env, buf);
+ }
+ return NULL;
+}
+
+jstring
+android_security_CertTool_getPkcs12Certificate(JNIEnv* env,
+ jobject thiz,
+ jint phandle)
+{
+ return call_pkcs12_ks_func((PKCS12_KEYSTORE_FUNC *)get_pkcs12_certificate,
+ env, thiz, phandle);
+}
+
+jstring
+android_security_CertTool_getPkcs12PrivateKey(JNIEnv* env,
+ jobject thiz,
+ jint phandle)
+{
+ return call_pkcs12_ks_func((PKCS12_KEYSTORE_FUNC *)get_pkcs12_private_key,
+ env, thiz, phandle);
+}
+
+jstring
+android_security_CertTool_popPkcs12CertificateStack(JNIEnv* env,
+ jobject thiz,
+ jint phandle)
+{
+ return call_pkcs12_ks_func((PKCS12_KEYSTORE_FUNC *)pop_pkcs12_certs_stack,
+ env, thiz, phandle);
+}
+
+void android_security_CertTool_freePkcs12Handle(JNIEnv* env,
+ jobject thiz,
+ jint handle)
+{
+ if (handle != 0) free_pkcs12_keystore((PKCS12_KEYSTORE*)handle);
}
jint
@@ -117,6 +196,16 @@ static JNINativeMethod gCertToolMethods[] = {
(void*)android_security_CertTool_generateCertificateRequest},
{"isPkcs12Keystore", "([B)Z",
(void*)android_security_CertTool_isPkcs12Keystore},
+ {"getPkcs12Handle", "([BLjava/lang/String;)I",
+ (void*)android_security_CertTool_getPkcs12Handle},
+ {"getPkcs12Certificate", "(I)Ljava/lang/String;",
+ (void*)android_security_CertTool_getPkcs12Certificate},
+ {"getPkcs12PrivateKey", "(I)Ljava/lang/String;",
+ (void*)android_security_CertTool_getPkcs12PrivateKey},
+ {"popPkcs12CertificateStack", "(I)Ljava/lang/String;",
+ (void*)android_security_CertTool_popPkcs12CertificateStack},
+ {"freePkcs12Handle", "(I)V",
+ (void*)android_security_CertTool_freePkcs12Handle},
{"generateX509Certificate", "([B)I",
(void*)android_security_CertTool_generateX509Certificate},
{"isCaCertificate", "(I)Z",
diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp
index 598a35682cfc..ffc02782c7d5 100644
--- a/libs/audioflinger/AudioFlinger.cpp
+++ b/libs/audioflinger/AudioFlinger.cpp
@@ -212,6 +212,7 @@ void AudioFlinger::setA2dpEnabled_l(bool enable)
} else {
mA2dpMixerThread->getTracks_l(tracks, activeTracks);
mHardwareMixerThread->putTracks_l(tracks, activeTracks);
+ mA2dpMixerThread->mOutput->standby();
}
mA2dpEnabled = enable;
mNotifyA2dpChange = true;
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index c3889e9855cf..c371a23b85f4 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -366,13 +366,8 @@ void IPCThreadState::restoreCallingIdentity(int64_t token)
void IPCThreadState::clearCaller()
{
- if (mProcess->supportsProcesses()) {
- mCallingPid = getpid();
- mCallingUid = getuid();
- } else {
- mCallingPid = -1;
- mCallingUid = -1;
- }
+ mCallingPid = getpid();
+ mCallingUid = getuid();
}
void IPCThreadState::flushCommands()
diff --git a/libs/rs/Android.mk b/libs/rs/Android.mk
index f5297f8d07ba..0091e322d08f 100644
--- a/libs/rs/Android.mk
+++ b/libs/rs/Android.mk
@@ -85,10 +85,12 @@ LOCAL_SRC_FILES:= \
rsContext.cpp \
rsDevice.cpp \
rsElement.cpp \
+ rsFileA3D.cpp \
rsLight.cpp \
rsLocklessFifo.cpp \
rsObjectBase.cpp \
rsMatrix.cpp \
+ rsMesh.cpp \
rsProgram.cpp \
rsProgramFragment.cpp \
rsProgramFragmentStore.cpp \
@@ -96,6 +98,7 @@ LOCAL_SRC_FILES:= \
rsSampler.cpp \
rsScript.cpp \
rsScriptC.cpp \
+ rsScriptC_Lib.cpp \
rsThreadIO.cpp \
rsType.cpp \
rsTriangleMesh.cpp
diff --git a/libs/rs/RenderScriptEnv.h b/libs/rs/RenderScriptEnv.h
index 53de1f1f1ec4..07893014ac70 100644
--- a/libs/rs/RenderScriptEnv.h
+++ b/libs/rs/RenderScriptEnv.h
@@ -30,65 +30,7 @@ typedef struct {
#define RS_PROGRAM_VERTEX_PROJECTION_OFFSET 16
#define RS_PROGRAM_VERTEX_TEXTURE_OFFSET 32
-typedef struct {
- const void * (*loadEnvVp)(uint32_t bank, uint32_t offset);
-
- float (*loadEnvF)(uint32_t bank, uint32_t offset);
- int32_t (*loadEnvI32)(uint32_t bank, uint32_t offset);
- uint32_t (*loadEnvU32)(uint32_t bank, uint32_t offset);
- void (*loadEnvVec4)(uint32_t bank, uint32_t offset, rsc_Vector4 *);
- void (*loadEnvMatrix)(uint32_t bank, uint32_t offset, rsc_Matrix *);
-
- void (*storeEnvF)(uint32_t bank, uint32_t offset, float);
- void (*storeEnvI32)(uint32_t bank, uint32_t offset, int32_t);
- void (*storeEnvU32)(uint32_t bank, uint32_t offset, uint32_t);
- void (*storeEnvVec4)(uint32_t bank, uint32_t offset, const rsc_Vector4 *);
- void (*storeEnvMatrix)(uint32_t bank, uint32_t offset, const rsc_Matrix *);
-
- void (*matrixLoadIdentity)(rsc_Matrix *);
- void (*matrixLoadFloat)(rsc_Matrix *, const float *);
- void (*matrixLoadMat)(rsc_Matrix *, const rsc_Matrix *);
- void (*matrixLoadRotate)(rsc_Matrix *, float rot, float x, float y, float z);
- void (*matrixLoadScale)(rsc_Matrix *, float x, float y, float z);
- void (*matrixLoadTranslate)(rsc_Matrix *, float x, float y, float z);
- void (*matrixLoadMultiply)(rsc_Matrix *, const rsc_Matrix *lhs, const rsc_Matrix *rhs);
- void (*matrixMultiply)(rsc_Matrix *, const rsc_Matrix *rhs);
- void (*matrixRotate)(rsc_Matrix *, float rot, float x, float y, float z);
- void (*matrixScale)(rsc_Matrix *, float x, float y, float z);
- void (*matrixTranslate)(rsc_Matrix *, float x, float y, float z);
-
- void (*color)(float r, float g, float b, float a);
-
- void (*programFragmentBindTexture)(RsProgramFragment, uint32_t slot, RsAllocation);
- void (*programFragmentBindSampler)(RsProgramFragment, uint32_t slot, RsAllocation);
-
- void (*materialDiffuse)(float r, float g, float b, float a);
- void (*materialSpecular)(float r, float g, float b, float a);
- void (*lightPosition)(float x, float y, float z, float w);
- void (*materialShininess)(float s);
-
- void (*uploadToTexture)(RsAllocation va, uint32_t baseMipLevel);
-
- void (*enable)(uint32_t);
- void (*disable)(uint32_t);
-
- uint32_t (*rand)(uint32_t max);
-
- void (*contextBindProgramFragment)(RsProgramFragment pf);
- void (*contextBindProgramFragmentStore)(RsProgramFragmentStore pfs);
-
-
- // Drawing funcs
- void (*renderTriangleMesh)(RsTriangleMesh);
- void (*renderTriangleMeshRange)(RsTriangleMesh, uint32_t start, uint32_t count);
-
- // Assumes (GL_FIXED) x,y,z (GL_UNSIGNED_BYTE)r,g,b,a
- void (*drawTriangleArray)(RsAllocation alloc, uint32_t count);
-
- void (*drawRect)(int32_t x1, int32_t x2, int32_t y1, int32_t y2);
-} rsc_FunctionTable;
-
-typedef int (*rsc_RunScript)(uint32_t launchIndex, const rsc_FunctionTable *);
+//typedef int (*rsc_RunScript)(uint32_t launchIndex, const rsc_FunctionTable *);
/* EnableCap */
diff --git a/libs/rs/java/Film/res/raw/filmstrip.c b/libs/rs/java/Film/res/raw/filmstrip.c
index 1687a3121068..6885251d9250 100644
--- a/libs/rs/java/Film/res/raw/filmstrip.c
+++ b/libs/rs/java/Film/res/raw/filmstrip.c
@@ -1,7 +1,7 @@
// Fountain test script
#pragma version(1)
-#pragma stateVertex(PV)
+#pragma stateVertex(PVBackground)
#pragma stateFragment(PFBackground)
#pragma stateFragmentStore(PFSBackground)
@@ -23,47 +23,35 @@ int main(int index)
{
int f1,f2,f3,f4, f5,f6,f7,f8, f9,f10,f11,f12, f13,f14,f15,f16;
int g1,g2,g3,g4, g5,g6,g7,g8, g9,g10,g11,g12, g13,g14,g15,g16;
- int float_1;
- int float_0;
- int float_2;
- int float_90;
- int float_0_5;
- int trans; // float
- int rot; // float
+ float trans;
+ float rot;
int x;
float focusPos; // float
int focusID;
int lastFocusID;
int imgCount;
- float_2 = intToFloat(2);
- float_1 = intToFloat(1);
- float_0 = intToFloat(0);
- float_90= intToFloat(90);
- float_0_5 = fixedtoFloat(0x8000);
-
trans = loadF(1, 0);
rot = loadF(1, 1);
- matrixLoadScale(&f16, float_2, float_2, float_2);
- matrixTranslate(&f16, 0, 0, trans);
- matrixRotate(&f16, float_90, 0, 0, float_1);
- matrixRotate(&f16, rot, float_1, 0, 0);
- storeEnvMatrix(3, 0, &f16);
+ matrixLoadScale(&f16, 2.f, 2.f, 2.f);
+ matrixTranslate(&f16, 0.f, 0.f, trans);
+ matrixRotate(&f16, 90.f, 0.f, 0.f, 1.f);
+ matrixRotate(&f16, rot, 1.f, 0.f, 0.f);
+ storeMatrix(3, 0, &f16);
//materialDiffuse(con, 0.0f, 0.0f, 0.0f, 1.0f);
//materialSpecular(con, 0.5f, 0.5f, 0.5f, 0.5f);
//materialShininess(intToFloat(20));
- //lightPosition(con, 0.2f, -0.2f, -2.0f, 0.0f);
- //enable(con, GL_LIGHTING);
- renderTriangleMesh(NAMED_mesh);
+ drawTriangleMesh(NAMED_mesh);
//int imgId = 0;
- contextBindProgramFragmentStore(NAMED_PFImages);
- contextBindProgramFragment(NAMED_PFSImages);
+ bindProgramFragmentStore(NAMED_PFImages);
+ bindProgramFragment(NAMED_PFSImages);
+ bindProgramVertex(NAMED_PVImages);
//focusPos = loadF(1, 2);
//focusID = 0;
diff --git a/libs/rs/java/Film/src/com/android/film/FilmRS.java b/libs/rs/java/Film/src/com/android/film/FilmRS.java
index 2711bf0cc4aa..fca0818983d1 100644
--- a/libs/rs/java/Film/src/com/android/film/FilmRS.java
+++ b/libs/rs/java/Film/src/com/android/film/FilmRS.java
@@ -45,7 +45,6 @@ public class FilmRS {
public void init(RenderScript rs, Resources res, int width, int height) {
mRS = rs;
mRes = res;
- initNamed();
initRS();
}
@@ -77,7 +76,8 @@ public class FilmRS {
private RenderScript.ProgramFragmentStore mPFSImages;
private RenderScript.ProgramFragment mPFBackground;
private RenderScript.ProgramFragment mPFImages;
- private RenderScript.ProgramVertex mPV;
+ private RenderScript.ProgramVertex mPVBackground;
+ private RenderScript.ProgramVertex mPVImages;
private ProgramVertexAlloc mPVA;
private RenderScript.Allocation mAllocEnv;
@@ -90,20 +90,7 @@ public class FilmRS {
private float[] mBufferPos;
private float[] mBufferPV;
- private void initNamed() {
- mElementVertex = mRS.elementGetPredefined(
- RenderScript.ElementPredefined.NORM_ST_XYZ_F32);
- mElementIndex = mRS.elementGetPredefined(
- RenderScript.ElementPredefined.INDEX_16);
-
- mRS.triangleMeshBegin(mElementVertex, mElementIndex);
- FilmStripMesh fsm = new FilmStripMesh();
- fsm.init(mRS);
- mMesh = mRS.triangleMeshCreate();
- mMesh.setName("mesh");
- Log.e("rs", "Done loading strips");
-
-
+ private void initSamplers() {
mRS.samplerBegin();
mRS.samplerSet(RenderScript.SamplerParam.FILTER_MIN,
RenderScript.SamplerValue.LINEAR_MIP_LINEAR);
@@ -112,19 +99,9 @@ public class FilmRS {
mRS.samplerSet(RenderScript.SamplerParam.WRAP_MODE_T,
RenderScript.SamplerValue.CLAMP);
mSampler = mRS.samplerCreate();
+ }
- mRS.programFragmentBegin(null, null);
- mPFBackground = mRS.programFragmentCreate();
- mPFBackground.setName("PFBackground");
-
- mRS.programFragmentBegin(null, null);
- mRS.programFragmentSetTexEnable(0, true);
- //mRS.programFragmentSetEnvMode(0, RS_TEX_ENV_MODE_REPLACE);
- //rsProgramFragmentSetType(0, gEnv.tex[0]->getType());
- mPFImages = mRS.programFragmentCreate();
- mPFImages.setName("PFImages");
- mPFImages.bindSampler(mSampler, 0);
-
+ private void initPFS() {
mRS.programFragmentStoreBegin(null, null);
mRS.programFragmentStoreDepthFunc(RenderScript.DepthFunc.LESS);
mRS.programFragmentStoreDitherEnable(true);
@@ -139,26 +116,59 @@ public class FilmRS {
RenderScript.BlendDstFunc.ONE);
mPFSImages = mRS.programFragmentStoreCreate();
mPFSImages.setName("PFSImages");
+ }
- mRS.programVertexBegin(null, null);
- mRS.programVertexSetTextureMatrixEnable(true);
- mPV = mRS.programVertexCreate();
- mPV.setName("PV");
+ private void initPF() {
+ mRS.programFragmentBegin(null, null);
+ mPFBackground = mRS.programFragmentCreate();
+ mPFBackground.setName("PFBackground");
+
+ mRS.programFragmentBegin(null, null);
+ mRS.programFragmentSetTexEnable(0, true);
+ //mRS.programFragmentSetEnvMode(0, RS_TEX_ENV_MODE_REPLACE);
+ //rsProgramFragmentSetType(0, gEnv.tex[0]->getType());
+ mPFImages = mRS.programFragmentCreate();
+ mPFImages.setName("PFImages");
+ }
+ private void initPV() {
mRS.lightBegin();
mLight = mRS.lightCreate();
mLight.setPosition(0, -0.5f, -1.0f);
- Log.e("rs", "Done loading named");
- }
+ mRS.programVertexBegin(null, null);
+ mRS.programVertexSetTextureMatrixEnable(true);
+ mRS.programVertexAddLight(mLight);
+ mPVBackground = mRS.programVertexCreate();
+ mPVBackground.setName("PVBackground");
+ mRS.programVertexBegin(null, null);
+ mPVImages = mRS.programVertexCreate();
+ mPVImages.setName("PVImages");
+ }
- private Bitmap mBackground;
int mParams[] = new int[10];
private void initRS() {
- int partCount = 1024;
+ mElementVertex = mRS.elementGetPredefined(
+ RenderScript.ElementPredefined.NORM_ST_XYZ_F32);
+ mElementIndex = mRS.elementGetPredefined(
+ RenderScript.ElementPredefined.INDEX_16);
+
+ mRS.triangleMeshBegin(mElementVertex, mElementIndex);
+ FilmStripMesh fsm = new FilmStripMesh();
+ fsm.init(mRS);
+ mMesh = mRS.triangleMeshCreate();
+ mMesh.setName("mesh");
+
+ initPFS();
+ initSamplers();
+ initPF();
+ initPV();
+ mPFImages.bindSampler(mSampler, 0);
+
+ Log.e("rs", "Done loading named");
mRS.scriptCBegin();
mRS.scriptCSetClearColor(0.0f, 0.0f, 0.0f, 1.0f);
@@ -172,7 +182,8 @@ public class FilmRS {
mBufferPos.length);
mPVA = new ProgramVertexAlloc(mRS);
- mPV.bindAllocation(0, mPVA.mAlloc);
+ mPVBackground.bindAllocation(0, mPVA.mAlloc);
+ mPVImages.bindAllocation(0, mPVA.mAlloc);
mPVA.setupProjectionNormalized(320, 480);
@@ -181,10 +192,6 @@ public class FilmRS {
mScriptStrip.bindAllocation(mPVA.mAlloc, 3);
- //mIntAlloc = mRS.allocationCreatePredefSized(RenderScript.ElementPredefined.USER_I32, 10);
- //mPartAlloc = mRS.allocationCreatePredefSized(RenderScript.ElementPredefined.USER_I32, partCount * 3 * 3);
- //mPartAlloc.setName("PartBuffer");
- //mVertAlloc = mRS.allocationCreatePredefSized(RenderScript.ElementPredefined.USER_I32, partCount * 5 + 1);
/*
{
Resources res = getResources();
@@ -203,25 +210,6 @@ public class FilmRS {
mPFS = mRS.programFragmentStoreCreate();
mPFS.setName("MyBlend");
mRS.contextBindProgramFragmentStore(mPFS);
-
- mRS.samplerBegin();
- mRS.samplerSet(RenderScript.SamplerParam.FILTER_MAG, RenderScript.SamplerValue.LINEAR);
- mRS.samplerSet(RenderScript.SamplerParam.FILTER_MIN, RenderScript.SamplerValue.LINEAR);
- mSampler = mRS.samplerCreate();
-
-
- mParams[0] = 0;
- mParams[1] = partCount;
- mParams[2] = 0;
- mParams[3] = 0;
- mParams[4] = 0;
- mIntAlloc.data(mParams);
-
- int t2[] = new int[partCount * 4*3];
- for (int ct=0; ct < t2.length; ct++) {
- t2[ct] = 0;
- }
- mPartAlloc.data(t2);
*/
setFilmStripPosition(0, 0);
diff --git a/libs/rs/java/Fountain/AndroidManifest.xml b/libs/rs/java/Fountain/AndroidManifest.xml
index a10938bae863..dd0e42876f53 100644
--- a/libs/rs/java/Fountain/AndroidManifest.xml
+++ b/libs/rs/java/Fountain/AndroidManifest.xml
@@ -3,7 +3,7 @@
package="com.android.fountain">
<application android:label="Fountain">
<activity android:name="Fountain"
- android:theme="@android:style/Theme.Black.NoTitleBar">
+ android:theme="@android:style/Theme.Translucent">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/libs/rs/java/Fountain/res/raw/fountain.c b/libs/rs/java/Fountain/res/raw/fountain.c
index 69195654732a..99551fcdf915 100644
--- a/libs/rs/java/Fountain/res/raw/fountain.c
+++ b/libs/rs/java/Fountain/res/raw/fountain.c
@@ -2,8 +2,8 @@
#pragma version(1)
#pragma stateVertex(default)
-#pragma stateFragment(PgmFragBackground)
-#pragma stateFragmentStore(PFSReplace)
+#pragma stateFragment(PgmFragParts)
+#pragma stateFragmentStore(PFSBlend)
int main(int launchID) {
@@ -34,15 +34,11 @@ int main(int launchID) {
}
}
- drawRect(0, 256, 0, 512);
- contextBindProgramFragment(NAMED_PgmFragParts);
- contextBindProgramFragmentStore(NAMED_PFSBlend);
-
if (touch) {
newPart = loadI32(2, 0);
for (ct2=0; ct2<rate; ct2++) {
- dx = scriptRand(0x10000) - 0x8000;
- dy = scriptRand(0x10000) - 0x8000;
+ dx = (int)((randf(1.f) - 0.5f) * 0x10000);
+ dy = (int)((randf(1.f) - 0.5f) * 0x10000);
idx = newPart * 5 + 1;
storeI32(2, idx, dx);
@@ -74,15 +70,15 @@ int main(int launchID) {
dstIdx = drawCount * 9;
c = 0xffafcf | ((life >> lifeShift) << 24);
- storeU32(1, dstIdx, c);
+ storeI32(1, dstIdx, c);
storeI32(1, dstIdx + 1, posx);
storeI32(1, dstIdx + 2, posy);
- storeU32(1, dstIdx + 3, c);
+ storeI32(1, dstIdx + 3, c);
storeI32(1, dstIdx + 4, posx + 0x10000);
storeI32(1, dstIdx + 5, posy + dy * 4);
- storeU32(1, dstIdx + 6, c);
+ storeI32(1, dstIdx + 6, c);
storeI32(1, dstIdx + 7, posx - 0x10000);
storeI32(1, dstIdx + 8, posy + dy * 4);
drawCount ++;
diff --git a/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java b/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java
index 745635fb1269..c8e9a1ed9a7f 100644
--- a/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java
+++ b/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java
@@ -56,11 +56,7 @@ public class FountainRS {
private RenderScript.Allocation mVertAlloc;
private RenderScript.Script mScript;
private RenderScript.ProgramFragmentStore mPFS;
- private RenderScript.ProgramFragmentStore mPFS2;
private RenderScript.ProgramFragment mPF;
- private RenderScript.ProgramFragment mPF2;
- private RenderScript.Allocation mTexture;
- private RenderScript.Sampler mSampler;
private Bitmap mBackground;
@@ -74,16 +70,6 @@ public class FountainRS {
mPartAlloc.setName("PartBuffer");
mVertAlloc = mRS.allocationCreatePredefSized(RenderScript.ElementPredefined.USER_I32, partCount * 5 + 1);
- {
- Drawable d = mRes.getDrawable(R.drawable.gadgets_clock_mp3);
- BitmapDrawable bd = (BitmapDrawable)d;
- Bitmap b = bd.getBitmap();
- mTexture = mRS.allocationCreateFromBitmap(b,
- RenderScript.ElementPredefined.RGB_565,
- false);
- mTexture.uploadToTexture(0);
- }
-
mRS.programFragmentStoreBegin(null, null);
mRS.programFragmentStoreBlendFunc(RenderScript.BlendSrcFunc.SRC_ALPHA, RenderScript.BlendDstFunc.ONE);
mRS.programFragmentStoreDepthFunc(RenderScript.DepthFunc.ALWAYS);
@@ -92,33 +78,10 @@ public class FountainRS {
mPFS = mRS.programFragmentStoreCreate();
mPFS.setName("PFSBlend");
- mRS.programFragmentStoreBegin(null, null);
- mRS.programFragmentStoreDepthFunc(RenderScript.DepthFunc.ALWAYS);
- mRS.programFragmentStoreDepthMask(false);
- mRS.programFragmentStoreDitherEnable(false);
- mPFS2 = mRS.programFragmentStoreCreate();
- mPFS2.setName("PFSReplace");
- mRS.contextBindProgramFragmentStore(mPFS2);
-
- mRS.samplerBegin();
- mRS.samplerSet(RenderScript.SamplerParam.FILTER_MAG, RenderScript.SamplerValue.NEAREST);
- mRS.samplerSet(RenderScript.SamplerParam.FILTER_MIN, RenderScript.SamplerValue.NEAREST);
- mSampler = mRS.samplerCreate();
-
-
mRS.programFragmentBegin(null, null);
mPF = mRS.programFragmentCreate();
mPF.setName("PgmFragParts");
- mRS.programFragmentBegin(null, null);
- mRS.programFragmentSetTexEnable(0, true);
- mPF2 = mRS.programFragmentCreate();
- mRS.contextBindProgramFragment(mPF2);
- mPF2.bindTexture(mTexture, 0);
- mPF2.bindSampler(mSampler, 0);
- mPF2.setName("PgmFragBackground");
-
-
mParams[0] = 0;
mParams[1] = partCount;
mParams[2] = 0;
diff --git a/libs/rs/java/RenderScript/android/renderscript/RenderScript.java b/libs/rs/java/RenderScript/android/renderscript/RenderScript.java
index 09d18369f39c..e3556351a952 100644
--- a/libs/rs/java/RenderScript/android/renderscript/RenderScript.java
+++ b/libs/rs/java/RenderScript/android/renderscript/RenderScript.java
@@ -156,6 +156,7 @@ public class RenderScript {
native private void nProgramVertexBegin(int inID, int outID);
native private void nProgramVertexSetType(int slot, int mID);
native private void nProgramVertexSetTextureMatrixEnable(boolean enable);
+ native private void nProgramVertexAddLight(int id);
native private int nProgramVertexCreate();
native private void nLightBegin();
@@ -720,7 +721,6 @@ public class RenderScript {
public void bindAllocation(int slot, Allocation va) {
nProgramVertexBindAllocation(mID, slot, va.mID);
}
-
}
public void programVertexBegin(Element in, Element out) {
@@ -743,6 +743,10 @@ public class RenderScript {
nProgramVertexSetTextureMatrixEnable(enable);
}
+ public void programVertexAddLight(Light l) {
+ nProgramVertexAddLight(l.mID);
+ }
+
public ProgramVertex programVertexCreate() {
int id = nProgramVertexCreate();
return new ProgramVertex(id);
@@ -846,6 +850,10 @@ public class RenderScript {
nProgramFragmentSetTexEnable(slot, enable);
}
+ public void programFragmentSetTexEnvMode(int slot, EnvMode env) {
+ nProgramFragmentSetEnvMode(slot, env.mID);
+ }
+
public ProgramFragment programFragmentCreate() {
int id = nProgramFragmentCreate();
return new ProgramFragment(id);
diff --git a/libs/rs/java/Rollo/AndroidManifest.xml b/libs/rs/java/Rollo/AndroidManifest.xml
index da160a37a343..127a140d2397 100644
--- a/libs/rs/java/Rollo/AndroidManifest.xml
+++ b/libs/rs/java/Rollo/AndroidManifest.xml
@@ -3,7 +3,7 @@
package="com.android.rollo">
<application android:label="Rollo">
<activity android:name="Rollo"
- android:theme="@android:style/Theme.Black.NoTitleBar">
+ android:theme="@android:style/Theme.Translucent">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/libs/rs/java/Rollo/res/drawable/browser.png b/libs/rs/java/Rollo/res/raw/browser.png
index 513f0be49693..513f0be49693 100644
--- a/libs/rs/java/Rollo/res/drawable/browser.png
+++ b/libs/rs/java/Rollo/res/raw/browser.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/drawable/market.png b/libs/rs/java/Rollo/res/raw/market.png
index 83b6910fcdec..83b6910fcdec 100644
--- a/libs/rs/java/Rollo/res/drawable/market.png
+++ b/libs/rs/java/Rollo/res/raw/market.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/drawable/photos.png b/libs/rs/java/Rollo/res/raw/photos.png
index 1ed8f1e811ad..1ed8f1e811ad 100644
--- a/libs/rs/java/Rollo/res/drawable/photos.png
+++ b/libs/rs/java/Rollo/res/raw/photos.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/rollo.c b/libs/rs/java/Rollo/res/raw/rollo.c
index f181e4972f72..d338d0df1b0c 100644
--- a/libs/rs/java/Rollo/res/raw/rollo.c
+++ b/libs/rs/java/Rollo/res/raw/rollo.c
@@ -3,54 +3,98 @@
#pragma stateFragment(PF)
#pragma stateFragmentStore(PFS)
+// Scratch buffer layout
+#define SCRATCH_FADE 0
+#define SCRATCH_ZOOM 1
+#define SCRATCH_ROT 2
+
+//#define STATE_POS_X 0
+#define STATE_DONE 1
+//#define STATE_PRESSURE 2
+#define STATE_ZOOM 3
+//#define STATE_WARP 4
+#define STATE_ORIENTATION 5
+#define STATE_SELECTION 6
+#define STATE_FIRST_VISIBLE 7
+#define STATE_COUNT 8
+#define STATE_TOUCH 9
+
+float filter(float val, float target, float str)
+{
+ float delta = (target - val);
+ return val + delta * str;
+}
+
int main(void* con, int ft, int launchID)
{
int rowCount;
- int x;
- int y;
int row;
int col;
int imageID;
- int tx1;
- int ty1;
- int tz1;
- int tx2;
- int ty2;
- int tz2;
- int rot;
- int rotStep;
- int tmpSin;
- int tmpCos;
- int iconCount;
- int pressure;
-
-
- rotStep = 20 * 0x10000;
- pressure = loadI32(0, 2);
+ int done = loadI32(0, STATE_DONE);
+ int selectedID = loadI32(0, STATE_SELECTION);
- rowCount = 4;
+ float f = loadF(2, 0);
- iconCount = loadI32(0, 1);
- rot = (-20 + loadI32(0, 0)) * 0x10000;
- while (iconCount) {
- tmpSin = sinx(rot);
- tmpCos = cosx(rot);
+ pfClearColor(0.0f, 0.0f, 0.0f, f);
+ if (done) {
+ if (f > 0.02f) {
+ //f = f - 0.02f;
+ //storeF(2, 0, f);
+ }
+ } else {
+ if (f < 0.8f) {
+ f = f + 0.02f;
+ storeF(2, 0, f);
+ }
+ }
- tx1 = tmpSin * 8 - tmpCos;
- tx2 = tx1 + tmpCos * 2;
+ float touchCut = 1.f;
+ if (loadI32(0, STATE_TOUCH)) {
+ touchCut = 5.f;
+ }
+
+
+ float targetZoom = ((float)loadI32(0, STATE_ZOOM)) / 1000.f;
+ float zoom = filter(loadF(2, SCRATCH_ZOOM), targetZoom, 0.15 * touchCut);
+ storeF(2, SCRATCH_ZOOM, zoom);
- tz1 = tmpCos * 8 + tmpSin + pressure;
- tz2 = tz1 - tmpSin * 2;
+ float targetRot = loadI32(0, STATE_FIRST_VISIBLE) / 180.0f * 3.14f;
+ float rot = filter(loadF(2, SCRATCH_ROT), targetRot, 0.1f * touchCut);
+ storeF(2, SCRATCH_ROT, rot);
- for (y = 0; (y < rowCount) && iconCount; y++) {
- ty1 = (y * 0x30000) - 0x48000;
- ty2 = ty1 + 0x20000;
- pfBindTexture(NAMED_PF, 0, loadI32(1, y));
+ float diam = 8.f;// + curve * 2.f;
+ float scale = 1.0f / zoom;
+
+ rot = rot * scale;
+ float rotStep = 20.0f / 180.0f * 3.14f * scale;
+ rowCount = 4;
+ int index = 0;
+ int iconCount = loadI32(0, STATE_COUNT);
+ while (iconCount) {
+ float tmpSin = sinf(rot);
+ float tmpCos = cosf(rot);
+
+ float tx1 = tmpSin * diam - (tmpCos * scale);
+ float tx2 = tx1 + (tmpCos * scale * 2.f);
+ float tz1 = tmpCos * diam + (tmpSin * scale);
+ float tz2 = tz1 - (tmpSin * scale * 2.f);
+
+ int y;
+ for (y = rowCount -1; (y >= 0) && iconCount; y--) {
+ float ty1 = ((y * 3.0f) - 4.5f) * scale;
+ float ty2 = ty1 + scale * 2.f;
+ bindTexture(NAMED_PF, 0, loadI32(1, y));
+ color(1.0f, 1.0f, 1.0f, 1.0f);
+ if (done && (index != selectedID)) {
+ color(0.4f, 0.4f, 0.4f, 1.0f);
+ }
drawQuad(tx1, ty1, tz1,
tx2, ty1, tz2,
tx2, ty2, tz2,
tx1, ty2, tz1);
iconCount--;
+ index++;
}
rot = rot + rotStep;
}
@@ -58,3 +102,4 @@ int main(void* con, int ft, int launchID)
return 0;
}
+
diff --git a/libs/rs/java/Rollo/res/raw/rollo2.c b/libs/rs/java/Rollo/res/raw/rollo2.c
new file mode 100644
index 000000000000..b04ea73e53f6
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/rollo2.c
@@ -0,0 +1,67 @@
+#pragma version(1)
+#pragma stateVertex(PV)
+#pragma stateFragment(PF)
+#pragma stateFragmentStore(PFS)
+
+void drawLoop(int x, int y, int z, int rot)
+{
+ int ct;
+ int tx;
+ int ty;
+ int tmpSin;
+ int tmpCos;
+ int sz;
+
+ for (ct = 0; ct < 10; ct ++) {
+ tmpSin = sinx((ct * 36 + rot) * 0x10000);
+ tmpCos = cosx((ct * 36 + rot) * 0x10000);
+
+ ty = y + tmpCos * 4;
+ tx = x + tmpSin * 4;
+ pfBindTexture(NAMED_PF, 0, loadI32(1, ct & 3));
+
+ sz = 0xc000;
+ drawQuad(tx - sz, ty - sz, z,
+ tx + sz, ty - sz, z,
+ tx + sz, ty + sz, z,
+ tx - sz, ty + sz, z);
+ }
+}
+
+int main(void* con, int ft, int launchID)
+{
+ int rowCount;
+ int x;
+ int y;
+ int row;
+ int col;
+ int imageID;
+ int tx1;
+ int ty1;
+ int tz1;
+ int tx2;
+ int ty2;
+ int tz2;
+ int tmpSin;
+ int tmpCos;
+ int iconCount;
+ int pressure;
+
+ int ringCount;
+
+
+
+ rotStep = 16 * 0x10000;
+ pressure = loadI32(0, 2);
+ rowCount = 4;
+
+ iconCount = loadI32(0, 1);
+ rot = (-20 + loadI32(0, 0)) * 0x10000;
+
+ for (ringCount = 0; ringCount < 5; ringCount++) {
+ drawLoop(0, 0, 0x90000 + (ringCount * 0x80000));
+ }
+
+ return 0;
+}
+
diff --git a/libs/rs/java/Rollo/res/drawable/settings.png b/libs/rs/java/Rollo/res/raw/settings.png
index dd2cd9570486..dd2cd9570486 100644
--- a/libs/rs/java/Rollo/res/drawable/settings.png
+++ b/libs/rs/java/Rollo/res/raw/settings.png
Binary files differ
diff --git a/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java b/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java
index 91f25c2725bd..8f4833519a62 100644
--- a/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java
+++ b/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java
@@ -24,6 +24,7 @@ import android.renderscript.ProgramVertexAlloc;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
@@ -37,6 +38,17 @@ import android.view.KeyEvent;
import android.view.MotionEvent;
public class RolloRS {
+ //public static final int STATE_SELECTED_ID = 0;
+ public static final int STATE_DONE = 1;
+ //public static final int STATE_PRESSURE = 2;
+ public static final int STATE_ZOOM = 3;
+ //public static final int STATE_WARP = 4;
+ public static final int STATE_ORIENTATION = 5;
+ public static final int STATE_SELECTION = 6;
+ public static final int STATE_FIRST_VISIBLE = 7;
+ public static final int STATE_COUNT = 8;
+ public static final int STATE_TOUCH = 9;
+
public RolloRS() {
}
@@ -48,22 +60,37 @@ public class RolloRS {
initRS();
}
- public void setPosition(float dx, float pressure) {
- mAllocStateBuf[0] += (int)(dx);
- mAllocStateBuf[2] = (int)(pressure * 0x40000);
+ public void setPosition(float column) {
+ mAllocStateBuf[STATE_FIRST_VISIBLE] = (int)(column * (-20));
mAllocState.data(mAllocStateBuf);
}
+ public void setTouch(boolean touch) {
+ mAllocStateBuf[STATE_TOUCH] = touch ? 1 : 0;
+ mAllocState.data(mAllocStateBuf);
+ }
- private Resources mRes;
- private RenderScript mRS;
+ public void setZoom(float z) {
+ //Log.e("rs", "zoom " + Float.toString(z));
+ mAllocStateBuf[STATE_ZOOM] = (int)(z * 1000.f);
+ mAllocState.data(mAllocStateBuf);
+ }
- private RenderScript.Script mScript;
+ public void setSelected(int index) {
+ Log.e("rs", "setSelected " + Integer.toString(index));
+
+ mAllocStateBuf[STATE_SELECTION] = index;
+ mAllocStateBuf[STATE_DONE] = 1;
+ mAllocState.data(mAllocStateBuf);
+ }
+
+ private Resources mRes;
+ private RenderScript mRS;
+ private RenderScript.Script mScript;
private RenderScript.Sampler mSampler;
private RenderScript.ProgramFragmentStore mPFSBackground;
- private RenderScript.ProgramFragmentStore mPFSImages;
private RenderScript.ProgramFragment mPFBackground;
private RenderScript.ProgramFragment mPFImages;
private RenderScript.ProgramVertex mPV;
@@ -77,6 +104,9 @@ public class RolloRS {
private int[] mAllocIconIDBuf;
private RenderScript.Allocation mAllocIconID;
+ private int[] mAllocScratchBuf;
+ private RenderScript.Allocation mAllocScratch;
+
private void initNamed() {
mRS.samplerBegin();
mRS.samplerSet(RenderScript.SamplerParam.FILTER_MIN,
@@ -92,16 +122,13 @@ public class RolloRS {
mRS.programFragmentBegin(null, null);
mRS.programFragmentSetTexEnable(0, true);
- //mRS.programFragmentSetTexEnable(1, true);
- //mRS.programFragmentSetEnvMode(0, RS_TEX_ENV_MODE_REPLACE);
- //mRS.programFragmentSetEnvMode(1, RS_TEX_ENV_MODE_MODULATE);
+ mRS.programFragmentSetTexEnvMode(0, RenderScript.EnvMode.MODULATE);
mPFImages = mRS.programFragmentCreate();
mPFImages.setName("PF");
mPFImages.bindSampler(mSampler, 0);
- mPFImages.bindSampler(mSampler, 1);
mRS.programFragmentStoreBegin(null, null);
- mRS.programFragmentStoreDepthFunc(RenderScript.DepthFunc.ALWAYS);
+ mRS.programFragmentStoreDepthFunc(RenderScript.DepthFunc.LESS);
mRS.programFragmentStoreDitherEnable(false);
mRS.programFragmentStoreDepthMask(false);
mRS.programFragmentStoreBlendFunc(RenderScript.BlendSrcFunc.ONE,
@@ -109,7 +136,6 @@ public class RolloRS {
mPFSBackground = mRS.programFragmentStoreCreate();
mPFSBackground.setName("PFS");
-
mPVAlloc = new ProgramVertexAlloc(mRS);
mRS.programVertexBegin(null, null);
mRS.programVertexSetTextureMatrixEnable(true);
@@ -123,10 +149,18 @@ public class RolloRS {
//mPVAlloc.setupOrthoNormalized(320, 480);
mRS.contextBindProgramVertex(mPV);
+ mAllocScratchBuf = new int[32];
+ for(int ct=0; ct < mAllocScratchBuf.length; ct++) {
+ mAllocScratchBuf[ct] = 0;
+ }
+ mAllocScratch = mRS.allocationCreatePredefSized(
+ RenderScript.ElementPredefined.USER_I32, mAllocScratchBuf.length);
+ mAllocScratch.data(mAllocScratchBuf);
Log.e("rs", "Done loading named");
+
{
mIcons = new RenderScript.Allocation[4];
mAllocIconIDBuf = new int[mIcons.length];
@@ -134,20 +168,21 @@ public class RolloRS {
RenderScript.ElementPredefined.USER_I32, mAllocIconIDBuf.length);
- BitmapDrawable bd;
Bitmap b;
-
- bd = (BitmapDrawable)mRes.getDrawable(R.drawable.browser);
- mIcons[0] = mRS.allocationCreateFromBitmap(bd.getBitmap(), RenderScript.ElementPredefined.RGB_565, true);
+ BitmapFactory.Options opts = new BitmapFactory.Options();
+ opts.inScaled = false;
- bd = (BitmapDrawable)mRes.getDrawable(R.drawable.market);
- mIcons[1] = mRS.allocationCreateFromBitmap(bd.getBitmap(), RenderScript.ElementPredefined.RGB_565, true);
+ b = BitmapFactory.decodeResource(mRes, R.raw.browser, opts);
+ mIcons[0] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
- bd = (BitmapDrawable)mRes.getDrawable(R.drawable.photos);
- mIcons[2] = mRS.allocationCreateFromBitmap(bd.getBitmap(), RenderScript.ElementPredefined.RGB_565, true);
+ b = BitmapFactory.decodeResource(mRes, R.raw.market, opts);
+ mIcons[1] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
- bd = (BitmapDrawable)mRes.getDrawable(R.drawable.settings);
- mIcons[3] = mRS.allocationCreateFromBitmap(bd.getBitmap(), RenderScript.ElementPredefined.RGB_565, true);
+ b = BitmapFactory.decodeResource(mRes, R.raw.photos, opts);
+ mIcons[2] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+
+ b = BitmapFactory.decodeResource(mRes, R.raw.settings, opts);
+ mIcons[3] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
for(int ct=0; ct < mIcons.length; ct++) {
mIcons[ct].uploadToTexture(0);
@@ -190,17 +225,23 @@ public class RolloRS {
private void initRS() {
mRS.scriptCBegin();
- mRS.scriptCSetClearColor(0.0f, 0.0f, 0.1f, 1.0f);
+ mRS.scriptCSetClearColor(0.0f, 0.0f, 0.0f, 0.0f);
mRS.scriptCSetScript(mRes, R.raw.rollo);
+ //mRS.scriptCSetScript(mRes, R.raw.rollo2);
mRS.scriptCSetRoot(true);
+ //mRS.scriptCSetClearDepth(0);
mScript = mRS.scriptCCreate();
- mAllocStateBuf = new int[] {0, 38, 0};
+ mAllocStateBuf = new int[] {0, 0, 0, 8, 0, 0, 0, 0, 38, 0, 0};
mAllocState = mRS.allocationCreatePredefSized(
RenderScript.ElementPredefined.USER_I32, mAllocStateBuf.length);
mScript.bindAllocation(mAllocState, 0);
mScript.bindAllocation(mAllocIconID, 1);
- setPosition(0, 0);
+ mScript.bindAllocation(mAllocScratch, 2);
+ setPosition(0);
+ setZoom(1);
+
+ //RenderScript.File f = mRS.fileOpen("/sdcard/test.a3d");
mRS.contextBindRootScript(mScript);
}
diff --git a/libs/rs/java/Rollo/src/com/android/rollo/RolloView.java b/libs/rs/java/Rollo/src/com/android/rollo/RolloView.java
index 27d2dd67d11e..b5e02af9867a 100644
--- a/libs/rs/java/Rollo/src/com/android/rollo/RolloView.java
+++ b/libs/rs/java/Rollo/src/com/android/rollo/RolloView.java
@@ -19,6 +19,7 @@ package com.android.rollo;
import java.io.Writer;
import java.util.ArrayList;
import java.util.concurrent.Semaphore;
+import java.lang.Float;
import android.renderscript.RSSurfaceView;
import android.renderscript.RenderScript;
@@ -37,13 +38,14 @@ import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import android.graphics.PixelFormat;
-public class RolloView extends RSSurfaceView {
+public class RolloView extends RSSurfaceView {
public RolloView(Context context) {
super(context);
-
- //setFocusable(true);
+ setFocusable(true);
+ getHolder().setFormat(PixelFormat.TRANSLUCENT);
}
private RenderScript mRS;
@@ -65,6 +67,48 @@ public class RolloView extends RSSurfaceView {
return super.onKeyDown(keyCode, event);
}
+ boolean mControlMode = false;
+ boolean mZoomMode = false;
+ boolean mFlingMode = false;
+ float mFlingX = 0;
+ float mFlingY = 0;
+ float mColumn = -1;
+ float mOldColumn;
+ float mZoom = 1;
+
+ int mIconCount = 38;
+ int mRows = 4;
+ int mColumns = (mIconCount + mRows - 1) / mRows;
+
+ float mMaxZoom = ((float)mColumns) / 3.f;
+
+
+ void setColumn(boolean clamp)
+ {
+ //Log.e("rs", " col = " + Float.toString(mColumn));
+ float c = mColumn;
+ if(c > (mColumns -2)) {
+ c = (mColumns -2);
+ }
+ if(c < 1) {
+ c = 1;
+ }
+ mRender.setPosition(c);
+ if(clamp) {
+ mColumn = c;
+ }
+ }
+
+ void computeSelection(float x, float y)
+ {
+ float col = mColumn + (x - 0.5f) * 3;
+ int iCol = (int)(col + 0.25f);
+
+ float row = (y / 0.8f) * mRows;
+ int iRow = (int)(row - 0.25f);
+
+ mRender.setSelected(iCol * mRows + iRow);
+ }
@Override
public boolean onTouchEvent(MotionEvent ev)
@@ -74,14 +118,91 @@ public class RolloView extends RSSurfaceView {
if (act == ev.ACTION_UP) {
ret = false;
}
- float x = ev.getX();
- x = (x - 180) / 40;
- //Log.e("rs", Float(x).toString());
- mRender.setPosition(x, ev.getPressure());
- //mRender.newTouchPosition((int)ev.getX(), (int)ev.getY());
+ float nx = ev.getX() / getWidth();
+ float ny = ev.getY() / getHeight();
+
+ mRender.setTouch(ret);
+
+ if((ny > 0.85f) || mControlMode) {
+ mFlingMode = false;
+
+ // Projector control
+ if((nx > 0.2f) && (nx < 0.8f) || mControlMode) {
+ if(act != ev.ACTION_UP) {
+ float zoom = mMaxZoom;
+ if(mControlMode) {
+ if(!mZoomMode) {
+ zoom = 1.f;
+ }
+ float dx = nx - mFlingX;
+
+ if((ny < 0.9) && mZoomMode) {
+ zoom = mMaxZoom - ((0.9f - ny) * 10.f);
+ if(zoom < 1) {
+ zoom = 1;
+ mZoomMode = false;
+ }
+ mOldColumn = mColumn;
+ }
+ mColumn += dx * 4;// * zoom;
+ if(zoom > 1.01f) {
+ mColumn += (mZoom - zoom) * (nx - 0.5f) * 4 * zoom;
+ }
+ } else {
+ mOldColumn = mColumn;
+ mColumn = ((float)mColumns) / 2;
+ mControlMode = true;
+ mZoomMode = true;
+ }
+ mZoom = zoom;
+ mFlingX = nx;
+ mRender.setZoom(zoom);
+ } else {
+ if(mControlMode && (mZoom < 1.01f)) {
+ computeSelection(nx, ny);
+ }
+ mControlMode = false;
+ mColumn = mOldColumn;
+ mRender.setZoom(1.f);
+ }
+ } else {
+ // Do something with corners here....
+ }
+ setColumn(true);
+
+ } else {
+ // icon control
+ if(act != ev.ACTION_UP) {
+ if(mFlingMode) {
+ mColumn += (mFlingX - nx) * 4;
+ setColumn(true);
+ }
+ mFlingMode = true;
+ mFlingX = nx;
+ mFlingY = ny;
+ } else {
+ mFlingMode = false;
+ }
+ }
+
+
return ret;
}
+
+ @Override
+ public boolean onTrackballEvent(MotionEvent ev)
+ {
+ float x = ev.getX();
+ float y = ev.getY();
+ //Float tx = new Float(x);
+ //Float ty = new Float(y);
+ //Log.e("rs", "tbe " + tx.toString() + ", " + ty.toString());
+
+
+ return true;
+ }
+
}
diff --git a/libs/rs/jni/RenderScript_jni.cpp b/libs/rs/jni/RenderScript_jni.cpp
index 248a6bd7c264..17476734f178 100644
--- a/libs/rs/jni/RenderScript_jni.cpp
+++ b/libs/rs/jni/RenderScript_jni.cpp
@@ -810,6 +810,14 @@ nProgramVertexSetTextureMatrixEnable(JNIEnv *_env, jobject _this, jboolean enabl
rsProgramVertexSetTextureMatrixEnable(enable);
}
+static void
+nProgramVertexAddLight(JNIEnv *_env, jobject _this, jint light)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nProgramVertexAddLight, con(%p), light(%p)", con, (RsLight)light);
+ rsProgramVertexAddLight((RsLight)light);
+}
+
static jint
nProgramVertexCreate(JNIEnv *_env, jobject _this)
{
@@ -1048,6 +1056,7 @@ static JNINativeMethod methods[] = {
{"nProgramVertexBegin", "(II)V", (void*)nProgramVertexBegin },
{"nProgramVertexSetType", "(II)V", (void*)nProgramVertexSetType },
{"nProgramVertexSetTextureMatrixEnable", "(Z)V", (void*)nProgramVertexSetTextureMatrixEnable },
+{"nProgramVertexAddLight", "(I)V", (void*)nProgramVertexAddLight },
{"nProgramVertexCreate", "()I", (void*)nProgramVertexCreate },
{"nLightBegin", "()V", (void*)nLightBegin },
diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec
index 62533af020bb..2f99808b8682 100644
--- a/libs/rs/rs.spec
+++ b/libs/rs/rs.spec
@@ -403,6 +403,10 @@ ProgramVertexSetTextureMatrixEnable {
param bool enable
}
+ProgramVertexAddLight {
+ param RsLight light
+ }
+
LightBegin {
}
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index 4025ff81a008..78b8bf89e9e8 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -35,9 +35,16 @@ void Context::initEGL()
EGLint s_configAttribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+#if 1
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 8,
+#else
EGL_RED_SIZE, 5,
EGL_GREEN_SIZE, 6,
EGL_BLUE_SIZE, 5,
+#endif
EGL_DEPTH_SIZE, 16,
EGL_NONE
};
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
index a7fd17c51614..497dbcf7620f 100644
--- a/libs/rs/rsContext.h
+++ b/libs/rs/rsContext.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_RS_CONTEXT_H
#define ANDROID_RS_CONTEXT_H
+#include "rsUtils.h"
+
#include <utils/Vector.h>
#include <ui/Surface.h>
@@ -30,10 +32,10 @@
#include "rsAllocation.h"
#include "rsAdapter.h"
#include "rsSampler.h"
+#include "rsLight.h"
#include "rsProgramFragment.h"
#include "rsProgramFragmentStore.h"
#include "rsProgramVertex.h"
-#include "rsLight.h"
#include "rsgApiStructs.h"
#include "rsLocklessFifo.h"
diff --git a/libs/rs/rsFileA3D.cpp b/libs/rs/rsFileA3D.cpp
index f669417d11cb..86d294bf3160 100644
--- a/libs/rs/rsFileA3D.cpp
+++ b/libs/rs/rsFileA3D.cpp
@@ -261,9 +261,12 @@ void FileA3D::processChunk_Primitive(Context *rsc, IO *io, A3DIndexEntry *ie)
p->mIndexCount = io->loadU32();
uint32_t vertIdx = io->loadU32();
+ p->mRestartCounts = io->loadU16();
uint32_t bits = io->loadU8();
p->mType = (RsPrimitive)io->loadU8();
+ LOGE("processChunk_Primitive count %i, bits %i", p->mIndexCount, bits);
+
p->mVerticies = (Mesh::Verticies_t *)mIndex[vertIdx].mRsObj;
p->mIndicies = new uint16_t[p->mIndexCount];
@@ -279,6 +282,27 @@ void FileA3D::processChunk_Primitive(Context *rsc, IO *io, A3DIndexEntry *ie)
p->mIndicies[ct] = io->loadU32();
break;
}
+ LOGE(" idx %i", p->mIndicies[ct]);
+ }
+
+ if (p->mRestartCounts) {
+ p->mRestarts = new uint16_t[p->mRestartCounts];
+ for (uint32_t ct = 0; ct < p->mRestartCounts; ct++) {
+ switch(bits) {
+ case 8:
+ p->mRestarts[ct] = io->loadU8();
+ break;
+ case 16:
+ p->mRestarts[ct] = io->loadU16();
+ break;
+ case 32:
+ p->mRestarts[ct] = io->loadU32();
+ break;
+ }
+ LOGE(" idx %i", p->mRestarts[ct]);
+ }
+ } else {
+ p->mRestarts = NULL;
}
ie->mRsObj = p;
@@ -289,16 +313,17 @@ void FileA3D::processChunk_Verticies(Context *rsc, IO *io, A3DIndexEntry *ie)
Mesh::Verticies_t *cv = new Mesh::Verticies_t;
cv->mAllocationCount = io->loadU32();
cv->mAllocations = new Allocation *[cv->mAllocationCount];
+ LOGE("processChunk_Verticies count %i", cv->mAllocationCount);
for (uint32_t ct = 0; ct < cv->mAllocationCount; ct++) {
uint32_t i = io->loadU32();
cv->mAllocations[ct] = (Allocation *)mIndex[i].mRsObj;
+ LOGE(" idx %i", i);
}
ie->mRsObj = cv;
}
void FileA3D::processChunk_Element(Context *rsc, IO *io, A3DIndexEntry *ie)
{
- LOGE("processChunk_Element ie %p", ie);
rsi_ElementBegin(rsc);
uint32_t count = io->loadU32();
@@ -320,6 +345,8 @@ void FileA3D::processChunk_ElementSource(Context *rsc, IO *io, A3DIndexEntry *ie
uint32_t index = io->loadU32();
uint32_t count = io->loadU32();
+ LOGE("processChunk_ElementSource count %i, index %i", count, index);
+
RsElement e = (RsElement)mIndex[index].mRsObj;
RsAllocation a = rsi_AllocationCreateSized(rsc, e, count);
@@ -328,6 +355,7 @@ void FileA3D::processChunk_ElementSource(Context *rsc, IO *io, A3DIndexEntry *ie
float * data = (float *)alloc->getPtr();
while(count--) {
*data = io->loadF();
+ LOGE(" %f", *data);
data++;
}
ie->mRsObj = alloc;
diff --git a/libs/rs/rsLight.cpp b/libs/rs/rsLight.cpp
index 67d009544402..24b58b622ac6 100644
--- a/libs/rs/rsLight.cpp
+++ b/libs/rs/rsLight.cpp
@@ -16,6 +16,8 @@
#include "rsContext.h"
+#include <GLES/gl.h>
+
using namespace android;
using namespace android::renderscript;
@@ -25,13 +27,15 @@ Light::Light(bool isLocal, bool isMono)
mIsLocal = isLocal;
mIsMono = isMono;
- mX = 0;
- mY = 0;
- mZ = 0;
+ mPosition[0] = 0;
+ mPosition[1] = 0;
+ mPosition[2] = 1;
+ mPosition[3] = 0;
- mR = 1.f;
- mG = 1.f;
- mB = 1.f;
+ mColor[0] = 1.f;
+ mColor[1] = 1.f;
+ mColor[2] = 1.f;
+ mColor[3] = 1.f;
}
Light::~Light()
@@ -40,16 +44,23 @@ Light::~Light()
void Light::setPosition(float x, float y, float z)
{
- mX = x;
- mY = y;
- mZ = z;
+ mPosition[0] = x;
+ mPosition[1] = y;
+ mPosition[2] = z;
}
void Light::setColor(float r, float g, float b)
{
- mR = r;
- mG = g;
- mB = b;
+ mColor[0] = r;
+ mColor[1] = g;
+ mColor[2] = b;
+}
+
+void Light::setupGL(uint32_t num) const
+{
+ glLightfv(GL_LIGHT0 + num, GL_DIFFUSE, mColor);
+ glLightfv(GL_LIGHT0 + num, GL_SPECULAR, mColor);
+ glLightfv(GL_LIGHT0 + num, GL_POSITION, mPosition);
}
////////////////////////////////////////////
diff --git a/libs/rs/rsLight.h b/libs/rs/rsLight.h
index 76d1eccacdb7..b0c33862f52d 100644
--- a/libs/rs/rsLight.h
+++ b/libs/rs/rsLight.h
@@ -36,9 +36,11 @@ public:
void setPosition(float x, float y, float z);
void setColor(float r, float g, float b);
+ void setupGL(uint32_t num) const;
+
protected:
- float mR, mG, mB;
- float mX, mY, mZ;
+ float mColor[4];
+ float mPosition[4];
bool mIsLocal;
bool mIsMono;
};
diff --git a/libs/rs/rsMesh.h b/libs/rs/rsMesh.h
index 1db36e1aa635..be207a3adab0 100644
--- a/libs/rs/rsMesh.h
+++ b/libs/rs/rsMesh.h
@@ -57,6 +57,9 @@ public:
uint32_t mIndexCount;
uint16_t *mIndicies;
+
+ uint32_t mRestartCounts;
+ uint16_t *mRestarts;
};
Verticies_t * mVerticies;
diff --git a/libs/rs/rsProgramFragmentStore.cpp b/libs/rs/rsProgramFragmentStore.cpp
index 0b6568036bb5..9ee270f2226e 100644
--- a/libs/rs/rsProgramFragmentStore.cpp
+++ b/libs/rs/rsProgramFragmentStore.cpp
@@ -61,8 +61,10 @@ void ProgramFragmentStore::setupGL()
glDisable(GL_BLEND);
}
+ //LOGE("pfs %i, %i, %x", mDepthWriteEnable, mDepthTestEnable, mDepthFunc);
+
glDepthMask(mDepthWriteEnable);
- if(mDepthTestEnable) {
+ if(mDepthTestEnable || mDepthWriteEnable) {
glEnable(GL_DEPTH_TEST);
glDepthFunc(mDepthFunc);
} else {
diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp
index 4089507bbb98..417ba6aed13e 100644
--- a/libs/rs/rsProgramVertex.cpp
+++ b/libs/rs/rsProgramVertex.cpp
@@ -28,6 +28,7 @@ ProgramVertex::ProgramVertex(Element *in, Element *out) :
Program(in, out)
{
mTextureMatrixEnable = false;
+ mLightCount = 0;
}
ProgramVertex::~ProgramVertex()
@@ -54,8 +55,29 @@ void ProgramVertex::setupGL()
glLoadIdentity();
}
- //logMatrix("prog", &f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET]);
- //logMatrix("model", &f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET]);
+
+ LOGE("lights %i ", mLightCount);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ if (mLightCount) {
+ int v = 1;
+ glEnable(GL_LIGHTING);
+ glLightModelxv(GL_LIGHT_MODEL_TWO_SIDE, &v);
+ for (uint32_t ct = 0; ct < mLightCount; ct++) {
+ const Light *l = mLights[ct].get();
+ glEnable(GL_LIGHT0 + ct);
+ l->setupGL(ct);
+ }
+ for (uint32_t ct = mLightCount; ct < MAX_LIGHTS; ct++) {
+ glDisable(GL_LIGHT0 + ct);
+ }
+ } else {
+ glDisable(GL_LIGHTING);
+ }
+
+ if (!f) {
+ LOGE("Must bind constants to vertex program");
+ }
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET]);
@@ -73,6 +95,14 @@ void ProgramVertex::bindAllocation(uint32_t slot, Allocation *a)
mConstants[slot].set(a);
}
+void ProgramVertex::addLight(const Light *l)
+{
+ if (mLightCount < MAX_LIGHTS) {
+ mLights[mLightCount].set(l);
+ mLightCount++;
+ }
+}
+
ProgramVertexState::ProgramVertexState()
{
@@ -136,6 +166,10 @@ void rsi_ProgramVertexSetTextureMatrixEnable(Context *rsc, bool enable)
rsc->mStateVertex.mPV->setTextureMatrixEnable(enable);
}
+void rsi_ProgramVertexAddLight(Context *rsc, RsLight light)
+{
+ rsc->mStateVertex.mPV->addLight(static_cast<const Light *>(light));
+}
}
diff --git a/libs/rs/rsProgramVertex.h b/libs/rs/rsProgramVertex.h
index 1a92f013c7b1..ac15b70a7cb6 100644
--- a/libs/rs/rsProgramVertex.h
+++ b/libs/rs/rsProgramVertex.h
@@ -28,6 +28,7 @@ class ProgramVertex : public Program
{
public:
const static uint32_t MAX_CONSTANTS = 2;
+ const static uint32_t MAX_LIGHTS = 8;
ProgramVertex(Element *in, Element *out);
virtual ~ProgramVertex();
@@ -38,12 +39,16 @@ public:
void setConstantType(uint32_t slot, const Type *);
void bindAllocation(uint32_t slot, Allocation *);
void setTextureMatrixEnable(bool e) {mTextureMatrixEnable = e;}
+ void addLight(const Light *);
protected:
bool mDirty;
+ uint32_t mLightCount;
ObjectBaseRef<Allocation> mConstants[MAX_CONSTANTS];
ObjectBaseRef<const Type> mConstantTypes[MAX_CONSTANTS];
+ ObjectBaseRef<const Light> mLights[MAX_LIGHTS];
+
// Hacks to create a program for now
bool mTextureMatrixEnable;
@@ -61,6 +66,8 @@ public:
ObjectBaseRef<ProgramVertex> mDefault;
ObjectBaseRef<Allocation> mDefaultAlloc;
+
+
ProgramVertex *mPV;
diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp
index 0ec6bafe9730..842c836b9ec5 100644
--- a/libs/rs/rsScriptC.cpp
+++ b/libs/rs/rsScriptC.cpp
@@ -46,415 +46,6 @@ ScriptC::~ScriptC()
}
}
-extern "C" float fixedToFloat(int32_t f)
-{
- return ((float)f) / 0x10000;
-}
-
-extern "C" float intToFloat(int32_t f)
-{
- return (float)f;
-}
-
-extern "C" void matrixLoadIdentity(rsc_Matrix *mat)
-{
- Matrix *m = reinterpret_cast<Matrix *>(mat);
- m->loadIdentity();
-}
-
-extern "C" void matrixLoadFloat(rsc_Matrix *mat, const float *f)
-{
- Matrix *m = reinterpret_cast<Matrix *>(mat);
- m->load(f);
-}
-
-extern "C" void matrixLoadMat(rsc_Matrix *mat, const rsc_Matrix *newmat)
-{
- Matrix *m = reinterpret_cast<Matrix *>(mat);
- m->load(reinterpret_cast<const Matrix *>(newmat));
-}
-
-extern "C" void matrixLoadRotate(rsc_Matrix *mat, float rot, float x, float y, float z)
-{
- Matrix *m = reinterpret_cast<Matrix *>(mat);
- m->loadRotate(rot, x, y, z);
-}
-
-extern "C" void matrixLoadScale(rsc_Matrix *mat, float x, float y, float z)
-{
- Matrix *m = reinterpret_cast<Matrix *>(mat);
- m->loadScale(x, y, z);
-}
-
-extern "C" void matrixLoadTranslate(rsc_Matrix *mat, float x, float y, float z)
-{
- Matrix *m = reinterpret_cast<Matrix *>(mat);
- m->loadTranslate(x, y, z);
-}
-
-extern "C" void matrixLoadMultiply(rsc_Matrix *mat, const rsc_Matrix *lhs, const rsc_Matrix *rhs)
-{
- Matrix *m = reinterpret_cast<Matrix *>(mat);
- m->loadMultiply(reinterpret_cast<const Matrix *>(lhs),
- reinterpret_cast<const Matrix *>(rhs));
-}
-
-extern "C" void matrixMultiply(rsc_Matrix *mat, const rsc_Matrix *rhs)
-{
- Matrix *m = reinterpret_cast<Matrix *>(mat);
- m->multiply(reinterpret_cast<const Matrix *>(rhs));
-}
-
-extern "C" void matrixRotate(rsc_Matrix *mat, float rot, float x, float y, float z)
-{
- Matrix *m = reinterpret_cast<Matrix *>(mat);
- m->rotate(rot, x, y, z);
-}
-
-extern "C" void matrixScale(rsc_Matrix *mat, float x, float y, float z)
-{
- Matrix *m = reinterpret_cast<Matrix *>(mat);
- m->scale(x, y, z);
-}
-
-extern "C" void matrixTranslate(rsc_Matrix *mat, float x, float y, float z)
-{
- Matrix *m = reinterpret_cast<Matrix *>(mat);
- m->translate(x, y, z);
-}
-
-
-extern "C" const void * loadVp(uint32_t bank, uint32_t offset)
-{
- GET_TLS();
- return &static_cast<const uint8_t *>(sc->mSlots[bank]->getPtr())[offset];
-}
-
-extern "C" float loadF(uint32_t bank, uint32_t offset)
-{
- GET_TLS();
- return static_cast<const float *>(sc->mSlots[bank]->getPtr())[offset];
-}
-
-extern "C" int32_t loadI32(uint32_t bank, uint32_t offset)
-{
- GET_TLS();
- return static_cast<const int32_t *>(sc->mSlots[bank]->getPtr())[offset];
-}
-
-extern "C" uint32_t loadU32(uint32_t bank, uint32_t offset)
-{
- GET_TLS();
- return static_cast<const uint32_t *>(sc->mSlots[bank]->getPtr())[offset];
-}
-
-extern "C" void loadEnvVec4(uint32_t bank, uint32_t offset, rsc_Vector4 *v)
-{
- GET_TLS();
- memcpy(v, &static_cast<const float *>(sc->mSlots[bank]->getPtr())[offset], sizeof(rsc_Vector4));
-}
-
-extern "C" void loadEnvMatrix(uint32_t bank, uint32_t offset, rsc_Matrix *m)
-{
- GET_TLS();
- memcpy(m, &static_cast<const float *>(sc->mSlots[bank]->getPtr())[offset], sizeof(rsc_Matrix));
-}
-
-
-extern "C" void storeF(uint32_t bank, uint32_t offset, float v)
-{
- GET_TLS();
- static_cast<float *>(sc->mSlots[bank]->getPtr())[offset] = v;
-}
-
-extern "C" void storeI32(uint32_t bank, uint32_t offset, int32_t v)
-{
- GET_TLS();
- static_cast<int32_t *>(sc->mSlots[bank]->getPtr())[offset] = v;
-}
-
-extern "C" void storeU32(uint32_t bank, uint32_t offset, uint32_t v)
-{
- GET_TLS();
- static_cast<uint32_t *>(sc->mSlots[bank]->getPtr())[offset] = v;
-}
-
-extern "C" void storeEnvVec4(uint32_t bank, uint32_t offset, const rsc_Vector4 *v)
-{
- GET_TLS();
- memcpy(&static_cast<float *>(sc->mSlots[bank]->getPtr())[offset], v, sizeof(rsc_Vector4));
-}
-
-extern "C" void storeEnvMatrix(uint32_t bank, uint32_t offset, const rsc_Matrix *m)
-{
- GET_TLS();
- memcpy(&static_cast<float *>(sc->mSlots[bank]->getPtr())[offset], m, sizeof(rsc_Matrix));
-}
-
-
-extern "C" void color(float r, float g, float b, float a)
-{
- glColor4f(r, g, b, a);
-}
-
-extern "C" void renderTriangleMesh(RsTriangleMesh mesh)
-{
- GET_TLS();
- rsi_TriangleMeshRender(rsc, mesh);
-}
-
-extern "C" void renderTriangleMeshRange(RsTriangleMesh mesh, uint32_t start, uint32_t count)
-{
- GET_TLS();
- rsi_TriangleMeshRenderRange(rsc, mesh, start, count);
-}
-
-extern "C" void materialDiffuse(float r, float g, float b, float a)
-{
- float v[] = {r, g, b, a};
- glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, v);
-}
-
-extern "C" void materialSpecular(float r, float g, float b, float a)
-{
- float v[] = {r, g, b, a};
- glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, v);
-}
-
-extern "C" void lightPosition(float x, float y, float z, float w)
-{
- float v[] = {x, y, z, w};
- glLightfv(GL_LIGHT0, GL_POSITION, v);
-}
-
-extern "C" void materialShininess(float s)
-{
- glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, &s);
-}
-
-extern "C" void uploadToTexture(RsAllocation va, uint32_t baseMipLevel)
-{
- GET_TLS();
- rsi_AllocationUploadToTexture(rsc, va, baseMipLevel);
-}
-
-extern "C" void enable(uint32_t p)
-{
- glEnable(p);
-}
-
-extern "C" void disable(uint32_t p)
-{
- glDisable(p);
-}
-
-extern "C" uint32_t scriptRand(uint32_t max)
-{
- return (uint32_t)(((float)rand()) * max / RAND_MAX);
-}
-
-// Assumes (GL_FIXED) x,y,z (GL_UNSIGNED_BYTE)r,g,b,a
-extern "C" void drawTriangleArray(RsAllocation alloc, uint32_t count)
-{
- GET_TLS();
-
- const Allocation *a = (const Allocation *)alloc;
- const uint32_t *ptr = (const uint32_t *)a->getPtr();
-
- rsc->setupCheck();
-
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tm->mBufferObjects[1]);
-
- glEnableClientState(GL_VERTEX_ARRAY);
- glDisableClientState(GL_NORMAL_ARRAY);
- glDisableClientState(GL_TEXTURE_COORD_ARRAY);
- glEnableClientState(GL_COLOR_ARRAY);
-
- glVertexPointer(2, GL_FIXED, 12, ptr + 1);
- //glTexCoordPointer(2, GL_FIXED, 24, ptr + 1);
- glColorPointer(4, GL_UNSIGNED_BYTE, 12, ptr);
-
- glDrawArrays(GL_TRIANGLES, 0, count * 3);
-}
-
-extern "C" void drawRect(int32_t x1, int32_t x2, int32_t y1, int32_t y2)
-{
- GET_TLS();
- x1 = (x1 << 16);
- x2 = (x2 << 16);
- y1 = (y1 << 16);
- y2 = (y2 << 16);
-
- int32_t vtx[] = {x1,y1, x1,y2, x2,y1, x2,y2};
- static const int32_t tex[] = {0,0, 0,0x10000, 0x10000,0, 0x10000,0x10000};
-
-
- rsc->setupCheck();
-
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tm->mBufferObjects[1]);
-
- glEnableClientState(GL_VERTEX_ARRAY);
- glEnableClientState(GL_TEXTURE_COORD_ARRAY);
- glDisableClientState(GL_NORMAL_ARRAY);
- glDisableClientState(GL_COLOR_ARRAY);
-
- glVertexPointer(2, GL_FIXED, 8, vtx);
- glTexCoordPointer(2, GL_FIXED, 8, tex);
- //glColorPointer(4, GL_UNSIGNED_BYTE, 12, ptr);
-
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
-}
-
-extern "C" void drawQuad(int32_t x1, int32_t y1, int32_t z1,
- int32_t x2, int32_t y2, int32_t z2,
- int32_t x3, int32_t y3, int32_t z3,
- int32_t x4, int32_t y4, int32_t z4)
-{
- GET_TLS();
- //x1 = (x1 << 16);
- //x2 = (x2 << 16);
- //y1 = (y1 << 16);
- //y2 = (y2 << 16);
-
- //LOGE("Quad");
- //LOGE("0x%08x, 0x%08x, 0x%08x", x1, y1, z1);
- //LOGE("0x%08x, 0x%08x, 0x%08x", x2, y2, z2);
- //LOGE("0x%08x, 0x%08x, 0x%08x", x3, y3, z3);
- //LOGE("0x%08x, 0x%08x, 0x%08x", x4, y4, z4);
-
- int32_t vtx[] = {x1,y1,z1, x2,y2,z2, x3,y3,z3, x4,y4,z4};
- static const int32_t tex[] = {0,0, 0,0x10000, 0x10000,0x10000, 0x10000,0};
-
-
- rsc->setupCheck();
-
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tm->mBufferObjects[1]);
-
- glEnableClientState(GL_VERTEX_ARRAY);
- glVertexPointer(3, GL_FIXED, 0, vtx);
-
- glClientActiveTexture(GL_TEXTURE0);
- glEnableClientState(GL_TEXTURE_COORD_ARRAY);
- glTexCoordPointer(2, GL_FIXED, 0, tex);
- glClientActiveTexture(GL_TEXTURE1);
- glEnableClientState(GL_TEXTURE_COORD_ARRAY);
- glTexCoordPointer(2, GL_FIXED, 0, tex);
- glClientActiveTexture(GL_TEXTURE0);
-
- glDisableClientState(GL_NORMAL_ARRAY);
- glDisableClientState(GL_COLOR_ARRAY);
-
- //glColorPointer(4, GL_UNSIGNED_BYTE, 12, ptr);
-
- glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
-}
-
-extern "C" int32_t sinx(int32_t angle)
-{
- float a = ((float)angle) / 0x10000;
- a *= 3.14f / 180.f;
- float s = (float)sin(a);
- return int32_t(s * 0x10000);
-}
-
-extern "C" int32_t cosx(int32_t angle)
-{
- float a = ((float)angle) / 0x10000;
- a *= 3.14f / 180.f;
- float s = (float)cos(a);
- return int32_t(s * 0x10000);
-}
-
-extern "C" void pfBindTexture(RsProgramFragment vpf, uint32_t slot, RsAllocation va)
-{
- GET_TLS();
- rsi_ProgramFragmentBindTexture(rsc,
- static_cast<ProgramFragment *>(vpf),
- slot,
- static_cast<Allocation *>(va));
-
-}
-
-extern "C" void pfBindSampler(RsProgramFragment vpf, uint32_t slot, RsSampler vs)
-{
- GET_TLS();
- rsi_ProgramFragmentBindSampler(rsc,
- static_cast<ProgramFragment *>(vpf),
- slot,
- static_cast<Sampler *>(vs));
-
-}
-
-extern "C" void contextBindProgramFragmentStore(RsProgramFragmentStore pfs)
-{
- GET_TLS();
- rsi_ContextBindProgramFragmentStore(rsc, pfs);
-
-}
-
-extern "C" void contextBindProgramFragment(RsProgramFragment pf)
-{
- GET_TLS();
- rsi_ContextBindProgramFragment(rsc, pf);
-
-}
-
-
-static rsc_FunctionTable scriptCPtrTable = {
- loadVp,
- loadF,
- loadI32,
- loadU32,
- loadEnvVec4,
- loadEnvMatrix,
-
- storeF,
- storeI32,
- storeU32,
- storeEnvVec4,
- storeEnvMatrix,
-
- matrixLoadIdentity,
- matrixLoadFloat,
- matrixLoadMat,
- matrixLoadRotate,
- matrixLoadScale,
- matrixLoadTranslate,
- matrixLoadMultiply,
- matrixMultiply,
- matrixRotate,
- matrixScale,
- matrixTranslate,
-
- color,
-
- pfBindTexture,
- pfBindSampler,
-
- materialDiffuse,
- materialSpecular,
- lightPosition,
- materialShininess,
- uploadToTexture,
- enable,
- disable,
-
- scriptRand,
- contextBindProgramFragment,
- contextBindProgramFragmentStore,
-
-
- renderTriangleMesh,
- renderTriangleMeshRange,
-
- drawTriangleArray,
- drawRect
-
-};
-
bool ScriptC::run(Context *rsc, uint32_t launchIndex)
{
@@ -471,9 +62,11 @@ bool ScriptC::run(Context *rsc, uint32_t launchIndex)
rsc->setVertex(mEnviroment.mVertex.get());
}
+ bool ret = false;
tls->mScript = this;
- return mProgram.mScript(launchIndex, &scriptCPtrTable) != 0;
+ ret = mProgram.mScript(launchIndex) != 0;
tls->mScript = NULL;
+ return ret;
}
ScriptCState::ScriptCState()
@@ -507,6 +100,15 @@ void ScriptCState::clear()
}
+static ACCvoid* symbolLookup(ACCvoid* pContext, const ACCchar* name)
+{
+ const ScriptCState::SymbolTable_t *sym = ScriptCState::lookupSymbol(name);
+ if (sym) {
+ return sym->mPtr;
+ }
+ LOGE("ScriptC sym lookup failed for %s", name);
+ return NULL;
+}
void ScriptCState::runCompiler(Context *rsc)
{
@@ -514,14 +116,25 @@ void ScriptCState::runCompiler(Context *rsc)
String8 tmp;
rsc->appendNameDefines(&tmp);
+ appendDecls(&tmp);
+ tmp.append("#line 1\n");
const char* scriptSource[] = {tmp.string(), mProgram.mScriptText};
int scriptLength[] = {tmp.length(), mProgram.mScriptTextLength} ;
accScriptSource(mAccScript, sizeof(scriptLength) / sizeof(int), scriptSource, scriptLength);
+ accRegisterSymbolCallback(mAccScript, symbolLookup, NULL);
accCompileScript(mAccScript);
accGetScriptLabel(mAccScript, "main", (ACCvoid**) &mProgram.mScript);
rsAssert(mProgram.mScript);
+ if (!mProgram.mScript) {
+ ACCchar buf[4096];
+ ACCsizei len;
+ accGetScriptInfoLog(mAccScript, sizeof(buf), &len, buf);
+ LOGE(buf);
+
+ }
+
mEnviroment.mFragment.set(rsc->getDefaultProgramFragment());
mEnviroment.mVertex.set(rsc->getDefaultProgramVertex());
mEnviroment.mFragmentStore.set(rsc->getDefaultProgramFragmentStore());
@@ -638,7 +251,7 @@ void rsi_ScriptCAddType(Context * rsc, RsType vt)
void rsi_ScriptCSetScript(Context * rsc, void *vp)
{
ScriptCState *ss = &rsc->mScriptC;
- ss->mProgram.mScript = reinterpret_cast<rsc_RunScript>(vp);
+ ss->mProgram.mScript = reinterpret_cast<ScriptC::RunScript_t>(vp);
}
void rsi_ScriptCSetRoot(Context * rsc, bool isRoot)
diff --git a/libs/rs/rsScriptC.h b/libs/rs/rsScriptC.h
index c46901b14806..860b4d19e1ab 100644
--- a/libs/rs/rsScriptC.h
+++ b/libs/rs/rsScriptC.h
@@ -32,6 +32,7 @@ namespace renderscript {
class ScriptC : public Script
{
public:
+ typedef int (*RunScript_t)(uint32_t launchIndex);
ScriptC();
virtual ~ScriptC();
@@ -44,7 +45,7 @@ public:
int mVersionMajor;
int mVersionMinor;
- rsc_RunScript mScript;
+ RunScript_t mScript;
};
Program_t mProgram;
@@ -69,6 +70,16 @@ public:
void clear();
void runCompiler(Context *rsc);
+
+ struct SymbolTable_t {
+ const char * mName;
+ void * mPtr;
+ const char * mRet;
+ const char * mParam;
+ };
+ static SymbolTable_t gSyms[];
+ static const SymbolTable_t * lookupSymbol(const char *);
+ static void appendDecls(String8 *str);
};
diff --git a/libs/rs/rsScriptC_Lib.cpp b/libs/rs/rsScriptC_Lib.cpp
new file mode 100644
index 000000000000..129b19fc5a54
--- /dev/null
+++ b/libs/rs/rsScriptC_Lib.cpp
@@ -0,0 +1,531 @@
+/*
+ * 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.
+ */
+
+#include "rsContext.h"
+#include "rsScriptC.h"
+#include "rsMatrix.h"
+
+#include "acc/acc.h"
+#include "utils/String8.h"
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+using namespace android;
+using namespace android::renderscript;
+
+#define GET_TLS() Context::ScriptTLSStruct * tls = \
+ (Context::ScriptTLSStruct *)pthread_getspecific(Context::gThreadTLSKey); \
+ Context * rsc = tls->mContext; \
+ ScriptC * sc = (ScriptC *) tls->mScript
+
+
+//////////////////////////////////////////////////////////////////////////////
+// IO routines
+//////////////////////////////////////////////////////////////////////////////
+
+static float SC_loadF(uint32_t bank, uint32_t offset)
+{
+ GET_TLS();
+ const void *vp = sc->mSlots[bank]->getPtr();
+ const float *f = static_cast<const float *>(vp);
+ //LOGE("loadF %i %i = %f %x", bank, offset, f, ((int *)&f)[0]);
+ return f[offset];
+}
+
+static int32_t SC_loadI32(uint32_t bank, uint32_t offset)
+{
+ GET_TLS();
+ const void *vp = sc->mSlots[bank]->getPtr();
+ const int32_t *i = static_cast<const int32_t *>(vp);
+ //LOGE("loadI32 %i %i = %i", bank, offset, t);
+ return i[offset];
+}
+
+static uint32_t SC_loadU32(uint32_t bank, uint32_t offset)
+{
+ GET_TLS();
+ const void *vp = sc->mSlots[bank]->getPtr();
+ const uint32_t *i = static_cast<const uint32_t *>(vp);
+ return i[offset];
+}
+
+static void SC_loadVec4(uint32_t bank, uint32_t offset, rsc_Vector4 *v)
+{
+ GET_TLS();
+ const void *vp = sc->mSlots[bank]->getPtr();
+ const float *f = static_cast<const float *>(vp);
+ memcpy(v, &f[offset], sizeof(rsc_Vector4));
+}
+
+static void SC_loadMatrix(uint32_t bank, uint32_t offset, rsc_Matrix *m)
+{
+ GET_TLS();
+ const void *vp = sc->mSlots[bank]->getPtr();
+ const float *f = static_cast<const float *>(vp);
+ memcpy(m, &f[offset], sizeof(rsc_Matrix));
+}
+
+
+static void SC_storeF(uint32_t bank, uint32_t offset, float v)
+{
+ //LOGE("storeF %i %i %f", bank, offset, v);
+ GET_TLS();
+ void *vp = sc->mSlots[bank]->getPtr();
+ float *f = static_cast<float *>(vp);
+ f[offset] = v;
+}
+
+static void SC_storeI32(uint32_t bank, uint32_t offset, int32_t v)
+{
+ GET_TLS();
+ void *vp = sc->mSlots[bank]->getPtr();
+ int32_t *f = static_cast<int32_t *>(vp);
+ static_cast<int32_t *>(sc->mSlots[bank]->getPtr())[offset] = v;
+}
+
+static void SC_storeU32(uint32_t bank, uint32_t offset, uint32_t v)
+{
+ GET_TLS();
+ void *vp = sc->mSlots[bank]->getPtr();
+ uint32_t *f = static_cast<uint32_t *>(vp);
+ static_cast<uint32_t *>(sc->mSlots[bank]->getPtr())[offset] = v;
+}
+
+static void SC_storeVec4(uint32_t bank, uint32_t offset, const rsc_Vector4 *v)
+{
+ GET_TLS();
+ void *vp = sc->mSlots[bank]->getPtr();
+ float *f = static_cast<float *>(vp);
+ memcpy(&f[offset], v, sizeof(rsc_Vector4));
+}
+
+static void SC_storeMatrix(uint32_t bank, uint32_t offset, const rsc_Matrix *m)
+{
+ GET_TLS();
+ void *vp = sc->mSlots[bank]->getPtr();
+ float *f = static_cast<float *>(vp);
+ memcpy(&f[offset], m, sizeof(rsc_Matrix));
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Math routines
+//////////////////////////////////////////////////////////////////////////////
+
+static float SC_randf(float max)
+{
+ float r = (float)rand();
+ return r / RAND_MAX * max;
+}
+
+
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Matrix routines
+//////////////////////////////////////////////////////////////////////////////
+
+
+static void SC_matrixLoadIdentity(rsc_Matrix *mat)
+{
+ Matrix *m = reinterpret_cast<Matrix *>(mat);
+ m->loadIdentity();
+}
+
+static void SC_matrixLoadFloat(rsc_Matrix *mat, const float *f)
+{
+ Matrix *m = reinterpret_cast<Matrix *>(mat);
+ m->load(f);
+}
+
+static void SC_matrixLoadMat(rsc_Matrix *mat, const rsc_Matrix *newmat)
+{
+ Matrix *m = reinterpret_cast<Matrix *>(mat);
+ m->load(reinterpret_cast<const Matrix *>(newmat));
+}
+
+static void SC_matrixLoadRotate(rsc_Matrix *mat, float rot, float x, float y, float z)
+{
+ Matrix *m = reinterpret_cast<Matrix *>(mat);
+ m->loadRotate(rot, x, y, z);
+}
+
+static void SC_matrixLoadScale(rsc_Matrix *mat, float x, float y, float z)
+{
+ Matrix *m = reinterpret_cast<Matrix *>(mat);
+ m->loadScale(x, y, z);
+}
+
+static void SC_matrixLoadTranslate(rsc_Matrix *mat, float x, float y, float z)
+{
+ Matrix *m = reinterpret_cast<Matrix *>(mat);
+ m->loadTranslate(x, y, z);
+}
+
+static void SC_matrixLoadMultiply(rsc_Matrix *mat, const rsc_Matrix *lhs, const rsc_Matrix *rhs)
+{
+ Matrix *m = reinterpret_cast<Matrix *>(mat);
+ m->loadMultiply(reinterpret_cast<const Matrix *>(lhs),
+ reinterpret_cast<const Matrix *>(rhs));
+}
+
+static void SC_matrixMultiply(rsc_Matrix *mat, const rsc_Matrix *rhs)
+{
+ Matrix *m = reinterpret_cast<Matrix *>(mat);
+ m->multiply(reinterpret_cast<const Matrix *>(rhs));
+}
+
+static void SC_matrixRotate(rsc_Matrix *mat, float rot, float x, float y, float z)
+{
+ Matrix *m = reinterpret_cast<Matrix *>(mat);
+ m->rotate(rot, x, y, z);
+}
+
+static void SC_matrixScale(rsc_Matrix *mat, float x, float y, float z)
+{
+ Matrix *m = reinterpret_cast<Matrix *>(mat);
+ m->scale(x, y, z);
+}
+
+static void SC_matrixTranslate(rsc_Matrix *mat, float x, float y, float z)
+{
+ Matrix *m = reinterpret_cast<Matrix *>(mat);
+ m->translate(x, y, z);
+}
+
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Context
+//////////////////////////////////////////////////////////////////////////////
+
+static void SC_bindTexture(RsProgramFragment vpf, uint32_t slot, RsAllocation va)
+{
+ GET_TLS();
+ rsi_ProgramFragmentBindTexture(rsc,
+ static_cast<ProgramFragment *>(vpf),
+ slot,
+ static_cast<Allocation *>(va));
+
+}
+
+static void SC_bindSampler(RsProgramFragment vpf, uint32_t slot, RsSampler vs)
+{
+ GET_TLS();
+ rsi_ProgramFragmentBindSampler(rsc,
+ static_cast<ProgramFragment *>(vpf),
+ slot,
+ static_cast<Sampler *>(vs));
+
+}
+
+static void SC_bindProgramFragmentStore(RsProgramFragmentStore pfs)
+{
+ GET_TLS();
+ rsi_ContextBindProgramFragmentStore(rsc, pfs);
+
+}
+
+static void SC_bindProgramFragment(RsProgramFragment pf)
+{
+ GET_TLS();
+ rsi_ContextBindProgramFragment(rsc, pf);
+
+}
+
+static void SC_bindProgramVertex(RsProgramVertex pv)
+{
+ GET_TLS();
+ rsi_ContextBindProgramVertex(rsc, pv);
+
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Drawing
+//////////////////////////////////////////////////////////////////////////////
+
+static void SC_drawTriangleMesh(RsTriangleMesh mesh)
+{
+ GET_TLS();
+ rsi_TriangleMeshRender(rsc, mesh);
+}
+
+static void SC_drawTriangleMeshRange(RsTriangleMesh mesh, uint32_t start, uint32_t count)
+{
+ GET_TLS();
+ rsi_TriangleMeshRenderRange(rsc, mesh, start, count);
+}
+
+// Assumes (GL_FIXED) x,y,z (GL_UNSIGNED_BYTE)r,g,b,a
+static void SC_drawTriangleArray(int ialloc, uint32_t count)
+{
+ GET_TLS();
+ RsAllocation alloc = (RsAllocation)ialloc;
+
+ const Allocation *a = (const Allocation *)alloc;
+ const uint32_t *ptr = (const uint32_t *)a->getPtr();
+
+ rsc->setupCheck();
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tm->mBufferObjects[1]);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnableClientState(GL_COLOR_ARRAY);
+
+ glVertexPointer(2, GL_FIXED, 12, ptr + 1);
+ //glTexCoordPointer(2, GL_FIXED, 24, ptr + 1);
+ glColorPointer(4, GL_UNSIGNED_BYTE, 12, ptr);
+
+ glDrawArrays(GL_TRIANGLES, 0, count * 3);
+}
+
+static void SC_drawQuad(float x1, float y1, float z1,
+ float x2, float y2, float z2,
+ float x3, float y3, float z3,
+ float x4, float y4, float z4)
+{
+ GET_TLS();
+
+ //LOGE("Quad");
+ //LOGE("%4.2f, %4.2f, %4.2f", x1, y1, z1);
+ //LOGE("%4.2f, %4.2f, %4.2f", x2, y2, z2);
+ //LOGE("%4.2f, %4.2f, %4.2f", x3, y3, z3);
+ //LOGE("%4.2f, %4.2f, %4.2f", x4, y4, z4);
+
+ float vtx[] = {x1,y1,z1, x2,y2,z2, x3,y3,z3, x4,y4,z4};
+ static const float tex[] = {0,1, 1,1, 1,0, 0,0};
+
+
+ rsc->setupCheck();
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tm->mBufferObjects[1]);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer(3, GL_FLOAT, 0, vtx);
+
+ glClientActiveTexture(GL_TEXTURE0);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glTexCoordPointer(2, GL_FLOAT, 0, tex);
+ glClientActiveTexture(GL_TEXTURE1);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glTexCoordPointer(2, GL_FLOAT, 0, tex);
+ glClientActiveTexture(GL_TEXTURE0);
+
+ glDisableClientState(GL_NORMAL_ARRAY);
+ glDisableClientState(GL_COLOR_ARRAY);
+
+ //glColorPointer(4, GL_UNSIGNED_BYTE, 12, ptr);
+
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+//////////////////////////////////////////////////////////////////////////////
+
+extern "C" const void * loadVp(uint32_t bank, uint32_t offset)
+{
+ GET_TLS();
+ return &static_cast<const uint8_t *>(sc->mSlots[bank]->getPtr())[offset];
+}
+
+
+
+static void SC_color(float r, float g, float b, float a)
+{
+ glColor4f(r, g, b, a);
+}
+
+
+extern "C" void materialDiffuse(float r, float g, float b, float a)
+{
+ float v[] = {r, g, b, a};
+ glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, v);
+}
+
+extern "C" void materialSpecular(float r, float g, float b, float a)
+{
+ float v[] = {r, g, b, a};
+ glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, v);
+}
+
+extern "C" void lightPosition(float x, float y, float z, float w)
+{
+ float v[] = {x, y, z, w};
+ glLightfv(GL_LIGHT0, GL_POSITION, v);
+}
+
+extern "C" void materialShininess(float s)
+{
+ glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, &s);
+}
+
+extern "C" void uploadToTexture(RsAllocation va, uint32_t baseMipLevel)
+{
+ GET_TLS();
+ rsi_AllocationUploadToTexture(rsc, va, baseMipLevel);
+}
+
+extern "C" void enable(uint32_t p)
+{
+ glEnable(p);
+}
+
+extern "C" void disable(uint32_t p)
+{
+ glDisable(p);
+}
+
+
+
+static void SC_ClearColor(float r, float g, float b, float a)
+{
+ //LOGE("c %f %f %f %f", r, g, b, a);
+ GET_TLS();
+ sc->mEnviroment.mClearColor[0] = r;
+ sc->mEnviroment.mClearColor[1] = g;
+ sc->mEnviroment.mClearColor[2] = b;
+ sc->mEnviroment.mClearColor[3] = a;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Class implementation
+//////////////////////////////////////////////////////////////////////////////
+
+ScriptCState::SymbolTable_t ScriptCState::gSyms[] = {
+ // IO
+ { "loadI32", (void *)&SC_loadI32,
+ "int", "(int, int)" },
+ //{ "loadU32", (void *)&SC_loadU32, "unsigned int", "(int, int)" },
+ { "loadF", (void *)&SC_loadF,
+ "float", "(int, int)" },
+ { "loadVec4", (void *)&SC_loadVec4,
+ "void", "(int, int, float *)" },
+ { "loadMatrix", (void *)&SC_loadMatrix,
+ "void", "(int, int, float *)" },
+ { "storeI32", (void *)&SC_storeI32,
+ "void", "(int, int, int)" },
+ //{ "storeU32", (void *)&SC_storeU32, "void", "(int, int, unsigned int)" },
+ { "storeF", (void *)&SC_storeF,
+ "void", "(int, int, float)" },
+ { "storeVec4", (void *)&SC_storeVec4,
+ "void", "(int, int, float *)" },
+ { "storeMatrix", (void *)&SC_storeMatrix,
+ "void", "(int, int, float *)" },
+
+ // math
+ { "sinf", (void *)&sinf,
+ "float", "(float)" },
+ { "cosf", (void *)&cosf,
+ "float", "(float)" },
+ { "fabs", (void *)&fabs,
+ "float", "(float)" },
+ { "randf", (void *)&SC_randf,
+ "float", "(float)" },
+
+ // matrix
+ { "matrixLoadIdentity", (void *)&SC_matrixLoadIdentity,
+ "void", "(float *mat)" },
+ { "matrixLoadFloat", (void *)&SC_matrixLoadFloat,
+ "void", "(float *mat, float *f)" },
+ { "matrixLoadMat", (void *)&SC_matrixLoadMat,
+ "void", "(float *mat, float *newmat)" },
+ { "matrixLoadRotate", (void *)&SC_matrixLoadRotate,
+ "void", "(float *mat, float rot, float x, float y, float z)" },
+ { "matrixLoadScale", (void *)&SC_matrixLoadScale,
+ "void", "(float *mat, float x, float y, float z)" },
+ { "matrixLoadTranslate", (void *)&SC_matrixLoadTranslate,
+ "void", "(float *mat, float x, float y, float z)" },
+ { "matrixLoadMultiply", (void *)&SC_matrixLoadMultiply,
+ "void", "(float *mat, float *lhs, float *rhs)" },
+ { "matrixMultiply", (void *)&SC_matrixMultiply,
+ "void", "(float *mat, float *rhs)" },
+ { "matrixRotate", (void *)&SC_matrixRotate,
+ "void", "(float *mat, float rot, float x, float y, float z)" },
+ { "matrixScale", (void *)&SC_matrixScale,
+ "void", "(float *mat, float x, float y, float z)" },
+ { "matrixTranslate", (void *)&SC_matrixTranslate,
+ "void", "(float *mat, float x, float y, float z)" },
+
+ // context
+ { "bindProgramFragment", (void *)&SC_bindProgramFragment,
+ "void", "(int)" },
+ { "bindProgramFragmentStore", (void *)&SC_bindProgramFragmentStore,
+ "void", "(int)" },
+ { "bindProgramVertex", (void *)&SC_bindProgramVertex,
+ "void", "(int)" },
+ { "bindSampler", (void *)&SC_bindSampler,
+ "void", "(int, int, int)" },
+ { "bindTexture", (void *)&SC_bindTexture,
+ "void", "(int, int, int)" },
+
+ // drawing
+ { "drawQuad", (void *)&SC_drawQuad,
+ "void", "(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4)" },
+ { "drawTriangleArray", (void *)&SC_drawTriangleArray,
+ "void", "(int ialloc, int count)" },
+ { "drawTriangleMesh", (void *)&SC_drawTriangleMesh,
+ "void", "(int mesh)" },
+ { "drawTriangleMeshRange", (void *)&SC_drawTriangleMeshRange,
+ "void", "(int mesh, int start, int count)" },
+
+
+ // misc
+ { "pfClearColor", (void *)&SC_ClearColor,
+ "void", "(float, float, float, float)" },
+
+ { "color", (void *)&SC_color,
+ "void", "(float, float, float, float)" },
+
+ { NULL, NULL, NULL, NULL }
+};
+
+const ScriptCState::SymbolTable_t * ScriptCState::lookupSymbol(const char *sym)
+{
+ ScriptCState::SymbolTable_t *syms = gSyms;
+
+ while (syms->mPtr) {
+ if (!strcmp(syms->mName, sym)) {
+ return syms;
+ }
+ syms++;
+ }
+ return NULL;
+}
+
+void ScriptCState::appendDecls(String8 *str)
+{
+ ScriptCState::SymbolTable_t *syms = gSyms;
+ while (syms->mPtr) {
+ str->append(syms->mRet);
+ str->append(" ");
+ str->append(syms->mName);
+ str->append(syms->mParam);
+ str->append(";\n");
+ syms++;
+ }
+}
+
+
diff --git a/libs/rs/rsUtils.h b/libs/rs/rsUtils.h
index 3d3437b298ca..b13e88fdbe91 100644
--- a/libs/rs/rsUtils.h
+++ b/libs/rs/rsUtils.h
@@ -24,6 +24,9 @@
#include <stdlib.h>
#include <pthread.h>
+#include <EGL/egl.h>
+#include <math.h>
+
#include "RenderScript.h"
namespace android {
diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp
index 13c30a795cb2..59c9476287f0 100644
--- a/libs/ui/EventHub.cpp
+++ b/libs/ui/EventHub.cpp
@@ -22,7 +22,6 @@
#include <utils/Log.h>
#include <utils/Timers.h>
#include <utils/threads.h>
-#include <utils/List.h>
#include <utils/Errors.h>
#include <stdlib.h>
@@ -84,7 +83,7 @@ EventHub::EventHub(void)
: mError(NO_INIT), mHaveFirstKeyboard(false), mFirstKeyboardId(0)
, mDevicesById(0), mNumDevicesById(0)
, mOpeningDevices(0), mClosingDevices(0)
- , mDevices(0), mFDs(0), mFDCount(0)
+ , mDevices(0), mFDs(0), mFDCount(0), mOpened(false)
{
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
#ifdef EV_SW
@@ -101,11 +100,6 @@ EventHub::~EventHub(void)
// we should free stuff here...
}
-void EventHub::onFirstRef()
-{
- mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR;
-}
-
status_t EventHub::errorCheck() const
{
return mError;
@@ -240,6 +234,41 @@ int EventHub::getKeycodeState(int32_t deviceId, int code) const
return 0;
}
+status_t EventHub::scancodeToKeycode(int32_t deviceId, int scancode,
+ int32_t* outKeycode, uint32_t* outFlags) const
+{
+ AutoMutex _l(mLock);
+ device_t* device = getDevice(deviceId);
+
+ if (device != NULL && device->layoutMap != NULL) {
+ status_t err = device->layoutMap->map(scancode, outKeycode, outFlags);
+ if (err == NO_ERROR) {
+ return NO_ERROR;
+ }
+ }
+
+ if (mHaveFirstKeyboard) {
+ device = getDevice(mFirstKeyboardId);
+
+ if (device != NULL && device->layoutMap != NULL) {
+ status_t err = device->layoutMap->map(scancode, outKeycode, outFlags);
+ if (err == NO_ERROR) {
+ return NO_ERROR;
+ }
+ }
+ }
+
+ *outKeycode = 0;
+ *outFlags = 0;
+ return NAME_NOT_FOUND;
+}
+
+void EventHub::addExcludedDevice(const char* deviceName)
+{
+ String8 name(deviceName);
+ mExcludedDevices.push_back(name);
+}
+
EventHub::device_t* EventHub::getDevice(int32_t deviceId) const
{
if (deviceId == 0) deviceId = mFirstKeyboardId;
@@ -277,7 +306,12 @@ bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
// Note that we only allow one caller to getEvent(), so don't need
// to do locking here... only when adding/removing devices.
-
+
+ if (!mOpened) {
+ mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR;
+ mOpened = true;
+ }
+
while(1) {
// First, report any devices that had last been added/removed.
@@ -475,6 +509,20 @@ int EventHub::open_device(const char *deviceName)
//fprintf(stderr, "could not get device name for %s, %s\n", deviceName, strerror(errno));
name[0] = '\0';
}
+
+ // check to see if the device is on our excluded list
+ List<String8>::iterator iter = mExcludedDevices.begin();
+ List<String8>::iterator end = mExcludedDevices.end();
+ for ( ; iter != end; iter++) {
+ const char* test = *iter;
+ if (strcmp(name, test) == 0) {
+ LOGI("ignoring event id %s driver %s\n", deviceName, test);
+ close(fd);
+ fd = -1;
+ return -1;
+ }
+ }
+
if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) {
//fprintf(stderr, "could not get location for %s, %s\n", deviceName, strerror(errno));
location[0] = '\0';
@@ -734,6 +782,7 @@ int EventHub::read_notify(int nfd)
int event_pos = 0;
struct inotify_event *event;
+LOGD("EventHub::read_notify nfd: %d\n", nfd);
res = read(nfd, event_buf, sizeof(event_buf));
if(res < (int)sizeof(*event)) {
if(errno == EINTR)
diff --git a/libs/ui/Overlay.cpp b/libs/ui/Overlay.cpp
index a092f8dd54e0..4854d6a2a26f 100644
--- a/libs/ui/Overlay.cpp
+++ b/libs/ui/Overlay.cpp
@@ -59,6 +59,18 @@ status_t Overlay::queueBuffer(overlay_buffer_t buffer)
return mOverlayData->queueBuffer(mOverlayData, buffer);
}
+status_t Overlay::setCrop(uint32_t x, uint32_t y, uint32_t w, uint32_t h)
+{
+ if (mStatus != NO_ERROR) return mStatus;
+ return mOverlayData->setCrop(mOverlayData, x, y, w, h);
+}
+
+status_t Overlay::getCrop(uint32_t* x, uint32_t* y, uint32_t* w, uint32_t* h)
+{
+ if (mStatus != NO_ERROR) return mStatus;
+ return mOverlayData->getCrop(mOverlayData, x, y, w, h);
+}
+
int32_t Overlay::getBufferCount() const
{
if (mStatus != NO_ERROR) return mStatus;
@@ -73,6 +85,15 @@ void* Overlay::getBufferAddress(overlay_buffer_t buffer)
void Overlay::destroy() {
if (mStatus != NO_ERROR) return;
+
+ // Must delete the objects in reverse creation order, thus the
+ // data side must be closed first and then the destroy send to
+ // the control side.
+ if (mOverlayData) {
+ overlay_data_close(mOverlayData);
+ mOverlayData = NULL;
+ }
+
mOverlayRef->mOverlayChannel->destroy();
}
diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp
index cce754a08213..be04777528ea 100644
--- a/libs/utils/BackupData.cpp
+++ b/libs/utils/BackupData.cpp
@@ -298,10 +298,12 @@ BackupDataReader::SkipEntityData()
}
if (m_header.entity.dataSize > 0) {
int pos = lseek(m_fd, m_dataEndPos, SEEK_SET);
- return pos == -1 ? (int)errno : (int)NO_ERROR;
- } else {
- return NO_ERROR;
+ if (pos == -1) {
+ return errno;
+ }
}
+ SKIP_PADDING();
+ return NO_ERROR;
}
ssize_t
diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp
index 87edb0125e73..98d450b76edc 100644
--- a/libs/utils/ResourceTypes.cpp
+++ b/libs/utils/ResourceTypes.cpp
@@ -4007,7 +4007,16 @@ void ResTable::print(bool inclValues) const
printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type);
continue;
}
- printf(" config %d lang=%c%c cnt=%c%c orien=%d touch=%d density=%d key=%d infl=%d nav=%d w=%d h=%d lyt=%d\n",
+ char density[16];
+ uint16_t dval = dtohs(type->config.density);
+ if (dval == ResTable_config::DENSITY_DEFAULT) {
+ strcpy(density, "def");
+ } else if (dval == ResTable_config::DENSITY_NONE) {
+ strcpy(density, "non");
+ } else {
+ sprintf(density, "%d", (int)dval);
+ }
+ printf(" config %d lang=%c%c cnt=%c%c orien=%d touch=%d density=%s key=%d infl=%d nav=%d w=%d h=%d lyt=%d\n",
(int)configIndex,
type->config.language[0] ? type->config.language[0] : '-',
type->config.language[1] ? type->config.language[1] : '-',
@@ -4015,7 +4024,7 @@ void ResTable::print(bool inclValues) const
type->config.country[1] ? type->config.country[1] : '-',
type->config.orientation,
type->config.touchscreen,
- dtohs(type->config.density),
+ density,
type->config.keyboard,
type->config.inputFlags,
type->config.navigation,
diff --git a/libs/utils/ZipUtils.cpp b/libs/utils/ZipUtils.cpp
index 5df94cbbd9a0..9138878ff776 100644
--- a/libs/utils/ZipUtils.cpp
+++ b/libs/utils/ZipUtils.cpp
@@ -210,7 +210,7 @@ bail:
LOGV("+++ reading %ld bytes (%ld left)\n",
getSize, compRemaining);
- int cc = fread(readBuf, getSize, 1, fp);
+ int cc = fread(readBuf, 1, getSize, fp);
if (cc != (int) getSize) {
LOGD("inflate read failed (%d vs %ld)\n",
cc, getSize);
@@ -341,4 +341,3 @@ bail:
return true;
}
-
diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java
index 3f0c2347d4d3..9e1a72c1a8af 100755
--- a/location/java/com/android/internal/location/GpsLocationProvider.java
+++ b/location/java/com/android/internal/location/GpsLocationProvider.java
@@ -184,8 +184,6 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
// number of fixes we have received since we started navigating
private int mFixCount;
- private boolean mAgpsConfigured;
-
// true if we started navigation
private boolean mStarted;
@@ -356,7 +354,6 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
try {
int port = Integer.parseInt(portString);
native_set_agps_server(AGPS_TYPE_SUPL, host, port);
- mAgpsConfigured = true;
} catch (NumberFormatException e) {
Log.e(TAG, "unable to parse SUPL_PORT: " + portString);
}
@@ -368,7 +365,6 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
try {
int port = Integer.parseInt(portString);
native_set_agps_server(AGPS_TYPE_C2K, host, port);
- mAgpsConfigured = true;
} catch (NumberFormatException e) {
Log.e(TAG, "unable to parse C2K_PORT: " + portString);
}
@@ -722,7 +718,7 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
if (DEBUG) Log.d(TAG, "startNavigating");
mStarted = true;
int positionMode;
- if (mAgpsConfigured && Settings.Secure.getInt(mContext.getContentResolver(),
+ if (Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ASSISTED_GPS_ENABLED, 0) != 0) {
positionMode = GPS_POSITION_MODE_MS_BASED;
} else {
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index ee41021d0a05..58c04f3a1f74 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -1327,10 +1327,12 @@ public class AudioService extends IAudioService.Stub {
}
private void persistVolume(VolumeStreamState streamState) {
- System.putInt(mContentResolver, streamState.mVolumeIndexSettingName,
- streamState.mIndex);
- System.putInt(mContentResolver, streamState.mLastAudibleVolumeIndexSettingName,
- streamState.mLastAudibleIndex);
+ if (streamState.mStreamType != AudioManager.STREAM_BLUETOOTH_SCO) {
+ System.putInt(mContentResolver, streamState.mVolumeIndexSettingName,
+ streamState.mIndex);
+ System.putInt(mContentResolver, streamState.mLastAudibleVolumeIndexSettingName,
+ streamState.mLastAudibleIndex);
+ }
}
private void persistRingerMode() {
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 84c1a92b51ca..d5801f7b3cdf 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -663,7 +663,7 @@ public class MediaScanner
ContentValues values = toValues();
String title = values.getAsString(MediaStore.MediaColumns.TITLE);
- if (TextUtils.isEmpty(title)) {
+ if (title == null || TextUtils.isEmpty(title.trim())) {
title = values.getAsString(MediaStore.MediaColumns.DATA);
// extract file name after last slash
int lastSlash = title.lastIndexOf('/');
diff --git a/media/java/android/media/Metadata.java b/media/java/android/media/Metadata.java
index 6525109945ff..70e89a2c82a9 100644
--- a/media/java/android/media/Metadata.java
+++ b/media/java/android/media/Metadata.java
@@ -20,10 +20,12 @@ import android.graphics.Bitmap;
import android.os.Parcel;
import android.util.Log;
+import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
-import java.util.Iterator;
+import java.util.HashMap;
import java.util.Set;
+import java.util.TimeZone;
/**
@@ -54,7 +56,7 @@ public class Metadata
//
// We manually serialize the data in Parcels. For large memory
// blob (bitmaps, raw pictures) we use MemoryFile which allow the
- // client to make the data purgeable once it is done with it.
+ // client to make the data purge-able once it is done with it.
//
public static final int ANY = 0; // Never used for metadata returned, only for filtering.
@@ -95,110 +97,322 @@ public class Metadata
public static final int VIDEO_WIDTH = 26; // Integer
public static final int NUM_TRACKS = 27; // Integer
public static final int DRM_CRIPPLED = 28; // Boolean
- public static final int LAST_SYSTEM = 29;
- public static final int FIRST_CUSTOM = 8092;
+
+ // Playback capabilities.
+ public static final int PAUSE_AVAILABLE = 29; // Boolean
+ public static final int SEEK_BACKWARD_AVAILABLE = 30; // Boolean
+ public static final int SEEK_FORWARD_AVAILABLE = 31; // Boolean
+
+ private static final int LAST_SYSTEM = 31;
+ private static final int FIRST_CUSTOM = 8092;
// Shorthands to set the MediaPlayer's metadata filter.
public static final Set<Integer> MATCH_NONE = Collections.EMPTY_SET;
public static final Set<Integer> MATCH_ALL = Collections.singleton(ANY);
- private static final int STRING_VAL = 1;
- private static final int INTEGER_VAL = 2;
- private static final int LONG_VAL = 3;
- private static final int DOUBLE_VAL = 4;
- private static final int TIMED_TEXT_VAL = 2;
+ public static final int STRING_VAL = 1;
+ public static final int INTEGER_VAL = 2;
+ public static final int BOOLEAN_VAL = 3;
+ public static final int LONG_VAL = 4;
+ public static final int DOUBLE_VAL = 5;
+ public static final int TIMED_TEXT_VAL = 6;
+ public static final int DATE_VAL = 7;
+ public static final int BYTE_ARRAY_VAL = 8;
+ // FIXME: misses a type for shared heap is missing (MemoryFile).
+ // FIXME: misses a type for bitmaps.
+ private static final int LAST_TYPE = 8;
+
+ private static final String TAG = "media.Metadata";
+ private static final int kInt32Size = 4;
+ private static final int kMetaHeaderSize = 2 * kInt32Size; // size + marker
+ private static final int kRecordHeaderSize = 3 * kInt32Size; // size + id + type
+
+ private static final int kMetaMarker = 0x4d455441; // 'M' 'E' 'T' 'A'
+
+ // After a successful parsing, set the parcel with the serialized metadata.
+ private Parcel mParcel;
+
+ // Map to associate a Metadata key (e.g TITLE) with the offset of
+ // the record's payload in the parcel.
+ // Used to look up if a key was present too.
+ // Key: Metadata ID
+ // Value: Offset of the metadata type field in the record.
+ private final HashMap<Integer, Integer> mKeyToPosMap =
+ new HashMap<Integer, Integer>();
/**
- * Helper class to hold a pair (time, text). Can be used to implement caption.
+ * Helper class to hold a triple (time, duration, text). Can be used to
+ * implement caption.
*/
public class TimedText {
private Date mTime;
+ private int mDuration; // millisec
private String mText;
- public TimedText(final Date time, final String text) {
+
+ public TimedText(Date time, int duration, String text) {
mTime = time;
+ mDuration = duration;
mText = text;
}
+
public String toString() {
StringBuilder res = new StringBuilder(80);
- res.append(mTime).append(":").append(mText);
+ res.append(mTime).append("-").append(mDuration)
+ .append(":").append(mText);
return res.toString();
}
}
- /* package */ Metadata() {}
+ public Metadata() { }
- /* package */ boolean parse(Parcel data) {
- // FIXME: Implement.
- return true;
+ /**
+ * Go over all the records, collecting metadata keys and records'
+ * type field offset in the Parcel. These are stored in
+ * mKeyToPosMap for latter retrieval.
+ * Format of a metadata record:
+ <pre>
+ 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | record size |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | metadata key | // TITLE
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | metadata type | // STRING_VAL
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ | .... metadata payload .... |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ </pre>
+ * @param parcel With the serialized records.
+ * @param bytesLeft How many bytes in the parcel should be processed.
+ * @return false if an error occurred during parsing.
+ */
+ private boolean scanAllRecords(Parcel parcel, int bytesLeft) {
+ int recCount = 0;
+ boolean error = false;
+
+ mKeyToPosMap.clear();
+ while (bytesLeft > kRecordHeaderSize) {
+ final int start = parcel.dataPosition();
+ // Check the size.
+ final int size = parcel.readInt();
+
+ if (size <= kRecordHeaderSize) { // at least 1 byte should be present.
+ Log.e(TAG, "Record is too short");
+ error = true;
+ break;
+ }
+
+ // Check the metadata key.
+ final int metadataId = parcel.readInt();
+ if (!checkMetadataId(metadataId)) {
+ error = true;
+ break;
+ }
+
+ // Store the record offset which points to the type
+ // field so we can later on read/unmarshall the record
+ // payload.
+ if (mKeyToPosMap.containsKey(metadataId)) {
+ Log.e(TAG, "Duplicate metadata ID found");
+ error = true;
+ break;
+ }
+
+ mKeyToPosMap.put(metadataId, parcel.dataPosition());
+
+ // Check the metadata type.
+ final int metadataType = parcel.readInt();
+ if (metadataType <= 0 || metadataType > LAST_TYPE) {
+ Log.e(TAG, "Invalid metadata type " + metadataType);
+ error = true;
+ break;
+ }
+
+ // Skip to the next one.
+ parcel.setDataPosition(start + size);
+ bytesLeft -= size;
+ ++recCount;
+ }
+
+ if (0 != bytesLeft || error) {
+ Log.e(TAG, "Ran out of data or error on record " + recCount);
+ mKeyToPosMap.clear();
+ return false;
+ } else {
+ return true;
+ }
}
/**
- * @return the number of element in this metadata set.
+ * Check a parcel containing metadata is well formed. The header
+ * is checked as well as the individual records format. However, the
+ * data inside the record is not checked because we do lazy access
+ * (we check/unmarshall only data the user asks for.)
+ *
+ * Format of a metadata parcel:
+ <pre>
+ 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | metadata total size |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | 'M' | 'E' | 'T' | 'A' |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ | .... metadata records .... |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ </pre>
+ *
+ * @param parcel With the serialized data. Metadata keeps a
+ * reference on it to access it later on. The caller
+ * should not modify the parcel after this call (and
+ * not call recycle on it.)
+ * @return false if an error occurred.
*/
- public int size() {
- // FIXME: Implement.
- return 0;
+ public boolean parse(Parcel parcel) {
+ if (parcel.dataAvail() < kMetaHeaderSize) {
+ Log.e(TAG, "Not enough data " + parcel.dataAvail());
+ return false;
+ }
+
+ final int pin = parcel.dataPosition(); // to roll back in case of errors.
+ final int size = parcel.readInt();
+
+ // The extra kInt32Size below is to account for the int32 'size' just read.
+ if (parcel.dataAvail() + kInt32Size < size || size < kMetaHeaderSize) {
+ Log.e(TAG, "Bad size " + size + " avail " + parcel.dataAvail() + " position " + pin);
+ parcel.setDataPosition(pin);
+ return false;
+ }
+
+ // Checks if the 'M' 'E' 'T' 'A' marker is present.
+ final int kShouldBeMetaMarker = parcel.readInt();
+ if (kShouldBeMetaMarker != kMetaMarker ) {
+ Log.e(TAG, "Marker missing " + Integer.toHexString(kShouldBeMetaMarker));
+ parcel.setDataPosition(pin);
+ return false;
+ }
+
+ // Scan the records to collect metadata ids and offsets.
+ if (!scanAllRecords(parcel, size - kMetaHeaderSize)) {
+ parcel.setDataPosition(pin);
+ return false;
+ }
+ mParcel = parcel;
+ return true;
}
/**
- * @return an iterator over the keys.
+ * @return The set of metadata ID found.
*/
- public Iterator<Integer> iterator() {
- // FIXME: Implement.
- return new java.util.HashSet<Integer>().iterator();
+ public Set<Integer> keySet() {
+ return mKeyToPosMap.keySet();
}
/**
* @return true if a value is present for the given key.
*/
- public boolean has(final int key) {
- if (key <= ANY) {
- throw new IllegalArgumentException("Invalid key: " + key);
- }
- if (LAST_SYSTEM <= key && key < FIRST_CUSTOM) {
- throw new IllegalArgumentException("Key in reserved range: " + key);
+ public boolean has(final int metadataId) {
+ if (!checkMetadataId(metadataId)) {
+ throw new IllegalArgumentException("Invalid key: " + metadataId);
}
- // FIXME: Implement.
- return true;
+ return mKeyToPosMap.containsKey(metadataId);
}
- // Accessors
+ // Accessors.
+ // Caller must make sure the key is present using the {@code has}
+ // method otherwise a RuntimeException will occur.
+
public String getString(final int key) {
- // FIXME: Implement.
- return new String();
+ checkType(key, STRING_VAL);
+ return mParcel.readString();
}
public int getInt(final int key) {
- // FIXME: Implement.
- return 0;
+ checkType(key, INTEGER_VAL);
+ return mParcel.readInt();
+ }
+
+ public boolean getBoolean(final int key) {
+ checkType(key, BOOLEAN_VAL);
+ return mParcel.readInt() == 1;
}
public long getLong(final int key) {
- // FIXME: Implement.
- return 0;
+ checkType(key, LONG_VAL);
+ return mParcel.readLong();
}
public double getDouble(final int key) {
- // FIXME: Implement.
- return 0.0;
+ checkType(key, DOUBLE_VAL);
+ return mParcel.readDouble();
}
public byte[] getByteArray(final int key) {
- return new byte[0];
- }
-
- public Bitmap getBitmap(final int key) {
- // FIXME: Implement.
- return null;
+ checkType(key, BYTE_ARRAY_VAL);
+ return mParcel.createByteArray();
}
public Date getDate(final int key) {
- // FIXME: Implement.
- return new Date();
+ checkType(key, DATE_VAL);
+ final long timeSinceEpoch = mParcel.readLong();
+ final String timeZone = mParcel.readString();
+
+ if (timeZone.length() == 0) {
+ return new Date(timeSinceEpoch);
+ } else {
+ TimeZone tz = TimeZone.getTimeZone(timeZone);
+ Calendar cal = Calendar.getInstance(tz);
+
+ cal.setTimeInMillis(timeSinceEpoch);
+ return cal.getTime();
+ }
}
public TimedText getTimedText(final int key) {
- // FIXME: Implement.
- return new TimedText(new Date(0), "<missing>");
+ checkType(key, TIMED_TEXT_VAL);
+ final Date startTime = new Date(mParcel.readLong()); // epoch
+ final int duration = mParcel.readInt(); // millisec
+
+ return new TimedText(startTime,
+ duration,
+ mParcel.readString());
+ }
+
+ // @return the last available system metadata id. Ids are
+ // 1-indexed.
+ public static int lastSytemId() { return LAST_SYSTEM; }
+
+ // @return the first available cutom metadata id.
+ public static int firstCustomId() { return FIRST_CUSTOM; }
+
+ // @return the last value of known type. Types are 1-indexed.
+ public static int lastType() { return LAST_TYPE; }
+
+ // Check val is either a system id or a custom one.
+ // @param val Metadata key to test.
+ // @return true if it is in a valid range.
+ private boolean checkMetadataId(final int val) {
+ if (val <= ANY || (LAST_SYSTEM < val && val < FIRST_CUSTOM)) {
+ Log.e(TAG, "Invalid metadata ID " + val);
+ return false;
+ }
+ return true;
+ }
+
+ // Check the type of the data match what is expected.
+ private void checkType(final int key, final int expectedType) {
+ final int pos = mKeyToPosMap.get(key);
+
+ mParcel.setDataPosition(pos);
+
+ final int type = mParcel.readInt();
+ if (type != expectedType) {
+ throw new IllegalStateException("Wrong type " + expectedType + " but got " + type);
+ }
}
}
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index ebbf13fa3bc2..cdaab045b762 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -18,7 +18,8 @@ LOCAL_SRC_FILES:= \
IMediaMetadataRetriever.cpp \
mediametadataretriever.cpp \
ToneGenerator.cpp \
- JetPlayer.cpp
+ JetPlayer.cpp \
+ IOMX.cpp
LOCAL_SHARED_LIBRARIES := \
libui libcutils libutils libbinder libsonivox
@@ -34,6 +35,7 @@ LOCAL_SHARED_LIBRARIES += libdl
endif
LOCAL_C_INCLUDES := \
- $(call include-path-for, graphics corecg)
+ $(call include-path-for, graphics corecg) \
+ $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index af7dae5f502f..7b9eda76d820 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -891,7 +891,7 @@ void AudioTrack::AudioTrackThread::onFirstRef()
// =========================================================================
audio_track_cblk_t::audio_track_cblk_t()
- : user(0), server(0), userBase(0), serverBase(0), buffers(0), frameCount(0),
+ : lock(Mutex::SHARED), user(0), server(0), userBase(0), serverBase(0), buffers(0), frameCount(0),
loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), volumeLR(0), flowControlFlag(1), forceReady(0)
{
}
diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp
index 0f64259012f0..8d2c36074ab7 100644
--- a/media/libmedia/IMediaPlayerService.cpp
+++ b/media/libmedia/IMediaPlayerService.cpp
@@ -17,12 +17,14 @@
#include <stdint.h>
#include <sys/types.h>
-#include <binder/Parcel.h>
+#include <binder/Parcel.h>
#include <binder/IMemory.h>
-#include <utils/Errors.h> // for status_t
#include <media/IMediaPlayerService.h>
#include <media/IMediaRecorder.h>
+#include <media/IOMX.h>
+
+#include <utils/Errors.h> // for status_t
namespace android {
@@ -33,6 +35,7 @@ enum {
DECODE_FD,
CREATE_MEDIA_RECORDER,
CREATE_METADATA_RETRIEVER,
+ CREATE_OMX,
};
class BpMediaPlayerService: public BpInterface<IMediaPlayerService>
@@ -110,6 +113,13 @@ public:
*pFormat = reply.readInt32();
return interface_cast<IMemory>(reply.readStrongBinder());
}
+
+ virtual sp<IOMX> createOMX() {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
+ remote()->transact(CREATE_OMX, data, &reply);
+ return interface_cast<IOMX>(reply.readStrongBinder());
+ }
};
IMPLEMENT_META_INTERFACE(MediaPlayerService, "android.media.IMediaPlayerService");
@@ -182,6 +192,12 @@ status_t BnMediaPlayerService::onTransact(
reply->writeStrongBinder(retriever->asBinder());
return NO_ERROR;
} break;
+ case CREATE_OMX: {
+ CHECK_INTERFACE(IMediaPlayerService, data, reply);
+ sp<IOMX> omx = createOMX();
+ reply->writeStrongBinder(omx->asBinder());
+ return NO_ERROR;
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
new file mode 100644
index 000000000000..f2a657aa0357
--- /dev/null
+++ b/media/libmedia/IOMX.cpp
@@ -0,0 +1,561 @@
+//#define LOG_NDEBUG 0
+#define LOG_TAG "IOMX"
+#include <utils/Log.h>
+
+#include <binder/IMemory.h>
+#include <binder/Parcel.h>
+#include <media/IOMX.h>
+
+namespace android {
+
+enum {
+ CONNECT = IBinder::FIRST_CALL_TRANSACTION,
+ LIST_NODES,
+ ALLOCATE_NODE,
+ FREE_NODE,
+ SEND_COMMAND,
+ GET_PARAMETER,
+ SET_PARAMETER,
+ USE_BUFFER,
+ ALLOC_BUFFER,
+ ALLOC_BUFFER_WITH_BACKUP,
+ FREE_BUFFER,
+ OBSERVE_NODE,
+ FILL_BUFFER,
+ EMPTY_BUFFER,
+ OBSERVER_ON_MSG,
+};
+
+static void *readVoidStar(const Parcel *parcel) {
+ // FIX if sizeof(void *) != sizeof(int32)
+ return (void *)parcel->readInt32();
+}
+
+static void writeVoidStar(void *x, Parcel *parcel) {
+ // FIX if sizeof(void *) != sizeof(int32)
+ parcel->writeInt32((int32_t)x);
+}
+
+class BpOMX : public BpInterface<IOMX> {
+public:
+ BpOMX(const sp<IBinder> &impl)
+ : BpInterface<IOMX>(impl) {
+ }
+
+#if IOMX_USES_SOCKETS
+ virtual status_t connect(int *sd) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ remote()->transact(CONNECT, data, &reply);
+
+ status_t err = reply.readInt32();
+ if (err == OK) {
+ *sd = dup(reply.readFileDescriptor());
+ } else {
+ *sd = -1;
+ }
+
+ return reply.readInt32();
+ }
+#endif
+
+ virtual status_t list_nodes(List<String8> *list) {
+ list->clear();
+
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ remote()->transact(LIST_NODES, data, &reply);
+
+ int32_t n = reply.readInt32();
+ for (int32_t i = 0; i < n; ++i) {
+ String8 s = reply.readString8();
+
+ list->push_back(s);
+ }
+
+ return OK;
+ }
+
+ virtual status_t allocate_node(const char *name, node_id *node) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ data.writeCString(name);
+ remote()->transact(ALLOCATE_NODE, data, &reply);
+
+ status_t err = reply.readInt32();
+ if (err == OK) {
+ *node = readVoidStar(&reply);
+ } else {
+ *node = 0;
+ }
+
+ return err;
+ }
+
+ virtual status_t free_node(node_id node) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ writeVoidStar(node, &data);
+ remote()->transact(FREE_NODE, data, &reply);
+
+ return reply.readInt32();
+ }
+
+ virtual status_t send_command(
+ node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ writeVoidStar(node, &data);
+ data.writeInt32(cmd);
+ data.writeInt32(param);
+ remote()->transact(SEND_COMMAND, data, &reply);
+
+ return reply.readInt32();
+ }
+
+ virtual status_t get_parameter(
+ node_id node, OMX_INDEXTYPE index,
+ void *params, size_t size) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ writeVoidStar(node, &data);
+ data.writeInt32(index);
+ data.writeInt32(size);
+ data.write(params, size);
+ remote()->transact(GET_PARAMETER, data, &reply);
+
+ status_t err = reply.readInt32();
+ if (err != OK) {
+ return err;
+ }
+
+ reply.read(params, size);
+
+ return OK;
+ }
+
+ virtual status_t set_parameter(
+ node_id node, OMX_INDEXTYPE index,
+ const void *params, size_t size) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ writeVoidStar(node, &data);
+ data.writeInt32(index);
+ data.writeInt32(size);
+ data.write(params, size);
+ remote()->transact(SET_PARAMETER, data, &reply);
+
+ return reply.readInt32();
+ }
+
+ virtual status_t use_buffer(
+ node_id node, OMX_U32 port_index, const sp<IMemory> &params,
+ buffer_id *buffer) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ writeVoidStar(node, &data);
+ data.writeInt32(port_index);
+ data.writeStrongBinder(params->asBinder());
+ remote()->transact(USE_BUFFER, data, &reply);
+
+ status_t err = reply.readInt32();
+ if (err != OK) {
+ *buffer = 0;
+
+ return err;
+ }
+
+ *buffer = readVoidStar(&reply);
+
+ return err;
+ }
+
+ virtual status_t allocate_buffer(
+ node_id node, OMX_U32 port_index, size_t size,
+ buffer_id *buffer) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ writeVoidStar(node, &data);
+ data.writeInt32(port_index);
+ data.writeInt32(size);
+ remote()->transact(ALLOC_BUFFER, data, &reply);
+
+ status_t err = reply.readInt32();
+ if (err != OK) {
+ *buffer = 0;
+
+ return err;
+ }
+
+ *buffer = readVoidStar(&reply);
+
+ return err;
+ }
+
+ virtual status_t allocate_buffer_with_backup(
+ node_id node, OMX_U32 port_index, const sp<IMemory> &params,
+ buffer_id *buffer) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ writeVoidStar(node, &data);
+ data.writeInt32(port_index);
+ data.writeStrongBinder(params->asBinder());
+ remote()->transact(ALLOC_BUFFER_WITH_BACKUP, data, &reply);
+
+ status_t err = reply.readInt32();
+ if (err != OK) {
+ *buffer = 0;
+
+ return err;
+ }
+
+ *buffer = readVoidStar(&reply);
+
+ return err;
+ }
+
+ virtual status_t free_buffer(
+ node_id node, OMX_U32 port_index, buffer_id buffer) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ writeVoidStar(node, &data);
+ data.writeInt32(port_index);
+ writeVoidStar(buffer, &data);
+ remote()->transact(FREE_BUFFER, data, &reply);
+
+ return reply.readInt32();
+ }
+
+#if !IOMX_USES_SOCKETS
+ virtual status_t observe_node(
+ node_id node, const sp<IOMXObserver> &observer) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ writeVoidStar(node, &data);
+ data.writeStrongBinder(observer->asBinder());
+ remote()->transact(OBSERVE_NODE, data, &reply);
+
+ return reply.readInt32();
+ }
+
+ virtual void fill_buffer(node_id node, buffer_id buffer) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ writeVoidStar(node, &data);
+ writeVoidStar(buffer, &data);
+ remote()->transact(FILL_BUFFER, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+
+ virtual void empty_buffer(
+ node_id node,
+ buffer_id buffer,
+ OMX_U32 range_offset, OMX_U32 range_length,
+ OMX_U32 flags, OMX_TICKS timestamp) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ writeVoidStar(node, &data);
+ writeVoidStar(buffer, &data);
+ data.writeInt32(range_offset);
+ data.writeInt32(range_length);
+ data.writeInt32(flags);
+ data.writeInt64(timestamp);
+ remote()->transact(EMPTY_BUFFER, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+#endif
+};
+
+IMPLEMENT_META_INTERFACE(OMX, "android.hardware.IOMX");
+
+////////////////////////////////////////////////////////////////////////////////
+
+#define CHECK_INTERFACE(interface, data, reply) \
+ do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
+ LOGW("Call incorrectly routed to " #interface); \
+ return PERMISSION_DENIED; \
+ } } while (0)
+
+status_t BnOMX::onTransact(
+ uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
+ switch (code) {
+#if IOMX_USES_SOCKETS
+ case CONNECT:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ int s;
+ status_t err = connect(&s);
+
+ reply->writeInt32(err);
+ if (err == OK) {
+ assert(s >= 0);
+ reply->writeDupFileDescriptor(s);
+ close(s);
+ s = -1;
+ } else {
+ assert(s == -1);
+ }
+
+ return NO_ERROR;
+ }
+#endif
+
+ case LIST_NODES:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ List<String8> list;
+ list_nodes(&list);
+
+ reply->writeInt32(list.size());
+ for (List<String8>::iterator it = list.begin();
+ it != list.end(); ++it) {
+ reply->writeString8(*it);
+ }
+
+ return NO_ERROR;
+ }
+
+ case ALLOCATE_NODE:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ node_id node;
+ status_t err = allocate_node(data.readCString(), &node);
+ reply->writeInt32(err);
+ if (err == OK) {
+ writeVoidStar(node, reply);
+ }
+
+ return NO_ERROR;
+ }
+
+ case FREE_NODE:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ node_id node = readVoidStar(&data);
+
+ reply->writeInt32(free_node(node));
+
+ return NO_ERROR;
+ }
+
+ case SEND_COMMAND:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ node_id node = readVoidStar(&data);
+
+ OMX_COMMANDTYPE cmd =
+ static_cast<OMX_COMMANDTYPE>(data.readInt32());
+
+ OMX_S32 param = data.readInt32();
+ reply->writeInt32(send_command(node, cmd, param));
+
+ return NO_ERROR;
+ }
+
+ case GET_PARAMETER:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ node_id node = readVoidStar(&data);
+ OMX_INDEXTYPE index = static_cast<OMX_INDEXTYPE>(data.readInt32());
+
+ size_t size = data.readInt32();
+
+ // XXX I am not happy with this but Parcel::readInplace didn't work.
+ void *params = malloc(size);
+ data.read(params, size);
+
+ status_t err = get_parameter(node, index, params, size);
+
+ reply->writeInt32(err);
+
+ if (err == OK) {
+ reply->write(params, size);
+ }
+
+ free(params);
+ params = NULL;
+
+ return NO_ERROR;
+ }
+
+ case SET_PARAMETER:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ node_id node = readVoidStar(&data);
+ OMX_INDEXTYPE index = static_cast<OMX_INDEXTYPE>(data.readInt32());
+
+ size_t size = data.readInt32();
+ void *params = const_cast<void *>(data.readInplace(size));
+
+ reply->writeInt32(set_parameter(node, index, params, size));
+
+ return NO_ERROR;
+ }
+
+ case USE_BUFFER:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ node_id node = readVoidStar(&data);
+ OMX_U32 port_index = data.readInt32();
+ sp<IMemory> params =
+ interface_cast<IMemory>(data.readStrongBinder());
+
+ buffer_id buffer;
+ status_t err = use_buffer(node, port_index, params, &buffer);
+ reply->writeInt32(err);
+
+ if (err == OK) {
+ writeVoidStar(buffer, reply);
+ }
+
+ return NO_ERROR;
+ }
+
+ case ALLOC_BUFFER:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ node_id node = readVoidStar(&data);
+ OMX_U32 port_index = data.readInt32();
+ size_t size = data.readInt32();
+
+ buffer_id buffer;
+ status_t err = allocate_buffer(node, port_index, size, &buffer);
+ reply->writeInt32(err);
+
+ if (err == OK) {
+ writeVoidStar(buffer, reply);
+ }
+
+ return NO_ERROR;
+ }
+
+ case ALLOC_BUFFER_WITH_BACKUP:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ node_id node = readVoidStar(&data);
+ OMX_U32 port_index = data.readInt32();
+ sp<IMemory> params =
+ interface_cast<IMemory>(data.readStrongBinder());
+
+ buffer_id buffer;
+ status_t err = allocate_buffer_with_backup(
+ node, port_index, params, &buffer);
+
+ reply->writeInt32(err);
+
+ if (err == OK) {
+ writeVoidStar(buffer, reply);
+ }
+
+ return NO_ERROR;
+ }
+
+ case FREE_BUFFER:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ node_id node = readVoidStar(&data);
+ OMX_U32 port_index = data.readInt32();
+ buffer_id buffer = readVoidStar(&data);
+ reply->writeInt32(free_buffer(node, port_index, buffer));
+
+ return NO_ERROR;
+ }
+
+#if !IOMX_USES_SOCKETS
+ case OBSERVE_NODE:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ node_id node = readVoidStar(&data);
+ sp<IOMXObserver> observer =
+ interface_cast<IOMXObserver>(data.readStrongBinder());
+ reply->writeInt32(observe_node(node, observer));
+
+ return NO_ERROR;
+ }
+
+ case FILL_BUFFER:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ node_id node = readVoidStar(&data);
+ buffer_id buffer = readVoidStar(&data);
+ fill_buffer(node, buffer);
+
+ return NO_ERROR;
+ }
+
+ case EMPTY_BUFFER:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ node_id node = readVoidStar(&data);
+ buffer_id buffer = readVoidStar(&data);
+ OMX_U32 range_offset = data.readInt32();
+ OMX_U32 range_length = data.readInt32();
+ OMX_U32 flags = data.readInt32();
+ OMX_TICKS timestamp = data.readInt64();
+
+ empty_buffer(
+ node, buffer, range_offset, range_length,
+ flags, timestamp);
+
+ return NO_ERROR;
+ }
+#endif
+
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+class BpOMXObserver : public BpInterface<IOMXObserver> {
+public:
+ BpOMXObserver(const sp<IBinder> &impl)
+ : BpInterface<IOMXObserver>(impl) {
+ }
+
+ virtual void on_message(const omx_message &msg) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMXObserver::getInterfaceDescriptor());
+ data.write(&msg, sizeof(msg));
+
+ remote()->transact(OBSERVER_ON_MSG, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+};
+
+IMPLEMENT_META_INTERFACE(OMXObserver, "android.hardware.IOMXObserver");
+
+status_t BnOMXObserver::onTransact(
+ uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
+ switch (code) {
+ case OBSERVER_ON_MSG:
+ {
+ CHECK_INTERFACE(IOMXObserver, data, reply);
+
+ omx_message msg;
+ data.read(&msg, sizeof(msg));
+
+ // XXX Could use readInplace maybe?
+ on_message(msg);
+
+ return NO_ERROR;
+ }
+
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+} // namespace android
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index 0877142e65f6..f74ef3a30372 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -10,6 +10,7 @@ LOCAL_SRC_FILES:= \
MediaRecorderClient.cpp \
MediaPlayerService.cpp \
MetadataRetrieverClient.cpp \
+ TestPlayerStub.cpp \
VorbisPlayer.cpp \
MidiFile.cpp
@@ -28,10 +29,27 @@ LOCAL_SHARED_LIBRARIES := \
libmedia \
libandroid_runtime
+ifneq ($(TARGET_SIMULATOR),true)
+LOCAL_SHARED_LIBRARIES += libdl
+endif
+
LOCAL_C_INCLUDES := external/tremor/Tremor \
- $(call include-path-for, graphics corecg)
+ $(call include-path-for, graphics corecg) \
+ $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include
LOCAL_MODULE:= libmediaplayerservice
+ifeq ($(BUILD_WITH_STAGEFRIGHT),true)
+ LOCAL_SRC_FILES += StagefrightPlayer.cpp
+
+ LOCAL_SHARED_LIBRARIES += \
+ libstagefright \
+ libstagefright_omx
+
+ LOCAL_C_INCLUDES += $(TOP)/frameworks/base/media/libstagefright/omx
+
+ LOCAL_CFLAGS += -DBUILD_WITH_STAGEFRIGHT -DUSE_STAGEFRIGHT
+endif
+
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 493dc13a2959..5e62f9d8f8e5 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -29,7 +29,7 @@
#include <string.h>
#include <cutils/atomic.h>
-#include <cutils/properties.h>
+#include <cutils/properties.h> // for property_get
#include <utils/misc.h>
@@ -58,6 +58,19 @@
#include "MidiFile.h"
#include "VorbisPlayer.h"
#include <media/PVPlayer.h>
+#include "TestPlayerStub.h"
+
+#if USE_STAGEFRIGHT
+#include "StagefrightPlayer.h"
+#endif
+
+#ifdef BUILD_WITH_STAGEFRIGHT
+#include <OMX.h>
+#else
+#include <media/IOMX.h>
+#endif
+
+
/* desktop Linux needs a little help with gettid() */
#if defined(HAVE_GETTID) && !defined(HAVE_ANDROID_OS)
@@ -86,6 +99,8 @@ const int kMaxFilterSize = 64; // I pulled that out of thin air.
// Keep in sync with ANY in Metadata.java
const int32_t kAny = 0;
+const int32_t kMetaMarker = 0x4d455441; // 'M' 'E' 'T' 'A'
+
// Unmarshall a filter from a Parcel.
// Filter format in a parcel:
@@ -186,6 +201,10 @@ typedef struct {
const player_type playertype;
} extmap;
extmap FILE_EXTS [] = {
+#if USE_STAGEFRIGHT
+ {".mp4", STAGEFRIGHT_PLAYER},
+ {".3gp", STAGEFRIGHT_PLAYER},
+#endif
{".mid", SONIVOX_PLAYER},
{".midi", SONIVOX_PLAYER},
{".smf", SONIVOX_PLAYER},
@@ -271,6 +290,14 @@ sp<IMediaPlayer> MediaPlayerService::create(pid_t pid, const sp<IMediaPlayerClie
return c;
}
+sp<IOMX> MediaPlayerService::createOMX() {
+#ifdef BUILD_WITH_STAGEFRIGHT
+ return new OMX;
+#else
+ return NULL;
+#endif
+}
+
status_t MediaPlayerService::AudioCache::dump(int fd, const Vector<String16>& args) const
{
const size_t SIZE = 256;
@@ -577,6 +604,7 @@ void MediaPlayerService::Client::disconnect()
p = mPlayer;
}
mClient.clear();
+
mPlayer.clear();
// clear the notification to prevent callbacks to dead client
@@ -624,12 +652,19 @@ static player_type getPlayerType(int fd, int64_t offset, int64_t length)
EAS_Shutdown(easdata);
}
+#if USE_STAGEFRIGHT
+ return STAGEFRIGHT_PLAYER;
+#endif
+
// Fall through to PV
return PV_PLAYER;
}
static player_type getPlayerType(const char* url)
{
+ if (TestPlayerStub::canBeUsed(url)) {
+ return TEST_PLAYER;
+ }
// use MidiFile for MIDI extensions
int lenURL = strlen(url);
@@ -643,6 +678,10 @@ static player_type getPlayerType(const char* url)
}
}
+#if USE_STAGEFRIGHT
+ return STAGEFRIGHT_PLAYER;
+#endif
+
// Fall through to PV
return PV_PLAYER;
}
@@ -666,6 +705,21 @@ static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie,
LOGV(" create VorbisPlayer");
p = new VorbisPlayer();
break;
+#if USE_STAGEFRIGHT
+ case STAGEFRIGHT_PLAYER:
+ LOGV(" create StagefrightPlayer");
+ p = new StagefrightPlayer;
+ break;
+#else
+ case STAGEFRIGHT_PLAYER:
+ LOG_ALWAYS_FATAL(
+ "Should not be here, stagefright player not enabled.");
+ break;
+#endif
+ case TEST_PLAYER:
+ LOGV("Create Test Player stub");
+ p = new TestPlayerStub();
+ break;
}
if (p != NULL) {
if (p->initCheck() == NO_ERROR) {
@@ -730,7 +784,11 @@ status_t MediaPlayerService::Client::setDataSource(const char *url)
// now set data source
LOGV(" setDataSource");
mStatus = p->setDataSource(url);
- if (mStatus == NO_ERROR) mPlayer = p;
+ if (mStatus == NO_ERROR) {
+ mPlayer = p;
+ } else {
+ LOGE(" error: %d", mStatus);
+ }
return mStatus;
}
}
@@ -814,10 +872,14 @@ status_t MediaPlayerService::Client::setMetadataFilter(const Parcel& filter)
status_t MediaPlayerService::Client::getMetadata(
bool update_only, bool apply_filter, Parcel *reply)
{
+ sp<MediaPlayerBase> p = getPlayer();
+ if (p == 0) return UNKNOWN_ERROR;
+
status_t status;
- reply->writeInt32(-1); // Placeholder for the return code
+ // Placeholder for the return code, updated by the caller.
+ reply->writeInt32(-1);
- SortedVector<MetadataType> updates;
+ SortedVector<MetadataType> ids;
// We don't block notifications while we fetch the data. We clear
// mMetadataUpdated first so we don't lose notifications happening
@@ -825,15 +887,34 @@ status_t MediaPlayerService::Client::getMetadata(
{
Mutex::Autolock lock(mLock);
if (update_only) {
- updates = mMetadataUpdated;
+ ids = mMetadataUpdated;
}
mMetadataUpdated.clear();
}
- // FIXME: Implement, query the native player and do the optional filtering, etc...
- status = OK;
+ const size_t begin = reply->dataPosition();
+ reply->writeInt32(-1); // Placeholder for the length of the metadata
+ reply->writeInt32(kMetaMarker);
- return status;
+ status = p->getMetadata(ids, reply);
+
+ if (status != OK) {
+ reply->setDataPosition(begin);
+ LOGE("getMetadata failed %d", status);
+ return status;
+ }
+
+ // FIXME: Implement filtering on the result. Not critical since
+ // filtering takes place on the update notifications already. This
+ // would be when all the metadata are fetch and a filter is set.
+
+ const size_t end = reply->dataPosition();
+
+ // Everything is fine, update the metadata length.
+ reply->setDataPosition(begin);
+ reply->writeInt32(end - begin);
+ reply->setDataPosition(end);
+ return OK;
}
status_t MediaPlayerService::Client::prepareAsync()
@@ -1136,7 +1217,8 @@ Exit:
#undef LOG_TAG
#define LOG_TAG "AudioSink"
MediaPlayerService::AudioOutput::AudioOutput()
-{
+ : mCallback(NULL),
+ mCallbackCookie(NULL) {
mTrack = 0;
mStreamType = AudioSystem::MUSIC;
mLeftVolume = 1.0;
@@ -1206,8 +1288,13 @@ float MediaPlayerService::AudioOutput::msecsPerFrame() const
return mMsecsPerFrame;
}
-status_t MediaPlayerService::AudioOutput::open(uint32_t sampleRate, int channelCount, int format, int bufferCount)
+status_t MediaPlayerService::AudioOutput::open(
+ uint32_t sampleRate, int channelCount, int format, int bufferCount,
+ AudioCallback cb, void *cookie)
{
+ mCallback = cb;
+ mCallbackCookie = cookie;
+
// Check argument "bufferCount" against the mininum buffer count
if (bufferCount < mMinBufferCount) {
LOGD("bufferCount (%d) is too small and increased to %d", bufferCount, mMinBufferCount);
@@ -1228,7 +1315,17 @@ status_t MediaPlayerService::AudioOutput::open(uint32_t sampleRate, int channelC
}
frameCount = (sampleRate*afFrameCount*bufferCount)/afSampleRate;
- AudioTrack *t = new AudioTrack(mStreamType, sampleRate, format, channelCount, frameCount);
+
+ AudioTrack *t;
+ if (mCallback != NULL) {
+ t = new AudioTrack(
+ mStreamType, sampleRate, format, channelCount, frameCount,
+ 0 /* flags */, CallbackWrapper, this);
+ } else {
+ t = new AudioTrack(
+ mStreamType, sampleRate, format, channelCount, frameCount);
+ }
+
if ((t == 0) || (t->initCheck() != NO_ERROR)) {
LOGE("Unable to create audio track");
delete t;
@@ -1254,6 +1351,8 @@ void MediaPlayerService::AudioOutput::start()
ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size)
{
+ LOG_FATAL_IF(mCallback != NULL, "Don't call write if supplying a callback.");
+
//LOGV("write(%p, %u)", buffer, size);
if (mTrack) return mTrack->write(buffer, size);
return NO_INIT;
@@ -1294,6 +1393,20 @@ void MediaPlayerService::AudioOutput::setVolume(float left, float right)
}
}
+// static
+void MediaPlayerService::AudioOutput::CallbackWrapper(
+ int event, void *cookie, void *info) {
+ if (event != AudioTrack::EVENT_MORE_DATA) {
+ return;
+ }
+
+ AudioOutput *me = (AudioOutput *)cookie;
+ AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info;
+
+ (*me->mCallback)(
+ me, buffer->raw, buffer->size, me->mCallbackCookie);
+}
+
#undef LOG_TAG
#define LOG_TAG "AudioCache"
MediaPlayerService::AudioCache::AudioCache(const char* name) :
@@ -1314,8 +1427,14 @@ float MediaPlayerService::AudioCache::msecsPerFrame() const
return mMsecsPerFrame;
}
-status_t MediaPlayerService::AudioCache::open(uint32_t sampleRate, int channelCount, int format, int bufferCount)
+status_t MediaPlayerService::AudioCache::open(
+ uint32_t sampleRate, int channelCount, int format, int bufferCount,
+ AudioCallback cb, void *cookie)
{
+ if (cb != NULL) {
+ return UNKNOWN_ERROR; // TODO: implement this.
+ }
+
LOGV("open(%u, %d, %d, %d)", sampleRate, channelCount, format, bufferCount);
if (mHeap->getHeapID() < 0) return NO_INIT;
mSampleRate = sampleRate;
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index db3d5d7c506c..94cb9178a3ec 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -35,6 +35,7 @@ typedef int32_t MetadataType;
class IMediaRecorder;
class IMediaMetadataRetriever;
+class IOMX;
#define CALLBACK_ANTAGONIZER 0
#if CALLBACK_ANTAGONIZER
@@ -75,7 +76,12 @@ class MediaPlayerService : public BnMediaPlayerService
virtual ssize_t frameSize() const;
virtual uint32_t latency() const;
virtual float msecsPerFrame() const;
- virtual status_t open(uint32_t sampleRate, int channelCount, int format, int bufferCount=4);
+
+ virtual status_t open(
+ uint32_t sampleRate, int channelCount,
+ int format, int bufferCount,
+ AudioCallback cb, void *cookie);
+
virtual void start();
virtual ssize_t write(const void* buffer, size_t size);
virtual void stop();
@@ -90,8 +96,12 @@ class MediaPlayerService : public BnMediaPlayerService
static int getMinBufferCount();
private:
static void setMinBufferCount();
+ static void CallbackWrapper(
+ int event, void *me, void *info);
AudioTrack* mTrack;
+ AudioCallback mCallback;
+ void * mCallbackCookie;
int mStreamType;
float mLeftVolume;
float mRightVolume;
@@ -119,7 +129,12 @@ class MediaPlayerService : public BnMediaPlayerService
virtual ssize_t frameSize() const { return ssize_t(mChannelCount * ((mFormat == AudioSystem::PCM_16_BIT)?sizeof(int16_t):sizeof(u_int8_t))); }
virtual uint32_t latency() const;
virtual float msecsPerFrame() const;
- virtual status_t open(uint32_t sampleRate, int channelCount, int format, int bufferCount=1);
+
+ virtual status_t open(
+ uint32_t sampleRate, int channelCount, int format,
+ int bufferCount = 1,
+ AudioCallback cb = NULL, void *cookie = NULL);
+
virtual void start() {}
virtual ssize_t write(const void* buffer, size_t size);
virtual void stop() {}
@@ -166,6 +181,7 @@ public:
virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, int fd, int64_t offset, int64_t length);
virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
+ virtual sp<IOMX> createOMX();
virtual status_t dump(int fd, const Vector<String16>& args);
diff --git a/media/libmediaplayerservice/MidiFile.h b/media/libmediaplayerservice/MidiFile.h
index 83d97fee6dfa..30b6a2e0c307 100644
--- a/media/libmediaplayerservice/MidiFile.h
+++ b/media/libmediaplayerservice/MidiFile.h
@@ -46,7 +46,13 @@ public:
virtual status_t reset();
virtual status_t setLooping(int loop);
virtual player_type playerType() { return SONIVOX_PLAYER; }
- virtual status_t invoke(const Parcel& request, Parcel *reply) {return INVALID_OPERATION;}
+ virtual status_t invoke(const Parcel& request, Parcel *reply) {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getMetadata(const SortedVector<MetadataType>& ids,
+ Parcel *records) {
+ return INVALID_OPERATION;
+ }
private:
status_t createOutputTrack();
diff --git a/media/libmediaplayerservice/StagefrightPlayer.cpp b/media/libmediaplayerservice/StagefrightPlayer.cpp
new file mode 100644
index 000000000000..8597275048e7
--- /dev/null
+++ b/media/libmediaplayerservice/StagefrightPlayer.cpp
@@ -0,0 +1,213 @@
+//#define LOG_NDEBUG 0
+#define LOG_TAG "StagefrightPlayer"
+#include <utils/Log.h>
+
+#include "StagefrightPlayer.h"
+#include <media/stagefright/MediaPlayerImpl.h>
+
+namespace android {
+
+StagefrightPlayer::StagefrightPlayer()
+ : mPlayer(NULL) {
+ LOGV("StagefrightPlayer");
+}
+
+StagefrightPlayer::~StagefrightPlayer() {
+ LOGV("~StagefrightPlayer");
+ reset();
+ LOGV("~StagefrightPlayer done.");
+}
+
+status_t StagefrightPlayer::initCheck() {
+ LOGV("initCheck");
+ return OK;
+}
+
+status_t StagefrightPlayer::setDataSource(const char *url) {
+ LOGV("setDataSource('%s')", url);
+
+ reset();
+ mPlayer = new MediaPlayerImpl(url);
+
+ status_t err = mPlayer->initCheck();
+ if (err != OK) {
+ delete mPlayer;
+ mPlayer = NULL;
+ } else {
+ mPlayer->setAudioSink(mAudioSink);
+ }
+
+ return err;
+}
+
+status_t StagefrightPlayer::setDataSource(int fd, int64_t offset, int64_t length) {
+ LOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
+
+ reset();
+ mPlayer = new MediaPlayerImpl(fd, offset, length);
+
+ status_t err = mPlayer->initCheck();
+ if (err != OK) {
+ delete mPlayer;
+ mPlayer = NULL;
+ } else {
+ mPlayer->setAudioSink(mAudioSink);
+ }
+
+ return err;
+}
+
+status_t StagefrightPlayer::setVideoSurface(const sp<ISurface> &surface) {
+ LOGV("setVideoSurface");
+
+ if (mPlayer == NULL) {
+ return NO_INIT;
+ }
+
+ mPlayer->setISurface(surface);
+
+ return OK;
+}
+
+status_t StagefrightPlayer::prepare() {
+ LOGV("prepare");
+
+ if (mPlayer == NULL) {
+ return NO_INIT;
+ }
+
+ sendEvent(
+ MEDIA_SET_VIDEO_SIZE,
+ mPlayer->getWidth(), mPlayer->getHeight());
+
+ return OK;
+}
+
+status_t StagefrightPlayer::prepareAsync() {
+ LOGV("prepareAsync");
+
+ status_t err = prepare();
+
+ if (err != OK) {
+ return err;
+ }
+
+ sendEvent(MEDIA_PREPARED);
+
+ return OK;
+}
+
+status_t StagefrightPlayer::start() {
+ LOGV("start");
+
+ if (mPlayer == NULL) {
+ return NO_INIT;
+ }
+
+ mPlayer->play();
+
+ return OK;
+}
+
+status_t StagefrightPlayer::stop() {
+ LOGV("stop");
+
+ if (mPlayer == NULL) {
+ return NO_INIT;
+ }
+
+ reset();
+
+ return OK;
+}
+
+status_t StagefrightPlayer::pause() {
+ LOGV("pause");
+
+ if (mPlayer == NULL) {
+ return NO_INIT;
+ }
+
+ mPlayer->pause();
+
+ return OK;
+}
+
+bool StagefrightPlayer::isPlaying() {
+ LOGV("isPlaying");
+ return mPlayer != NULL && mPlayer->isPlaying();
+}
+
+status_t StagefrightPlayer::seekTo(int msec) {
+ LOGV("seekTo");
+
+ if (mPlayer == NULL) {
+ return NO_INIT;
+ }
+
+ status_t err = mPlayer->seekTo((int64_t)msec * 1000);
+
+ sendEvent(MEDIA_SEEK_COMPLETE);
+
+ return err;
+}
+
+status_t StagefrightPlayer::getCurrentPosition(int *msec) {
+ LOGV("getCurrentPosition");
+
+ if (mPlayer == NULL) {
+ return NO_INIT;
+ }
+
+ *msec = mPlayer->getPosition() / 1000;
+ return OK;
+}
+
+status_t StagefrightPlayer::getDuration(int *msec) {
+ LOGV("getDuration");
+
+ if (mPlayer == NULL) {
+ return NO_INIT;
+ }
+
+ *msec = mPlayer->getDuration() / 1000;
+ return OK;
+}
+
+status_t StagefrightPlayer::reset() {
+ LOGV("reset");
+
+ delete mPlayer;
+ mPlayer = NULL;
+
+ return OK;
+}
+
+status_t StagefrightPlayer::setLooping(int loop) {
+ LOGV("setLooping");
+ return UNKNOWN_ERROR;
+}
+
+player_type StagefrightPlayer::playerType() {
+ LOGV("playerType");
+ return STAGEFRIGHT_PLAYER;
+}
+
+status_t StagefrightPlayer::invoke(const Parcel &request, Parcel *reply) {
+ return INVALID_OPERATION;
+}
+
+void StagefrightPlayer::setAudioSink(const sp<AudioSink> &audioSink) {
+ MediaPlayerInterface::setAudioSink(audioSink);
+
+ if (mPlayer != NULL) {
+ mPlayer->setAudioSink(audioSink);
+ }
+}
+
+status_t StagefrightPlayer::getMetadata(
+ const SortedVector<MetadataType> &ids, Parcel *records) {
+ return INVALID_OPERATION;
+}
+
+} // namespace android
diff --git a/media/libmediaplayerservice/StagefrightPlayer.h b/media/libmediaplayerservice/StagefrightPlayer.h
new file mode 100644
index 000000000000..f93c1f854999
--- /dev/null
+++ b/media/libmediaplayerservice/StagefrightPlayer.h
@@ -0,0 +1,63 @@
+/*
+**
+** Copyright 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.
+*/
+
+#ifndef ANDROID_STAGEFRIGHTPLAYER_H
+#define ANDROID_STAGEFRIGHTPLAYER_H
+
+#include <media/MediaPlayerInterface.h>
+
+namespace android {
+
+class MediaPlayerImpl;
+
+class StagefrightPlayer : public MediaPlayerInterface {
+public:
+ StagefrightPlayer();
+ virtual ~StagefrightPlayer();
+
+ virtual status_t initCheck();
+ virtual status_t setDataSource(const char *url);
+ virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
+ virtual status_t setVideoSurface(const sp<ISurface> &surface);
+ virtual status_t prepare();
+ virtual status_t prepareAsync();
+ virtual status_t start();
+ virtual status_t stop();
+ virtual status_t pause();
+ virtual bool isPlaying();
+ virtual status_t seekTo(int msec);
+ virtual status_t getCurrentPosition(int *msec);
+ virtual status_t getDuration(int *msec);
+ virtual status_t reset();
+ virtual status_t setLooping(int loop);
+ virtual player_type playerType();
+ virtual status_t invoke(const Parcel &request, Parcel *reply);
+ virtual void setAudioSink(const sp<AudioSink> &audioSink);
+
+ virtual status_t getMetadata(
+ const SortedVector<MetadataType> &ids, Parcel *records);
+
+private:
+ MediaPlayerImpl *mPlayer;
+
+ StagefrightPlayer(const StagefrightPlayer &);
+ StagefrightPlayer &operator=(const StagefrightPlayer &);
+};
+
+} // namespace android
+
+#endif // ANDROID_STAGEFRIGHTPLAYER_H
diff --git a/media/libmediaplayerservice/TestPlayerStub.cpp b/media/libmediaplayerservice/TestPlayerStub.cpp
new file mode 100644
index 000000000000..862770864565
--- /dev/null
+++ b/media/libmediaplayerservice/TestPlayerStub.cpp
@@ -0,0 +1,196 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "TestPlayerStub"
+#include "utils/Log.h"
+
+#include "TestPlayerStub.h"
+
+#include <dlfcn.h> // for dlopen/dlclose
+#include <stdlib.h>
+#include <string.h>
+#include <cutils/properties.h>
+#include <utils/Errors.h> // for status_t
+
+#include "media/MediaPlayerInterface.h"
+
+
+namespace {
+using android::status_t;
+using android::MediaPlayerBase;
+
+const char *kTestUrlScheme = "test:";
+const char *kUrlParam = "url=";
+
+const char *kBuildTypePropName = "ro.build.type";
+const char *kEngBuild = "eng";
+const char *kTestBuild = "test";
+
+// @return true if the current build is 'eng' or 'test'.
+bool isTestBuild()
+{
+ char prop[PROPERTY_VALUE_MAX] = { '\0', };
+
+ property_get(kBuildTypePropName, prop, '\0');
+ return strcmp(prop, kEngBuild) == 0 || strcmp(prop, kTestBuild) == 0;
+}
+
+// @return true if the url scheme is 'test:'
+bool isTestUrl(const char *url)
+{
+ return url && strncmp(url, kTestUrlScheme, strlen(kTestUrlScheme)) == 0;
+}
+
+} // anonymous namespace
+
+namespace android {
+
+TestPlayerStub::TestPlayerStub()
+ :mUrl(NULL), mFilename(NULL), mContentUrl(NULL),
+ mHandle(NULL), mNewPlayer(NULL), mDeletePlayer(NULL),
+ mPlayer(NULL) { }
+
+TestPlayerStub::~TestPlayerStub()
+{
+ resetInternal();
+}
+
+status_t TestPlayerStub::initCheck()
+{
+ return isTestBuild() ? OK : INVALID_OPERATION;
+}
+
+// Parse mUrl to get:
+// * The library to be dlopened.
+// * The url to be passed to the real setDataSource impl.
+//
+// mUrl is expected to be in following format:
+//
+// test:<name of the .so>?url=<url for setDataSource>
+//
+// The value of the url parameter is treated as a string (no
+// unescaping of illegal charaters).
+status_t TestPlayerStub::parseUrl()
+{
+ if (strlen(mUrl) < strlen(kTestUrlScheme)) {
+ resetInternal();
+ return BAD_VALUE;
+ }
+
+ char *i = mUrl + strlen(kTestUrlScheme);
+
+ mFilename = i;
+
+ while (*i != '\0' && *i != '?') {
+ ++i;
+ }
+
+ if (*i == '\0' || strncmp(i + 1, kUrlParam, strlen(kUrlParam)) != 0) {
+ resetInternal();
+ return BAD_VALUE;
+ }
+ *i = '\0'; // replace '?' to nul-terminate mFilename
+
+ mContentUrl = i + 1 + strlen(kUrlParam);
+ return OK;
+}
+
+// Load the dynamic library.
+// Create the test player.
+// Call setDataSource on the test player with the url in param.
+status_t TestPlayerStub::setDataSource(const char *url)
+{
+ if (!isTestUrl(url) || NULL != mHandle) {
+ return INVALID_OPERATION;
+ }
+
+ mUrl = strdup(url);
+
+ status_t status = parseUrl();
+
+ if (OK != status) {
+ resetInternal();
+ return status;
+ }
+
+ ::dlerror(); // Clears any pending error.
+
+ // Load the test player from the url. dlopen will fail if the lib
+ // is not there. dls are under /system/lib
+ // None of the entry points should be NULL.
+ mHandle = ::dlopen(mFilename, RTLD_NOW | RTLD_GLOBAL);
+ if (!mHandle) {
+ LOGE("dlopen failed: %s", ::dlerror());
+ resetInternal();
+ return UNKNOWN_ERROR;
+ }
+
+ // Load the 2 entry points to create and delete instances.
+ const char *err;
+ mNewPlayer = reinterpret_cast<NEW_PLAYER>(dlsym(mHandle,
+ "newPlayer"));
+ err = ::dlerror();
+ if (err || mNewPlayer == NULL) {
+ // if err is NULL the string <null> is inserted in the logs =>
+ // mNewPlayer was NULL.
+ LOGE("dlsym for newPlayer failed %s", err);
+ resetInternal();
+ return UNKNOWN_ERROR;
+ }
+
+ mDeletePlayer = reinterpret_cast<DELETE_PLAYER>(dlsym(mHandle,
+ "deletePlayer"));
+ err = ::dlerror();
+ if (err || mDeletePlayer == NULL) {
+ LOGE("dlsym for deletePlayer failed %s", err);
+ resetInternal();
+ return UNKNOWN_ERROR;
+ }
+
+ mPlayer = (*mNewPlayer)();
+ return mPlayer->setDataSource(mContentUrl);
+}
+
+// Internal cleanup.
+status_t TestPlayerStub::resetInternal()
+{
+ if(mUrl) {
+ free(mUrl);
+ mUrl = NULL;
+ }
+ mFilename = NULL;
+ mContentUrl = NULL;
+
+ if (mPlayer) {
+ LOG_ASSERT(mDeletePlayer != NULL);
+ (*mDeletePlayer)(mPlayer);
+ mPlayer = NULL;
+ }
+
+ if (mHandle) {
+ ::dlclose(mHandle);
+ mHandle = NULL;
+ }
+ return OK;
+}
+
+/* static */ bool TestPlayerStub::canBeUsed(const char *url)
+{
+ return isTestBuild() && isTestUrl(url);
+}
+
+} // namespace android
diff --git a/media/libmediaplayerservice/TestPlayerStub.h b/media/libmediaplayerservice/TestPlayerStub.h
new file mode 100644
index 000000000000..339b10851bc6
--- /dev/null
+++ b/media/libmediaplayerservice/TestPlayerStub.h
@@ -0,0 +1,123 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_FRAMEWORKS_BASE_MEDIA_LIBMEDIAPLAYERSERVICE_TESTPLAYERSTUB_H__
+#define ANDROID_FRAMEWORKS_BASE_MEDIA_LIBMEDIAPLAYERSERVICE_TESTPLAYERSTUB_H__
+
+#include <media/MediaPlayerInterface.h>
+#include <utils/Errors.h>
+
+namespace android {
+class MediaPlayerBase; // in media/MediaPlayerInterface.h
+
+// Wrapper around a test media player that gets dynamically loaded.
+//
+// The URL passed to setDataSource has this format:
+//
+// test:<name of the .so>?url=<url for the real setDataSource impl.>
+//
+// e.g:
+// test:invoke_test_media_player.so?url=http://youtube.com/
+// test:invoke_test_media_player.so?url=speedtest
+//
+// TestPlayerStub::setDataSource loads the library in the test url. 2
+// entry points with C linkage are expected. One to create the test
+// player and one to destroy it.
+//
+// extern "C" android::MediaPlayerBase* newPlayer();
+// extern "C" android::status_t deletePlayer(android::MediaPlayerBase *p);
+//
+// Once the test player has been loaded, its setDataSource
+// implementation is called with the value of the 'url' parameter.
+//
+// typical usage in a java test:
+// ============================
+//
+// MediaPlayer p = new MediaPlayer();
+// p.setDataSource("test:invoke_mock_media_player.so?url=http://youtube.com");
+// p.prepare();
+// ...
+// p.release();
+
+class TestPlayerStub : public MediaPlayerInterface {
+ public:
+ typedef MediaPlayerBase* (*NEW_PLAYER)();
+ typedef status_t (*DELETE_PLAYER)(MediaPlayerBase *);
+
+ TestPlayerStub();
+ virtual ~TestPlayerStub();
+
+ // Called right after the constructor. Check if the current build
+ // allows test players.
+ virtual status_t initCheck();
+
+ // @param url Should be a test url. See class comment.
+ virtual status_t setDataSource(const char* url);
+
+ // Test player for a file descriptor source is not supported.
+ virtual status_t setDataSource(int, int64_t, int64_t) {
+ return INVALID_OPERATION;
+ }
+
+
+ // All the methods below wrap the mPlayer instance.
+ virtual status_t setVideoSurface(const android::sp<android::ISurface>& s) {
+ return mPlayer->setVideoSurface(s);
+ }
+ virtual status_t prepare() {return mPlayer->prepare();}
+ virtual status_t prepareAsync() {return mPlayer->prepareAsync();}
+ virtual status_t start() {return mPlayer->start();}
+ virtual status_t stop() {return mPlayer->stop();}
+ virtual status_t pause() {return mPlayer->pause();}
+ virtual bool isPlaying() {return mPlayer->isPlaying();}
+ virtual status_t seekTo(int msec) {return mPlayer->seekTo(msec);}
+ virtual status_t getCurrentPosition(int *p) {
+ return mPlayer->getCurrentPosition(p);
+ }
+ virtual status_t getDuration(int *d) {return mPlayer->getDuration(d);}
+ virtual status_t reset() {return mPlayer->reset();}
+ virtual status_t setLooping(int b) {return mPlayer->setLooping(b);}
+ virtual player_type playerType() {return mPlayer->playerType();}
+ virtual status_t invoke(const android::Parcel& in, android::Parcel *out) {
+ return mPlayer->invoke(in, out);
+ }
+ virtual status_t getMetadata(const SortedVector<MetadataType>& ids,
+ Parcel *records) {
+ return INVALID_OPERATION;
+ }
+
+
+ // @return true if the current build is 'eng' or 'test' and the
+ // url's scheme is 'test:'
+ static bool canBeUsed(const char *url);
+
+ private:
+ // Release the player, dlclose the library.
+ status_t resetInternal();
+ status_t parseUrl();
+
+ char *mUrl; // test:foo.so?url=http://bar
+ char *mFilename; // foo.so
+ char *mContentUrl; // http://bar
+ void *mHandle; // returned by dlopen
+ NEW_PLAYER mNewPlayer;
+ DELETE_PLAYER mDeletePlayer;
+ MediaPlayerBase *mPlayer; // wrapped player
+};
+
+} // namespace android
+
+#endif
diff --git a/media/libmediaplayerservice/VorbisPlayer.h b/media/libmediaplayerservice/VorbisPlayer.h
index 40246543d4ce..040eb36a7c7d 100644
--- a/media/libmediaplayerservice/VorbisPlayer.h
+++ b/media/libmediaplayerservice/VorbisPlayer.h
@@ -54,6 +54,10 @@ public:
virtual status_t setLooping(int loop);
virtual player_type playerType() { return VORBIS_PLAYER; }
virtual status_t invoke(const Parcel& request, Parcel *reply) {return INVALID_OPERATION;}
+ virtual status_t getMetadata(const SortedVector<MetadataType>& ids,
+ Parcel *records) {
+ return INVALID_OPERATION;
+ }
private:
status_t setdatasource(const char *path, int fd, int64_t offset, int64_t length);
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
new file mode 100644
index 000000000000..5944d9c2bf04
--- /dev/null
+++ b/media/libstagefright/Android.mk
@@ -0,0 +1,60 @@
+ifeq ($(BUILD_WITH_STAGEFRIGHT),true)
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ CachingDataSource.cpp \
+ DataSource.cpp \
+ FileSource.cpp \
+ HTTPDataSource.cpp \
+ HTTPStream.cpp \
+ MP3Extractor.cpp \
+ MPEG4Extractor.cpp \
+ MPEG4Writer.cpp \
+ MediaBuffer.cpp \
+ MediaBufferGroup.cpp \
+ MediaExtractor.cpp \
+ MediaPlayerImpl.cpp \
+ MediaSource.cpp \
+ MetaData.cpp \
+ MmapSource.cpp \
+ QComHardwareRenderer.cpp \
+ SampleTable.cpp \
+ ShoutcastSource.cpp \
+ SoftwareRenderer.cpp \
+ SurfaceRenderer.cpp \
+ TimeSource.cpp \
+ TimedEventQueue.cpp \
+ Utils.cpp \
+ AudioPlayer.cpp \
+ ESDS.cpp \
+ OMXClient.cpp \
+ OMXDecoder.cpp \
+ string.cpp
+
+LOCAL_C_INCLUDES:= \
+ $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
+ $(TOP)/external/opencore/android
+
+LOCAL_SHARED_LIBRARIES := \
+ libbinder \
+ libmedia \
+ libutils \
+ libcutils \
+ libui
+
+ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true)
+ LOCAL_LDLIBS += -lpthread
+endif
+
+LOCAL_CFLAGS += -Wno-multichar
+
+LOCAL_PRELINK_MODULE:= false
+
+LOCAL_MODULE:= libstagefright
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
+endif
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
new file mode 100644
index 000000000000..17c72b926e3e
--- /dev/null
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -0,0 +1,285 @@
+/*
+ * 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.
+ */
+
+#undef NDEBUG
+#include <assert.h>
+
+#define LOG_TAG "AudioPlayer"
+#include <utils/Log.h>
+
+#include <media/AudioTrack.h>
+#include <media/stagefright/AudioPlayer.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+
+namespace android {
+
+AudioPlayer::AudioPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink)
+ : mSource(NULL),
+ mAudioTrack(NULL),
+ mInputBuffer(NULL),
+ mSampleRate(0),
+ mLatencyUs(0),
+ mFrameSize(0),
+ mNumFramesPlayed(0),
+ mPositionTimeMediaUs(-1),
+ mPositionTimeRealUs(-1),
+ mSeeking(false),
+ mStarted(false),
+ mAudioSink(audioSink) {
+}
+
+AudioPlayer::~AudioPlayer() {
+ if (mStarted) {
+ stop();
+ }
+}
+
+void AudioPlayer::setSource(MediaSource *source) {
+ assert(mSource == NULL);
+ mSource = source;
+}
+
+void AudioPlayer::start() {
+ assert(!mStarted);
+ assert(mSource != NULL);
+
+ status_t err = mSource->start();
+ assert(err == OK);
+
+ sp<MetaData> format = mSource->getFormat();
+ const char *mime;
+ bool success = format->findCString(kKeyMIMEType, &mime);
+ assert(success);
+ assert(!strcasecmp(mime, "audio/raw"));
+
+ success = format->findInt32(kKeySampleRate, &mSampleRate);
+ assert(success);
+
+ int32_t numChannels;
+ success = format->findInt32(kKeyChannelCount, &numChannels);
+ assert(success);
+
+ if (mAudioSink.get() != NULL) {
+ status_t err = mAudioSink->open(
+ mSampleRate, numChannels, AudioSystem::PCM_16_BIT,
+ DEFAULT_AUDIOSINK_BUFFERCOUNT,
+ &AudioPlayer::AudioSinkCallback, this);
+ assert(err == OK);
+
+ mLatencyUs = (int64_t)mAudioSink->latency() * 1000;
+ mFrameSize = mAudioSink->frameSize();
+
+ mAudioSink->start();
+ } else {
+ mAudioTrack = new AudioTrack(
+ AudioSystem::MUSIC, mSampleRate, AudioSystem::PCM_16_BIT,
+ numChannels, 8192, 0, &AudioCallback, this, 0);
+
+ assert(mAudioTrack->initCheck() == OK);
+
+ mLatencyUs = (int64_t)mAudioTrack->latency() * 1000;
+ mFrameSize = mAudioTrack->frameSize();
+
+ mAudioTrack->start();
+ }
+
+ mStarted = true;
+}
+
+void AudioPlayer::pause() {
+ assert(mStarted);
+
+ if (mAudioSink.get() != NULL) {
+ mAudioSink->pause();
+ } else {
+ mAudioTrack->stop();
+ }
+}
+
+void AudioPlayer::resume() {
+ assert(mStarted);
+
+ if (mAudioSink.get() != NULL) {
+ mAudioSink->start();
+ } else {
+ mAudioTrack->start();
+ }
+}
+
+void AudioPlayer::stop() {
+ assert(mStarted);
+
+ if (mAudioSink.get() != NULL) {
+ mAudioSink->stop();
+ } else {
+ mAudioTrack->stop();
+
+ delete mAudioTrack;
+ mAudioTrack = NULL;
+ }
+
+ // Make sure to release any buffer we hold onto so that the
+ // source is able to stop().
+ if (mInputBuffer != NULL) {
+ LOGI("AudioPlayer releasing input buffer.");
+
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ }
+
+ mSource->stop();
+
+ mNumFramesPlayed = 0;
+ mPositionTimeMediaUs = -1;
+ mPositionTimeRealUs = -1;
+ mSeeking = false;
+ mStarted = false;
+}
+
+// static
+void AudioPlayer::AudioCallback(int event, void *user, void *info) {
+ static_cast<AudioPlayer *>(user)->AudioCallback(event, info);
+}
+
+// static
+void AudioPlayer::AudioSinkCallback(
+ MediaPlayerBase::AudioSink *audioSink,
+ void *buffer, size_t size, void *cookie) {
+ AudioPlayer *me = (AudioPlayer *)cookie;
+
+ me->fillBuffer(buffer, size);
+}
+
+void AudioPlayer::AudioCallback(int event, void *info) {
+ if (event != AudioTrack::EVENT_MORE_DATA) {
+ return;
+ }
+
+ AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info;
+ fillBuffer(buffer->raw, buffer->size);
+}
+
+void AudioPlayer::fillBuffer(void *data, size_t size) {
+ if (mNumFramesPlayed == 0) {
+ LOGI("AudioCallback");
+ }
+
+ size_t size_done = 0;
+ size_t size_remaining = size;
+ while (size_remaining > 0) {
+ MediaSource::ReadOptions options;
+
+ {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mSeeking) {
+ options.setSeekTo(mSeekTimeUs);
+
+ if (mInputBuffer != NULL) {
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ }
+ mSeeking = false;
+ }
+ }
+
+ if (mInputBuffer == NULL) {
+ status_t err = mSource->read(&mInputBuffer, &options);
+
+ assert((err == OK && mInputBuffer != NULL)
+ || (err != OK && mInputBuffer == NULL));
+
+ if (err != OK) {
+ memset((char *)data + size_done, 0, size_remaining);
+ break;
+ }
+
+ int32_t units, scale;
+ bool success =
+ mInputBuffer->meta_data()->findInt32(kKeyTimeUnits, &units);
+ success = success &&
+ mInputBuffer->meta_data()->findInt32(kKeyTimeScale, &scale);
+ assert(success);
+
+ Mutex::Autolock autoLock(mLock);
+ mPositionTimeMediaUs = (int64_t)units * 1000000 / scale;
+ mPositionTimeRealUs =
+ ((mNumFramesPlayed + size_done / 4) * 1000000) / mSampleRate; // XXX
+ }
+
+ if (mInputBuffer->range_length() == 0) {
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+
+ continue;
+ }
+
+ size_t copy = size_remaining;
+ if (copy > mInputBuffer->range_length()) {
+ copy = mInputBuffer->range_length();
+ }
+
+ memcpy((char *)data + size_done,
+ (const char *)mInputBuffer->data() + mInputBuffer->range_offset(),
+ copy);
+
+ mInputBuffer->set_range(mInputBuffer->range_offset() + copy,
+ mInputBuffer->range_length() - copy);
+
+ size_done += copy;
+ size_remaining -= copy;
+ }
+
+ Mutex::Autolock autoLock(mLock);
+ mNumFramesPlayed += size / mFrameSize;
+}
+
+int64_t AudioPlayer::getRealTimeUs() {
+ Mutex::Autolock autoLock(mLock);
+ return getRealTimeUsLocked();
+}
+
+int64_t AudioPlayer::getRealTimeUsLocked() const {
+ return -mLatencyUs + (mNumFramesPlayed * 1000000) / mSampleRate;
+}
+
+int64_t AudioPlayer::getMediaTimeUs() {
+ Mutex::Autolock autoLock(mLock);
+
+ return mPositionTimeMediaUs + (getRealTimeUsLocked() - mPositionTimeRealUs);
+}
+
+bool AudioPlayer::getMediaTimeMapping(
+ int64_t *realtime_us, int64_t *mediatime_us) {
+ Mutex::Autolock autoLock(mLock);
+
+ *realtime_us = mPositionTimeRealUs;
+ *mediatime_us = mPositionTimeMediaUs;
+
+ return mPositionTimeRealUs != -1 || mPositionTimeMediaUs != -1;
+}
+
+status_t AudioPlayer::seekTo(int64_t time_us) {
+ Mutex::Autolock autoLock(mLock);
+
+ mSeeking = true;
+ mSeekTimeUs = time_us;
+
+ return OK;
+}
+
+}
diff --git a/media/libstagefright/CachingDataSource.cpp b/media/libstagefright/CachingDataSource.cpp
new file mode 100644
index 000000000000..0fd71d5b9c18
--- /dev/null
+++ b/media/libstagefright/CachingDataSource.cpp
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ */
+
+#include <media/stagefright/CachingDataSource.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+namespace android {
+
+CachingDataSource::CachingDataSource(
+ DataSource *source, size_t pageSize, int numPages)
+ : mSource(source),
+ mData(malloc(pageSize * numPages)),
+ mPageSize(pageSize),
+ mFirst(NULL),
+ mLast(NULL) {
+ for (int i = 0; i < numPages; ++i) {
+ Page *page = new Page;
+ page->mPrev = mLast;
+ page->mNext = NULL;
+
+ if (mLast == NULL) {
+ mFirst = page;
+ } else {
+ mLast->mNext = page;
+ }
+
+ mLast = page;
+
+ page->mOffset = -1;
+ page->mLength = 0;
+ page->mData = (char *)mData + mPageSize * i;
+ }
+}
+
+CachingDataSource::~CachingDataSource() {
+ Page *page = mFirst;
+ while (page != NULL) {
+ Page *next = page->mNext;
+ delete page;
+ page = next;
+ }
+ mFirst = mLast = NULL;
+
+ free(mData);
+ mData = NULL;
+
+ delete mSource;
+ mSource = NULL;
+}
+
+status_t CachingDataSource::InitCheck() const {
+ return OK;
+}
+
+ssize_t CachingDataSource::read_at(off_t offset, void *data, size_t size) {
+ Mutex::Autolock autoLock(mLock);
+
+ size_t total = 0;
+ while (size > 0) {
+ Page *page = mFirst;
+ while (page != NULL) {
+ if (page->mOffset >= 0 && offset >= page->mOffset
+ && offset < page->mOffset + page->mLength) {
+ break;
+ }
+ page = page->mNext;
+ }
+
+ if (page == NULL) {
+ page = allocate_page();
+ page->mOffset = offset - offset % mPageSize;
+ ssize_t n = mSource->read_at(page->mOffset, page->mData, mPageSize);
+ if (n < 0) {
+ page->mLength = 0;
+ } else {
+ page->mLength = (size_t)n;
+ }
+ mFirst->mPrev = page;
+ page->mNext = mFirst;
+ page->mPrev = NULL;
+ mFirst = page;
+
+ if (n < 0) {
+ return n;
+ }
+
+ if (offset >= page->mOffset + page->mLength) {
+ break;
+ }
+ } else {
+ // Move "page" to the front in LRU order.
+ if (page->mNext != NULL) {
+ page->mNext->mPrev = page->mPrev;
+ } else {
+ mLast = page->mPrev;
+ }
+
+ if (page->mPrev != NULL) {
+ page->mPrev->mNext = page->mNext;
+ } else {
+ mFirst = page->mNext;
+ }
+
+ mFirst->mPrev = page;
+ page->mNext = mFirst;
+ page->mPrev = NULL;
+ mFirst = page;
+ }
+
+ size_t copy = page->mLength - (offset - page->mOffset);
+ if (copy > size) {
+ copy = size;
+ }
+ memcpy(data,(const char *)page->mData + (offset - page->mOffset),
+ copy);
+
+ total += copy;
+
+ if (page->mLength < mPageSize) {
+ // This was the final page. There is no more data beyond it.
+ break;
+ }
+
+ offset += copy;
+ size -= copy;
+ data = (char *)data + copy;
+ }
+
+ return total;
+}
+
+CachingDataSource::Page *CachingDataSource::allocate_page() {
+ // The last page is the least recently used, i.e. oldest.
+
+ Page *page = mLast;
+
+ page->mPrev->mNext = NULL;
+ mLast = page->mPrev;
+ page->mPrev = NULL;
+
+ return page;
+}
+
+} // namespace android
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
new file mode 100644
index 000000000000..ee1287335bf0
--- /dev/null
+++ b/media/libstagefright/CameraSource.cpp
@@ -0,0 +1,226 @@
+/*
+ * 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.
+ */
+
+#include <sys/time.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <OMX_Component.h>
+
+#include <binder/IServiceManager.h>
+#include <media/stagefright/CameraSource.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+#include <ui/ICameraClient.h>
+#include <ui/ICameraService.h>
+#include <ui/Overlay.h>
+#include <utils/String16.h>
+
+namespace android {
+
+class CameraBuffer : public MediaBuffer {
+public:
+ CameraBuffer(const sp<IMemory> &frame)
+ : MediaBuffer(frame->pointer(), frame->size()),
+ mFrame(frame) {
+ }
+
+ sp<IMemory> releaseFrame() {
+ sp<IMemory> frame = mFrame;
+ mFrame.clear();
+ return frame;
+ }
+
+private:
+ sp<IMemory> mFrame;
+};
+
+class CameraSourceClient : public BnCameraClient {
+public:
+ CameraSourceClient()
+ : mSource(NULL) {
+ }
+
+ virtual void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) {
+ assert(mSource != NULL);
+ mSource->notifyCallback(msgType, ext1, ext2);
+ }
+
+ virtual void dataCallback(int32_t msgType, const sp<IMemory> &data) {
+ assert(mSource != NULL);
+ mSource->dataCallback(msgType, data);
+ }
+
+ void setCameraSource(CameraSource *source) {
+ mSource = source;
+ }
+
+private:
+ CameraSource *mSource;
+};
+
+class DummySurface : public BnSurface {
+public:
+ DummySurface() {}
+
+ virtual status_t registerBuffers(const BufferHeap &buffers) {
+ return OK;
+ }
+
+ virtual void postBuffer(ssize_t offset) {
+ }
+
+ virtual void unregisterBuffers() {
+ }
+
+ virtual sp<OverlayRef> createOverlay(
+ uint32_t w, uint32_t h, int32_t format) {
+ return NULL;
+ }
+};
+
+// static
+CameraSource *CameraSource::Create() {
+ sp<IServiceManager> sm = defaultServiceManager();
+
+ sp<ICameraService> service =
+ interface_cast<ICameraService>(
+ sm->getService(String16("media.camera")));
+
+ sp<CameraSourceClient> client = new CameraSourceClient;
+ sp<ICamera> camera = service->connect(client);
+
+ CameraSource *source = new CameraSource(camera, client);
+ client->setCameraSource(source);
+
+ return source;
+}
+
+CameraSource::CameraSource(
+ const sp<ICamera> &camera, const sp<ICameraClient> &client)
+ : mCamera(camera),
+ mCameraClient(client),
+ mNumFrames(0),
+ mStarted(false) {
+ printf("params: \"%s\"\n", mCamera->getParameters().string());
+}
+
+CameraSource::~CameraSource() {
+ if (mStarted) {
+ stop();
+ }
+
+ mCamera->disconnect();
+}
+
+status_t CameraSource::start(MetaData *) {
+ assert(!mStarted);
+
+ status_t err = mCamera->lock();
+ assert(err == OK);
+
+ err = mCamera->setPreviewDisplay(new DummySurface);
+ assert(err == OK);
+ mCamera->setPreviewCallbackFlag(1);
+ mCamera->startPreview();
+ assert(err == OK);
+
+ mStarted = true;
+
+ return OK;
+}
+
+status_t CameraSource::stop() {
+ assert(mStarted);
+
+ mCamera->stopPreview();
+ mCamera->unlock();
+
+ mStarted = false;
+
+ return OK;
+}
+
+sp<MetaData> CameraSource::getFormat() {
+ sp<MetaData> meta = new MetaData;
+ meta->setCString(kKeyMIMEType, "video/raw");
+ meta->setInt32(kKeyColorFormat, OMX_COLOR_FormatYUV420SemiPlanar);
+ meta->setInt32(kKeyWidth, 480);
+ meta->setInt32(kKeyHeight, 320);
+
+ return meta;
+}
+
+status_t CameraSource::read(
+ MediaBuffer **buffer, const ReadOptions *options) {
+ assert(mStarted);
+
+ *buffer = NULL;
+
+ int64_t seekTimeUs;
+ if (options && options->getSeekTo(&seekTimeUs)) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ sp<IMemory> frame;
+
+ {
+ Mutex::Autolock autoLock(mLock);
+ while (mFrames.empty()) {
+ mFrameAvailableCondition.wait(mLock);
+ }
+
+ frame = *mFrames.begin();
+ mFrames.erase(mFrames.begin());
+ }
+
+ int count = mNumFrames++;
+
+ *buffer = new CameraBuffer(frame);
+
+ (*buffer)->meta_data()->clear();
+ (*buffer)->meta_data()->setInt32(kKeyTimeScale, 15);
+ (*buffer)->meta_data()->setInt32(kKeyTimeUnits, count);
+
+ (*buffer)->add_ref();
+ (*buffer)->setObserver(this);
+
+ return OK;
+}
+
+void CameraSource::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) {
+ printf("notifyCallback %d, %d, %d\n", msgType, ext1, ext2);
+}
+
+void CameraSource::dataCallback(int32_t msgType, const sp<IMemory> &data) {
+ Mutex::Autolock autoLock(mLock);
+
+ mFrames.push_back(data);
+ mFrameAvailableCondition.signal();
+}
+
+void CameraSource::signalBufferReturned(MediaBuffer *_buffer) {
+ CameraBuffer *buffer = static_cast<CameraBuffer *>(_buffer);
+
+ mCamera->releaseRecordingFrame(buffer->releaseFrame());
+
+ buffer->setObserver(NULL);
+ buffer->release();
+ buffer = NULL;
+}
+
+} // namespace android
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
new file mode 100644
index 000000000000..6e6b43def271
--- /dev/null
+++ b/media/libstagefright/DataSource.cpp
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MP3Extractor.h>
+#include <media/stagefright/MPEG4Extractor.h>
+#include <utils/String8.h>
+
+namespace android {
+
+status_t DataSource::getSize(off_t *size) {
+ *size = 0;
+
+ return ERROR_UNSUPPORTED;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+Mutex DataSource::gSnifferMutex;
+List<DataSource::SnifferFunc> DataSource::gSniffers;
+
+bool DataSource::sniff(String8 *mimeType, float *confidence) {
+ *mimeType = "";
+ *confidence = 0.0f;
+
+ Mutex::Autolock autoLock(gSnifferMutex);
+ for (List<SnifferFunc>::iterator it = gSniffers.begin();
+ it != gSniffers.end(); ++it) {
+ String8 newMimeType;
+ float newConfidence;
+ if ((*it)(this, &newMimeType, &newConfidence)) {
+ if (newConfidence > *confidence) {
+ *mimeType = newMimeType;
+ *confidence = newConfidence;
+ }
+ }
+ }
+
+ return *confidence > 0.0;
+}
+
+// static
+void DataSource::RegisterSniffer(SnifferFunc func) {
+ Mutex::Autolock autoLock(gSnifferMutex);
+
+ for (List<SnifferFunc>::iterator it = gSniffers.begin();
+ it != gSniffers.end(); ++it) {
+ if (*it == func) {
+ return;
+ }
+ }
+
+ gSniffers.push_back(func);
+}
+
+// static
+void DataSource::RegisterDefaultSniffers() {
+ RegisterSniffer(SniffMP3);
+ RegisterSniffer(SniffMPEG4);
+}
+
+} // namespace android
diff --git a/media/libstagefright/ESDS.cpp b/media/libstagefright/ESDS.cpp
new file mode 100644
index 000000000000..53b92a07df40
--- /dev/null
+++ b/media/libstagefright/ESDS.cpp
@@ -0,0 +1,196 @@
+/*
+ * 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.
+ */
+
+#include <media/stagefright/ESDS.h>
+
+#include <string.h>
+
+namespace android {
+
+ESDS::ESDS(const void *data, size_t size)
+ : mData(new uint8_t[size]),
+ mSize(size),
+ mInitCheck(NO_INIT),
+ mDecoderSpecificOffset(0),
+ mDecoderSpecificLength(0) {
+ memcpy(mData, data, size);
+
+ mInitCheck = parse();
+}
+
+ESDS::~ESDS() {
+ delete[] mData;
+ mData = NULL;
+}
+
+status_t ESDS::InitCheck() const {
+ return mInitCheck;
+}
+
+status_t ESDS::getCodecSpecificInfo(const void **data, size_t *size) const {
+ if (mInitCheck != OK) {
+ return mInitCheck;
+ }
+
+ *data = &mData[mDecoderSpecificOffset];
+ *size = mDecoderSpecificLength;
+
+ return OK;
+}
+
+status_t ESDS::skipDescriptorHeader(
+ size_t offset, size_t size,
+ uint8_t *tag, size_t *data_offset, size_t *data_size) const {
+ if (size == 0) {
+ return ERROR_MALFORMED;
+ }
+
+ *tag = mData[offset++];
+ --size;
+
+ *data_size = 0;
+ bool more;
+ do {
+ if (size == 0) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t x = mData[offset++];
+ --size;
+
+ *data_size = (*data_size << 7) | (x & 0x7f);
+ more = (x & 0x80) != 0;
+ }
+ while (more);
+
+ if (*data_size > size) {
+ return ERROR_MALFORMED;
+ }
+
+ *data_offset = offset;
+
+ return OK;
+}
+
+status_t ESDS::parse() {
+ uint8_t tag;
+ size_t data_offset;
+ size_t data_size;
+ status_t err =
+ skipDescriptorHeader(0, mSize, &tag, &data_offset, &data_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (tag != kTag_ESDescriptor) {
+ return ERROR_MALFORMED;
+ }
+
+ return parseESDescriptor(data_offset, data_size);
+}
+
+status_t ESDS::parseESDescriptor(size_t offset, size_t size) {
+ if (size < 3) {
+ return ERROR_MALFORMED;
+ }
+
+ offset += 2; // skip ES_ID
+ size -= 2;
+
+ unsigned streamDependenceFlag = mData[offset] & 0x80;
+ unsigned URL_Flag = mData[offset] & 0x40;
+ unsigned OCRstreamFlag = mData[offset] & 0x20;
+
+ ++offset;
+ --size;
+
+ if (streamDependenceFlag) {
+ offset += 2;
+ size -= 2;
+ }
+
+ if (URL_Flag) {
+ if (offset >= size) {
+ return ERROR_MALFORMED;
+ }
+ unsigned URLlength = mData[offset];
+ offset += URLlength + 1;
+ size -= URLlength + 1;
+ }
+
+ if (OCRstreamFlag) {
+ offset += 2;
+ size -= 2;
+ }
+
+ if (offset >= size) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t tag;
+ size_t sub_offset, sub_size;
+ status_t err = skipDescriptorHeader(
+ offset, size, &tag, &sub_offset, &sub_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (tag != kTag_DecoderConfigDescriptor) {
+ return ERROR_MALFORMED;
+ }
+
+ err = parseDecoderConfigDescriptor(sub_offset, sub_size);
+
+ return err;
+}
+
+status_t ESDS::parseDecoderConfigDescriptor(size_t offset, size_t size) {
+ if (size < 13) {
+ return ERROR_MALFORMED;
+ }
+
+ offset += 13;
+ size -= 13;
+
+ if (size == 0) {
+ mDecoderSpecificOffset = 0;
+ mDecoderSpecificLength = 0;
+ return OK;
+ }
+
+ uint8_t tag;
+ size_t sub_offset, sub_size;
+ status_t err = skipDescriptorHeader(
+ offset, size, &tag, &sub_offset, &sub_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (tag != kTag_DecoderSpecificInfo) {
+ return ERROR_MALFORMED;
+ }
+
+ mDecoderSpecificOffset = sub_offset;
+ mDecoderSpecificLength = sub_size;
+
+ return OK;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/FileSource.cpp b/media/libstagefright/FileSource.cpp
new file mode 100644
index 000000000000..c26d0a0f5c07
--- /dev/null
+++ b/media/libstagefright/FileSource.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+#include <media/stagefright/FileSource.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+namespace android {
+
+FileSource::FileSource(const char *filename)
+ : mFile(fopen(filename, "rb")) {
+}
+
+FileSource::~FileSource() {
+ if (mFile != NULL) {
+ fclose(mFile);
+ mFile = NULL;
+ }
+}
+
+status_t FileSource::InitCheck() const {
+ return mFile != NULL ? OK : NO_INIT;
+}
+
+ssize_t FileSource::read_at(off_t offset, void *data, size_t size) {
+ Mutex::Autolock autoLock(mLock);
+
+ int err = fseeko(mFile, offset, SEEK_SET);
+ assert(err != -1);
+
+ ssize_t result = fread(data, 1, size, mFile);
+
+ return result;
+}
+
+} // namespace android
diff --git a/media/libstagefright/HTTPDataSource.cpp b/media/libstagefright/HTTPDataSource.cpp
new file mode 100644
index 000000000000..d1f8cd415e14
--- /dev/null
+++ b/media/libstagefright/HTTPDataSource.cpp
@@ -0,0 +1,174 @@
+/*
+ * 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.
+ */
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <stdlib.h>
+
+#include <media/stagefright/HTTPDataSource.h>
+#include <media/stagefright/HTTPStream.h>
+#include <media/stagefright/string.h>
+
+namespace android {
+
+HTTPDataSource::HTTPDataSource(const char *uri)
+ : mHost(NULL),
+ mPort(0),
+ mPath(NULL),
+ mBuffer(malloc(kBufferSize)),
+ mBufferLength(0),
+ mBufferOffset(0) {
+ assert(!strncasecmp("http://", uri, 7));
+
+ string host;
+ string path;
+ int port;
+
+ char *slash = strchr(uri + 7, '/');
+ if (slash == NULL) {
+ host = uri + 7;
+ path = "/";
+ } else {
+ host = string(uri + 7, slash - (uri + 7));
+ path = slash;
+ }
+
+ char *colon = strchr(host.c_str(), ':');
+ if (colon == NULL) {
+ port = 80;
+ } else {
+ char *end;
+ long tmp = strtol(colon + 1, &end, 10);
+ assert(end > colon + 1);
+ assert(tmp > 0 && tmp < 65536);
+ port = tmp;
+
+ host = string(host, 0, colon - host.c_str());
+ }
+
+ LOGI("Connecting to host '%s', port %d, path '%s'",
+ host.c_str(), port, path.c_str());
+
+ mHost = strdup(host.c_str());
+ mPort = port;
+ mPath = strdup(path.c_str());
+
+ status_t err = mHttp.connect(mHost, mPort);
+ assert(err == OK);
+}
+
+HTTPDataSource::HTTPDataSource(const char *host, int port, const char *path)
+ : mHost(strdup(host)),
+ mPort(port),
+ mPath(strdup(path)),
+ mBuffer(malloc(kBufferSize)),
+ mBufferLength(0),
+ mBufferOffset(0) {
+ status_t err = mHttp.connect(mHost, mPort);
+ assert(err == OK);
+}
+
+HTTPDataSource::~HTTPDataSource() {
+ mHttp.disconnect();
+
+ free(mBuffer);
+ mBuffer = NULL;
+
+ free(mPath);
+ mPath = NULL;
+}
+
+ssize_t HTTPDataSource::read_at(off_t offset, void *data, size_t size) {
+ if (offset >= mBufferOffset && offset < mBufferOffset + mBufferLength) {
+ size_t num_bytes_available = mBufferLength - (offset - mBufferOffset);
+
+ size_t copy = num_bytes_available;
+ if (copy > size) {
+ copy = size;
+ }
+
+ memcpy(data, (const char *)mBuffer + (offset - mBufferOffset), copy);
+
+ return copy;
+ }
+
+ mBufferOffset = offset;
+ mBufferLength = 0;
+
+ char host[128];
+ sprintf(host, "Host: %s\r\n", mHost);
+
+ char range[128];
+ sprintf(range, "Range: bytes=%ld-%ld\r\n\r\n",
+ mBufferOffset, mBufferOffset + kBufferSize - 1);
+
+ int http_status;
+
+ status_t err;
+ int attempt = 1;
+ for (;;) {
+ if ((err = mHttp.send("GET ")) != OK
+ || (err = mHttp.send(mPath)) != OK
+ || (err = mHttp.send(" HTTP/1.1\r\n")) != OK
+ || (err = mHttp.send(host)) != OK
+ || (err = mHttp.send(range)) != OK
+ || (err = mHttp.send("\r\n")) != OK
+ || (err = mHttp.receive_header(&http_status)) != OK) {
+
+ if (attempt == 3) {
+ return err;
+ }
+
+ mHttp.connect(mHost, mPort);
+ ++attempt;
+ } else {
+ break;
+ }
+ }
+
+ if ((http_status / 100) != 2) {
+ return UNKNOWN_ERROR;
+ }
+
+ string value;
+ if (!mHttp.find_header_value("Content-Length", &value)) {
+ return UNKNOWN_ERROR;
+ }
+
+ char *end;
+ unsigned long contentLength = strtoul(value.c_str(), &end, 10);
+
+ ssize_t num_bytes_received = mHttp.receive(mBuffer, contentLength);
+
+ if (num_bytes_received <= 0) {
+ return num_bytes_received;
+ }
+
+ mBufferLength = (size_t)num_bytes_received;
+
+ size_t copy = mBufferLength;
+ if (copy > size) {
+ copy = size;
+ }
+
+ memcpy(data, mBuffer, copy);
+
+ return copy;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/HTTPStream.cpp b/media/libstagefright/HTTPStream.cpp
new file mode 100644
index 000000000000..29e6f726b99f
--- /dev/null
+++ b/media/libstagefright/HTTPStream.cpp
@@ -0,0 +1,285 @@
+/*
+ * 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.
+ */
+
+#include <sys/socket.h>
+
+#include <arpa/inet.h>
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <media/stagefright/HTTPStream.h>
+
+namespace android {
+
+// static
+const char *HTTPStream::kStatusKey = ":status:";
+
+HTTPStream::HTTPStream()
+ : mState(READY),
+ mSocket(-1) {
+}
+
+HTTPStream::~HTTPStream() {
+ disconnect();
+}
+
+status_t HTTPStream::connect(const char *server, int port) {
+ status_t err = OK;
+
+ if (mState == CONNECTED) {
+ return ERROR_ALREADY_CONNECTED;
+ }
+
+ assert(mSocket == -1);
+ mSocket = socket(AF_INET, SOCK_STREAM, 0);
+
+ if (mSocket < 0) {
+ return UNKNOWN_ERROR;
+ }
+
+ struct hostent *ent = gethostbyname(server);
+ if (ent == NULL) {
+ err = ERROR_UNKNOWN_HOST;
+ goto exit1;
+ }
+
+ struct sockaddr_in addr;
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
+ memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
+
+ if (::connect(mSocket, (const struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ err = ERROR_CANNOT_CONNECT;
+ goto exit1;
+ }
+
+ mState = CONNECTED;
+
+ return OK;
+
+exit1:
+ close(mSocket);
+ mSocket = -1;
+
+ return err;
+}
+
+status_t HTTPStream::disconnect() {
+ if (mState != CONNECTED) {
+ return ERROR_NOT_CONNECTED;
+ }
+
+ assert(mSocket >= 0);
+ close(mSocket);
+ mSocket = -1;
+
+ mState = READY;
+
+ return OK;
+}
+
+status_t HTTPStream::send(const char *data, size_t size) {
+ if (mState != CONNECTED) {
+ return ERROR_NOT_CONNECTED;
+ }
+
+ while (size > 0) {
+ ssize_t n = ::send(mSocket, data, size, 0);
+
+ if (n < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+
+ disconnect();
+
+ return ERROR_IO;
+ } else if (n == 0) {
+ disconnect();
+
+ return ERROR_CONNECTION_LOST;
+ }
+
+ size -= (size_t)n;
+ data += (size_t)n;
+ }
+
+ return OK;
+}
+
+status_t HTTPStream::send(const char *data) {
+ return send(data, strlen(data));
+}
+
+status_t HTTPStream::receive_line(char *line, size_t size) {
+ if (mState != CONNECTED) {
+ return ERROR_NOT_CONNECTED;
+ }
+
+ bool saw_CR = false;
+ size_t length = 0;
+
+ for (;;) {
+ char c;
+ ssize_t n = recv(mSocket, &c, 1, 0);
+ if (n < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+
+ disconnect();
+
+ return ERROR_IO;
+ } else if (n == 0) {
+ disconnect();
+
+ return ERROR_CONNECTION_LOST;
+ }
+
+ if (saw_CR && c == '\n') {
+ // We have a complete line.
+
+ line[length - 1] = '\0';
+ return OK;
+ }
+
+ saw_CR = (c == '\r');
+
+ assert(length + 1 < size);
+ line[length++] = c;
+ }
+}
+
+status_t HTTPStream::receive_header(int *http_status) {
+ *http_status = -1;
+ mHeaders.clear();
+
+ char line[256];
+ status_t err = receive_line(line, sizeof(line));
+ if (err != OK) {
+ return err;
+ }
+
+ mHeaders.add(string(kStatusKey), string(line));
+
+ char *spacePos = strchr(line, ' ');
+ if (spacePos == NULL) {
+ // Malformed response?
+ return UNKNOWN_ERROR;
+ }
+
+ char *status_start = spacePos + 1;
+ char *status_end = status_start;
+ while (isdigit(*status_end)) {
+ ++status_end;
+ }
+
+ if (status_end == status_start) {
+ // Malformed response, status missing?
+ return UNKNOWN_ERROR;
+ }
+
+ memmove(line, status_start, status_end - status_start);
+ line[status_end - status_start] = '\0';
+
+ long tmp = strtol(line, NULL, 10);
+ if (tmp < 0 || tmp > 999) {
+ return UNKNOWN_ERROR;
+ }
+
+ *http_status = (int)tmp;
+
+ for (;;) {
+ err = receive_line(line, sizeof(line));
+ if (err != OK) {
+ return err;
+ }
+
+ if (*line == '\0') {
+ // Empty line signals the end of the header.
+ break;
+ }
+
+ // puts(line);
+
+ char *colonPos = strchr(line, ':');
+ if (colonPos == NULL) {
+ mHeaders.add(string(line), string());
+ } else {
+ char *end_of_key = colonPos;
+ while (end_of_key > line && isspace(end_of_key[-1])) {
+ --end_of_key;
+ }
+
+ char *start_of_value = colonPos + 1;
+ while (isspace(*start_of_value)) {
+ ++start_of_value;
+ }
+
+ *end_of_key = '\0';
+
+ mHeaders.add(string(line), string(start_of_value));
+ }
+ }
+
+ return OK;
+}
+
+ssize_t HTTPStream::receive(void *data, size_t size) {
+ size_t total = 0;
+ while (total < size) {
+ ssize_t n = recv(mSocket, (char *)data + total, size - total, 0);
+
+ if (n < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+
+ disconnect();
+ return ERROR_IO;
+ } else if (n == 0) {
+ disconnect();
+
+ return ERROR_CONNECTION_LOST;
+ }
+
+ total += (size_t)n;
+ }
+
+ return (ssize_t)total;
+}
+
+bool HTTPStream::find_header_value(const string &key, string *value) const {
+ ssize_t index = mHeaders.indexOfKey(key);
+ if (index < 0) {
+ value->clear();
+ return false;
+ }
+
+ *value = mHeaders.valueAt(index);
+
+ return true;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp
new file mode 100644
index 000000000000..6b47a3808a0b
--- /dev/null
+++ b/media/libstagefright/MP3Extractor.cpp
@@ -0,0 +1,527 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MP3Extractor"
+#include <utils/Log.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MP3Extractor.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+#include <utils/String8.h>
+
+namespace android {
+
+static bool get_mp3_frame_size(
+ uint32_t header, size_t *frame_size,
+ int *out_sampling_rate = NULL, int *out_channels = NULL,
+ int *out_bitrate = NULL) {
+ *frame_size = 0;
+
+ if (out_sampling_rate) {
+ *out_sampling_rate = 0;
+ }
+
+ if (out_channels) {
+ *out_channels = 0;
+ }
+
+ if (out_bitrate) {
+ *out_bitrate = 0;
+ }
+
+ if ((header & 0xffe00000) != 0xffe00000) {
+ return false;
+ }
+
+ unsigned version = (header >> 19) & 3;
+
+ if (version == 0x01) {
+ return false;
+ }
+
+ unsigned layer = (header >> 17) & 3;
+
+ if (layer == 0x00) {
+ return false;
+ }
+
+ unsigned protection = (header >> 16) & 1;
+
+ unsigned bitrate_index = (header >> 12) & 0x0f;
+
+ if (bitrate_index == 0 || bitrate_index == 0x0f) {
+ // Disallow "free" bitrate.
+
+ LOGE("We disallow 'free' bitrate for now.");
+ return false;
+ }
+
+ unsigned sampling_rate_index = (header >> 10) & 3;
+
+ if (sampling_rate_index == 3) {
+ return false;
+ }
+
+ static const int kSamplingRateV1[] = { 44100, 48000, 32000 };
+ int sampling_rate = kSamplingRateV1[sampling_rate_index];
+ if (version == 2 /* V2 */) {
+ sampling_rate /= 2;
+ } else if (version == 0 /* V2.5 */) {
+ sampling_rate /= 4;
+ }
+
+ unsigned padding = (header >> 9) & 1;
+
+ if (layer == 3) {
+ // layer I
+
+ static const int kBitrateV1[] = {
+ 32, 64, 96, 128, 160, 192, 224, 256,
+ 288, 320, 352, 384, 416, 448
+ };
+
+ static const int kBitrateV2[] = {
+ 32, 48, 56, 64, 80, 96, 112, 128,
+ 144, 160, 176, 192, 224, 256
+ };
+
+ int bitrate =
+ (version == 3 /* V1 */)
+ ? kBitrateV1[bitrate_index - 1]
+ : kBitrateV2[bitrate_index - 1];
+
+ if (out_bitrate) {
+ *out_bitrate = bitrate;
+ }
+
+ *frame_size = (12000 * bitrate / sampling_rate + padding) * 4;
+ } else {
+ // layer II or III
+
+ static const int kBitrateV1L2[] = {
+ 32, 48, 56, 64, 80, 96, 112, 128,
+ 160, 192, 224, 256, 320, 384
+ };
+
+ static const int kBitrateV1L3[] = {
+ 32, 40, 48, 56, 64, 80, 96, 112,
+ 128, 160, 192, 224, 256, 320
+ };
+
+ static const int kBitrateV2[] = {
+ 8, 16, 24, 32, 40, 48, 56, 64,
+ 80, 96, 112, 128, 144, 160
+ };
+
+ int bitrate;
+ if (version == 3 /* V1 */) {
+ bitrate = (layer == 2 /* L2 */)
+ ? kBitrateV1L2[bitrate_index - 1]
+ : kBitrateV1L3[bitrate_index - 1];
+ } else {
+ // V2 (or 2.5)
+
+ bitrate = kBitrateV2[bitrate_index - 1];
+ }
+
+ if (out_bitrate) {
+ *out_bitrate = bitrate;
+ }
+
+ *frame_size = 144000 * bitrate / sampling_rate + padding;
+ }
+
+ if (out_sampling_rate) {
+ *out_sampling_rate = sampling_rate;
+ }
+
+ if (out_channels) {
+ int channel_mode = (header >> 6) & 3;
+
+ *out_channels = (channel_mode == 3) ? 1 : 2;
+ }
+
+ return true;
+}
+
+static bool Resync(
+ DataSource *source, uint32_t match_header,
+ off_t *inout_pos, uint32_t *out_header) {
+ // Everything must match except for
+ // protection, bitrate, padding, private bits and mode extension.
+ const uint32_t kMask = 0xfffe0ccf;
+
+ const size_t kMaxFrameSize = 4096;
+ uint8_t *buffer = new uint8_t[kMaxFrameSize];
+
+ off_t pos = *inout_pos - kMaxFrameSize;
+ size_t buffer_offset = kMaxFrameSize;
+ size_t buffer_length = kMaxFrameSize;
+ bool valid = false;
+ do {
+ if (buffer_offset + 3 >= buffer_length) {
+ if (buffer_length < kMaxFrameSize) {
+ break;
+ }
+
+ pos += buffer_offset;
+
+ if (pos >= *inout_pos + 128 * 1024) {
+ // Don't scan forever.
+ LOGV("giving up at offset %ld", pos);
+ break;
+ }
+
+ memmove(buffer, &buffer[buffer_offset], buffer_length - buffer_offset);
+ buffer_length = buffer_length - buffer_offset;
+ buffer_offset = 0;
+
+ ssize_t n = source->read_at(
+ pos, &buffer[buffer_length], kMaxFrameSize - buffer_length);
+
+ if (n <= 0) {
+ break;
+ }
+
+ buffer_length += (size_t)n;
+
+ continue;
+ }
+
+ uint32_t header = U32_AT(&buffer[buffer_offset]);
+
+ if (match_header != 0 && (header & kMask) != (match_header & kMask)) {
+ ++buffer_offset;
+ continue;
+ }
+
+ size_t frame_size;
+ int sample_rate, num_channels, bitrate;
+ if (!get_mp3_frame_size(header, &frame_size,
+ &sample_rate, &num_channels, &bitrate)) {
+ ++buffer_offset;
+ continue;
+ }
+
+ LOGV("found possible 1st frame at %ld", pos + buffer_offset);
+
+ // We found what looks like a valid frame,
+ // now find its successors.
+
+ off_t test_pos = pos + buffer_offset + frame_size;
+
+ valid = true;
+ for (int j = 0; j < 3; ++j) {
+ uint8_t tmp[4];
+ if (source->read_at(test_pos, tmp, 4) < 4) {
+ valid = false;
+ break;
+ }
+
+ uint32_t test_header = U32_AT(tmp);
+
+ LOGV("subsequent header is %08x", test_header);
+
+ if ((test_header & kMask) != (header & kMask)) {
+ valid = false;
+ break;
+ }
+
+ size_t test_frame_size;
+ if (!get_mp3_frame_size(test_header, &test_frame_size)) {
+ valid = false;
+ break;
+ }
+
+ LOGV("found subsequent frame #%d at %ld", j + 2, test_pos);
+
+ test_pos += test_frame_size;
+ }
+
+ if (valid) {
+ *inout_pos = pos + buffer_offset;
+
+ if (out_header != NULL) {
+ *out_header = header;
+ }
+ } else {
+ LOGV("no dice, no valid sequence of frames found.");
+ }
+
+ ++buffer_offset;
+
+ } while (!valid);
+
+ delete[] buffer;
+ buffer = NULL;
+
+ return valid;
+}
+
+class MP3Source : public MediaSource {
+public:
+ MP3Source(
+ const sp<MetaData> &meta, DataSource *source,
+ off_t first_frame_pos, uint32_t fixed_header);
+
+ virtual ~MP3Source();
+
+ virtual status_t start(MetaData *params = NULL);
+ virtual status_t stop();
+
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+private:
+ sp<MetaData> mMeta;
+ DataSource *mDataSource;
+ off_t mFirstFramePos;
+ uint32_t mFixedHeader;
+ off_t mCurrentPos;
+ int64_t mCurrentTimeUs;
+ bool mStarted;
+
+ MediaBufferGroup *mGroup;
+
+ MP3Source(const MP3Source &);
+ MP3Source &operator=(const MP3Source &);
+};
+
+MP3Extractor::MP3Extractor(DataSource *source)
+ : mDataSource(source),
+ mFirstFramePos(-1),
+ mFixedHeader(0) {
+ off_t pos = 0;
+ uint32_t header;
+ bool success = Resync(mDataSource, 0, &pos, &header);
+ assert(success);
+
+ if (success) {
+ mFirstFramePos = pos;
+ mFixedHeader = header;
+
+ size_t frame_size;
+ int sample_rate;
+ int num_channels;
+ int bitrate;
+ get_mp3_frame_size(
+ header, &frame_size, &sample_rate, &num_channels, &bitrate);
+
+ mMeta = new MetaData;
+
+ mMeta->setCString(kKeyMIMEType, "audio/mpeg");
+ mMeta->setInt32(kKeySampleRate, sample_rate);
+ mMeta->setInt32(kKeyBitRate, bitrate);
+ mMeta->setInt32(kKeyChannelCount, num_channels);
+
+ off_t fileSize;
+ if (mDataSource->getSize(&fileSize) == OK) {
+ mMeta->setInt32(
+ kKeyDuration,
+ 8 * (fileSize - mFirstFramePos) / bitrate);
+ mMeta->setInt32(kKeyTimeScale, 1000);
+ }
+ }
+}
+
+MP3Extractor::~MP3Extractor() {
+ delete mDataSource;
+ mDataSource = NULL;
+}
+
+status_t MP3Extractor::countTracks(int *num_tracks) {
+ *num_tracks = mFirstFramePos < 0 ? 0 : 1;
+
+ return OK;
+}
+
+status_t MP3Extractor::getTrack(int index, MediaSource **source) {
+ if (mFirstFramePos < 0 || index != 0) {
+ return ERROR_OUT_OF_RANGE;
+ }
+
+ *source = new MP3Source(
+ mMeta, mDataSource, mFirstFramePos, mFixedHeader);
+
+ return OK;
+}
+
+sp<MetaData> MP3Extractor::getTrackMetaData(int index) {
+ if (mFirstFramePos < 0 || index != 0) {
+ return NULL;
+ }
+
+ return mMeta;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+MP3Source::MP3Source(
+ const sp<MetaData> &meta, DataSource *source,
+ off_t first_frame_pos, uint32_t fixed_header)
+ : mMeta(meta),
+ mDataSource(source),
+ mFirstFramePos(first_frame_pos),
+ mFixedHeader(fixed_header),
+ mCurrentPos(0),
+ mCurrentTimeUs(0),
+ mStarted(false),
+ mGroup(NULL) {
+}
+
+MP3Source::~MP3Source() {
+ if (mStarted) {
+ stop();
+ }
+}
+
+status_t MP3Source::start(MetaData *) {
+ assert(!mStarted);
+
+ mGroup = new MediaBufferGroup;
+
+ const size_t kMaxFrameSize = 32768;
+ mGroup->add_buffer(new MediaBuffer(kMaxFrameSize));
+
+ mCurrentPos = mFirstFramePos;
+ mCurrentTimeUs = 0;
+
+ mStarted = true;
+
+ return OK;
+}
+
+status_t MP3Source::stop() {
+ assert(mStarted);
+
+ delete mGroup;
+ mGroup = NULL;
+
+ mStarted = false;
+
+ return OK;
+}
+
+sp<MetaData> MP3Source::getFormat() {
+ return mMeta;
+}
+
+status_t MP3Source::read(
+ MediaBuffer **out, const ReadOptions *options) {
+ *out = NULL;
+
+ int64_t seekTimeUs;
+ if (options != NULL && options->getSeekTo(&seekTimeUs)) {
+ int32_t bitrate;
+ if (!mMeta->findInt32(kKeyBitRate, &bitrate)) {
+ // bitrate is in kbits/sec.
+ LOGI("no bitrate");
+
+ return ERROR_UNSUPPORTED;
+ }
+
+ mCurrentTimeUs = seekTimeUs;
+ mCurrentPos = mFirstFramePos + seekTimeUs * bitrate / 1000000 * 125;
+ }
+
+ MediaBuffer *buffer;
+ status_t err = mGroup->acquire_buffer(&buffer);
+ if (err != OK) {
+ return err;
+ }
+
+ size_t frame_size;
+ for (;;) {
+ ssize_t n = mDataSource->read_at(mCurrentPos, buffer->data(), 4);
+ if (n < 4) {
+ buffer->release();
+ buffer = NULL;
+
+ return ERROR_END_OF_STREAM;
+ }
+
+ uint32_t header = U32_AT((const uint8_t *)buffer->data());
+
+ if (get_mp3_frame_size(header, &frame_size)) {
+ break;
+ }
+
+ // Lost sync.
+ LOGW("lost sync!\n");
+
+ off_t pos = mCurrentPos;
+ if (!Resync(mDataSource, mFixedHeader, &pos, NULL)) {
+ LOGE("Unable to resync. Signalling end of stream.");
+
+ buffer->release();
+ buffer = NULL;
+
+ return ERROR_END_OF_STREAM;
+ }
+
+ mCurrentPos = pos;
+
+ // Try again with the new position.
+ }
+
+ assert(frame_size <= buffer->size());
+
+ ssize_t n = mDataSource->read_at(mCurrentPos, buffer->data(), frame_size);
+ if (n < (ssize_t)frame_size) {
+ buffer->release();
+ buffer = NULL;
+
+ return ERROR_END_OF_STREAM;
+ }
+
+ buffer->set_range(0, frame_size);
+
+ buffer->meta_data()->setInt32(kKeyTimeUnits, mCurrentTimeUs / 1000);
+ buffer->meta_data()->setInt32(kKeyTimeScale, 1000);
+
+ mCurrentPos += frame_size;
+ mCurrentTimeUs += 1152 * 1000000 / 44100;
+
+ *out = buffer;
+
+ return OK;
+}
+
+bool SniffMP3(DataSource *source, String8 *mimeType, float *confidence) {
+ off_t pos = 0;
+ uint32_t header;
+ if (!Resync(source, 0, &pos, &header)) {
+ return false;
+ }
+
+ *mimeType = "audio/mpeg";
+ *confidence = 0.3f;
+
+ return true;
+}
+
+} // namespace android
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
new file mode 100644
index 000000000000..caaec06af32d
--- /dev/null
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -0,0 +1,937 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "MPEG4Extractor"
+#include <utils/Log.h>
+
+#include <arpa/inet.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <ctype.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MPEG4Extractor.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/SampleTable.h>
+#include <media/stagefright/Utils.h>
+#include <utils/String8.h>
+
+namespace android {
+
+class MPEG4Source : public MediaSource {
+public:
+ // Caller retains ownership of both "dataSource" and "sampleTable".
+ MPEG4Source(const sp<MetaData> &format, DataSource *dataSource,
+ SampleTable *sampleTable);
+
+ virtual ~MPEG4Source();
+
+ virtual status_t start(MetaData *params = NULL);
+ virtual status_t stop();
+
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+private:
+ sp<MetaData> mFormat;
+ DataSource *mDataSource;
+ int32_t mTimescale;
+ SampleTable *mSampleTable;
+ uint32_t mCurrentSampleIndex;
+
+ bool mIsAVC;
+ bool mStarted;
+
+ MediaBufferGroup *mGroup;
+
+ MediaBuffer *mBuffer;
+ size_t mBufferOffset;
+ size_t mBufferSizeRemaining;
+
+ bool mNeedsNALFraming;
+
+ MPEG4Source(const MPEG4Source &);
+ MPEG4Source &operator=(const MPEG4Source &);
+};
+
+static void hexdump(const void *_data, size_t size) {
+ const uint8_t *data = (const uint8_t *)_data;
+ size_t offset = 0;
+ while (offset < size) {
+ printf("0x%04x ", offset);
+
+ size_t n = size - offset;
+ if (n > 16) {
+ n = 16;
+ }
+
+ for (size_t i = 0; i < 16; ++i) {
+ if (i == 8) {
+ printf(" ");
+ }
+
+ if (offset + i < size) {
+ printf("%02x ", data[offset + i]);
+ } else {
+ printf(" ");
+ }
+ }
+
+ printf(" ");
+
+ for (size_t i = 0; i < n; ++i) {
+ if (isprint(data[offset + i])) {
+ printf("%c", data[offset + i]);
+ } else {
+ printf(".");
+ }
+ }
+
+ printf("\n");
+
+ offset += 16;
+ }
+}
+
+static const char *const FourCC2MIME(uint32_t fourcc) {
+ switch (fourcc) {
+ case FOURCC('m', 'p', '4', 'a'):
+ return "audio/mp4a-latm";
+
+ case FOURCC('s', 'a', 'm', 'r'):
+ return "audio/3gpp";
+
+ case FOURCC('m', 'p', '4', 'v'):
+ return "video/mp4v-es";
+
+ case FOURCC('s', '2', '6', '3'):
+ return "video/3gpp";
+
+ case FOURCC('a', 'v', 'c', '1'):
+ return "video/avc";
+
+ default:
+ assert(!"should not be here.");
+ return NULL;
+ }
+}
+
+MPEG4Extractor::MPEG4Extractor(DataSource *source)
+ : mDataSource(source),
+ mHaveMetadata(false),
+ mFirstTrack(NULL),
+ mLastTrack(NULL) {
+}
+
+MPEG4Extractor::~MPEG4Extractor() {
+ Track *track = mFirstTrack;
+ while (track) {
+ Track *next = track->next;
+
+ delete track->sampleTable;
+ track->sampleTable = NULL;
+
+ delete track;
+ track = next;
+ }
+ mFirstTrack = mLastTrack = NULL;
+
+ delete mDataSource;
+ mDataSource = NULL;
+}
+
+status_t MPEG4Extractor::countTracks(int *num_tracks) {
+ status_t err;
+ if ((err = readMetaData()) != OK) {
+ return err;
+ }
+
+ *num_tracks = 0;
+ Track *track = mFirstTrack;
+ while (track) {
+ ++*num_tracks;
+ track = track->next;
+ }
+
+ return OK;
+}
+
+sp<MetaData> MPEG4Extractor::getTrackMetaData(int index) {
+ if (index < 0) {
+ return NULL;
+ }
+
+ status_t err;
+ if ((err = readMetaData()) != OK) {
+ return NULL;
+ }
+
+ Track *track = mFirstTrack;
+ while (index > 0) {
+ if (track == NULL) {
+ return NULL;
+ }
+
+ track = track->next;
+ --index;
+ }
+
+ return track->meta;
+}
+
+status_t MPEG4Extractor::readMetaData() {
+ if (mHaveMetadata) {
+ return OK;
+ }
+
+ off_t offset = 0;
+ status_t err;
+ while ((err = parseChunk(&offset, 0)) == OK) {
+ }
+
+ if (mHaveMetadata) {
+ return OK;
+ }
+
+ return err;
+}
+
+static void MakeFourCCString(uint32_t x, char *s) {
+ s[0] = x >> 24;
+ s[1] = (x >> 16) & 0xff;
+ s[2] = (x >> 8) & 0xff;
+ s[3] = x & 0xff;
+ s[4] = '\0';
+}
+
+status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
+ uint32_t hdr[2];
+ if (mDataSource->read_at(*offset, hdr, 8) < 8) {
+ return ERROR_IO;
+ }
+ uint64_t chunk_size = ntohl(hdr[0]);
+ uint32_t chunk_type = ntohl(hdr[1]);
+ off_t data_offset = *offset + 8;
+
+ if (chunk_size == 1) {
+ if (mDataSource->read_at(*offset + 8, &chunk_size, 8) < 8) {
+ return ERROR_IO;
+ }
+ chunk_size = ntoh64(chunk_size);
+ data_offset += 8;
+ }
+
+ char chunk[5];
+ MakeFourCCString(chunk_type, chunk);
+
+#if 0
+ static const char kWhitespace[] = " ";
+ const char *indent = &kWhitespace[sizeof(kWhitespace) - 1 - 2 * depth];
+ printf("%sfound chunk '%s' of size %lld\n", indent, chunk, chunk_size);
+
+ char buffer[256];
+ if (chunk_size <= sizeof(buffer)) {
+ if (mDataSource->read_at(*offset, buffer, chunk_size) < chunk_size) {
+ return ERROR_IO;
+ }
+
+ hexdump(buffer, chunk_size);
+ }
+#endif
+
+ off_t chunk_data_size = *offset + chunk_size - data_offset;
+
+ switch(chunk_type) {
+ case FOURCC('m', 'o', 'o', 'v'):
+ case FOURCC('t', 'r', 'a', 'k'):
+ case FOURCC('m', 'd', 'i', 'a'):
+ case FOURCC('m', 'i', 'n', 'f'):
+ case FOURCC('d', 'i', 'n', 'f'):
+ case FOURCC('s', 't', 'b', 'l'):
+ case FOURCC('m', 'v', 'e', 'x'):
+ case FOURCC('m', 'o', 'o', 'f'):
+ case FOURCC('t', 'r', 'a', 'f'):
+ case FOURCC('m', 'f', 'r', 'a'):
+ case FOURCC('s', 'k', 'i' ,'p'):
+ {
+ off_t stop_offset = *offset + chunk_size;
+ *offset = data_offset;
+ while (*offset < stop_offset) {
+ status_t err = parseChunk(offset, depth + 1);
+ if (err != OK) {
+ return err;
+ }
+ }
+ assert(*offset == stop_offset);
+
+ if (chunk_type == FOURCC('m', 'o', 'o', 'v')) {
+ mHaveMetadata = true;
+
+ return UNKNOWN_ERROR; // Return a dummy error.
+ }
+ break;
+ }
+
+ case FOURCC('t', 'k', 'h', 'd'):
+ {
+ assert(chunk_data_size >= 4);
+
+ uint8_t version;
+ if (mDataSource->read_at(data_offset, &version, 1) < 1) {
+ return ERROR_IO;
+ }
+
+ uint64_t ctime, mtime, duration;
+ int32_t id;
+ uint32_t width, height;
+
+ if (version == 1) {
+ if (chunk_data_size != 36 + 60) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t buffer[36 + 60];
+ if (mDataSource->read_at(
+ data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
+ return ERROR_IO;
+ }
+
+ ctime = U64_AT(&buffer[4]);
+ mtime = U64_AT(&buffer[12]);
+ id = U32_AT(&buffer[20]);
+ duration = U64_AT(&buffer[28]);
+ width = U32_AT(&buffer[88]);
+ height = U32_AT(&buffer[92]);
+ } else if (version == 0) {
+ if (chunk_data_size != 24 + 60) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t buffer[24 + 60];
+ if (mDataSource->read_at(
+ data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
+ return ERROR_IO;
+ }
+ ctime = U32_AT(&buffer[4]);
+ mtime = U32_AT(&buffer[8]);
+ id = U32_AT(&buffer[12]);
+ duration = U32_AT(&buffer[20]);
+ width = U32_AT(&buffer[76]);
+ height = U32_AT(&buffer[80]);
+ }
+
+ Track *track = new Track;
+ track->next = NULL;
+ if (mLastTrack) {
+ mLastTrack->next = track;
+ } else {
+ mFirstTrack = track;
+ }
+ mLastTrack = track;
+
+ track->meta = new MetaData;
+ track->timescale = 0;
+ track->sampleTable = new SampleTable(mDataSource);
+ track->meta->setCString(kKeyMIMEType, "application/octet-stream");
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('m', 'd', 'h', 'd'):
+ {
+ if (chunk_data_size < 4) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t version;
+ if (mDataSource->read_at(
+ data_offset, &version, sizeof(version))
+ < (ssize_t)sizeof(version)) {
+ return ERROR_IO;
+ }
+
+ off_t timescale_offset;
+
+ if (version == 1) {
+ timescale_offset = data_offset + 4 + 16;
+ } else if (version == 0) {
+ timescale_offset = data_offset + 4 + 8;
+ } else {
+ return ERROR_IO;
+ }
+
+ uint32_t timescale;
+ if (mDataSource->read_at(
+ timescale_offset, &timescale, sizeof(timescale))
+ < (ssize_t)sizeof(timescale)) {
+ return ERROR_IO;
+ }
+
+ mLastTrack->timescale = ntohl(timescale);
+ mLastTrack->meta->setInt32(kKeyTimeScale, mLastTrack->timescale);
+
+ int64_t duration;
+ if (version == 1) {
+ if (mDataSource->read_at(
+ timescale_offset + 4, &duration, sizeof(duration))
+ < (ssize_t)sizeof(duration)) {
+ return ERROR_IO;
+ }
+ duration = ntoh64(duration);
+ } else {
+ int32_t duration32;
+ if (mDataSource->read_at(
+ timescale_offset + 4, &duration32, sizeof(duration32))
+ < (ssize_t)sizeof(duration32)) {
+ return ERROR_IO;
+ }
+ duration = ntohl(duration32);
+ }
+ mLastTrack->meta->setInt32(kKeyDuration, duration);
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('h', 'd', 'l', 'r'):
+ {
+ if (chunk_data_size < 25) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t buffer[24];
+ if (mDataSource->read_at(data_offset, buffer, 24) < 24) {
+ return ERROR_IO;
+ }
+
+ if (U32_AT(buffer) != 0) {
+ // Should be version 0, flags 0.
+ return ERROR_MALFORMED;
+ }
+
+ if (U32_AT(&buffer[4]) != 0) {
+ // pre_defined should be 0.
+ return ERROR_MALFORMED;
+ }
+
+ mHandlerType = U32_AT(&buffer[8]);
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('s', 't', 's', 'd'):
+ {
+ if (chunk_data_size < 8) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t buffer[8];
+ assert(chunk_data_size >= (off_t)sizeof(buffer));
+ if (mDataSource->read_at(
+ data_offset, buffer, 8) < 8) {
+ return ERROR_IO;
+ }
+
+ if (U32_AT(buffer) != 0) {
+ // Should be version 0, flags 0.
+ return ERROR_MALFORMED;
+ }
+
+ uint32_t entry_count = U32_AT(&buffer[4]);
+
+ if (entry_count > 1) {
+ // For now we only support a single type of media per track.
+ return ERROR_UNSUPPORTED;
+ }
+
+ off_t stop_offset = *offset + chunk_size;
+ *offset = data_offset + 8;
+ for (uint32_t i = 0; i < entry_count; ++i) {
+ status_t err = parseChunk(offset, depth + 1);
+ if (err != OK) {
+ return err;
+ }
+ }
+ assert(*offset == stop_offset);
+ break;
+ }
+
+ case FOURCC('m', 'p', '4', 'a'):
+ case FOURCC('s', 'a', 'm', 'r'):
+ {
+ if (mHandlerType != FOURCC('s', 'o', 'u', 'n')) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t buffer[8 + 20];
+ if (chunk_data_size < (ssize_t)sizeof(buffer)) {
+ // Basic AudioSampleEntry size.
+ return ERROR_MALFORMED;
+ }
+
+ if (mDataSource->read_at(
+ data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
+ return ERROR_IO;
+ }
+
+ uint16_t data_ref_index = U16_AT(&buffer[6]);
+ uint16_t num_channels = U16_AT(&buffer[16]);
+
+ if (!strcasecmp("audio/3gpp", FourCC2MIME(chunk_type))) {
+ // AMR audio is always mono.
+ num_channels = 1;
+ }
+
+ uint16_t sample_size = U16_AT(&buffer[18]);
+ uint32_t sample_rate = U32_AT(&buffer[24]) >> 16;
+
+ printf("*** coding='%s' %d channels, size %d, rate %d\n",
+ chunk, num_channels, sample_size, sample_rate);
+
+ mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
+ mLastTrack->meta->setInt32(kKeyChannelCount, num_channels);
+ mLastTrack->meta->setInt32(kKeySampleRate, sample_rate);
+
+ off_t stop_offset = *offset + chunk_size;
+ *offset = data_offset + sizeof(buffer);
+ while (*offset < stop_offset) {
+ status_t err = parseChunk(offset, depth + 1);
+ if (err != OK) {
+ return err;
+ }
+ }
+ assert(*offset == stop_offset);
+ break;
+ }
+
+ case FOURCC('m', 'p', '4', 'v'):
+ case FOURCC('s', '2', '6', '3'):
+ case FOURCC('a', 'v', 'c', '1'):
+ {
+ if (mHandlerType != FOURCC('v', 'i', 'd', 'e')) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t buffer[78];
+ if (chunk_data_size < (ssize_t)sizeof(buffer)) {
+ // Basic VideoSampleEntry size.
+ return ERROR_MALFORMED;
+ }
+
+ if (mDataSource->read_at(
+ data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
+ return ERROR_IO;
+ }
+
+ uint16_t data_ref_index = U16_AT(&buffer[6]);
+ uint16_t width = U16_AT(&buffer[6 + 18]);
+ uint16_t height = U16_AT(&buffer[6 + 20]);
+
+ printf("*** coding='%s' width=%d height=%d\n",
+ chunk, width, height);
+
+ mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
+ mLastTrack->meta->setInt32(kKeyWidth, width);
+ mLastTrack->meta->setInt32(kKeyHeight, height);
+
+ off_t stop_offset = *offset + chunk_size;
+ *offset = data_offset + sizeof(buffer);
+ while (*offset < stop_offset) {
+ status_t err = parseChunk(offset, depth + 1);
+ if (err != OK) {
+ return err;
+ }
+ }
+ assert(*offset == stop_offset);
+ break;
+ }
+
+ case FOURCC('s', 't', 'c', 'o'):
+ case FOURCC('c', 'o', '6', '4'):
+ {
+ status_t err =
+ mLastTrack->sampleTable->setChunkOffsetParams(
+ chunk_type, data_offset, chunk_data_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('s', 't', 's', 'c'):
+ {
+ status_t err =
+ mLastTrack->sampleTable->setSampleToChunkParams(
+ data_offset, chunk_data_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('s', 't', 's', 'z'):
+ case FOURCC('s', 't', 'z', '2'):
+ {
+ status_t err =
+ mLastTrack->sampleTable->setSampleSizeParams(
+ chunk_type, data_offset, chunk_data_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('s', 't', 't', 's'):
+ {
+ status_t err =
+ mLastTrack->sampleTable->setTimeToSampleParams(
+ data_offset, chunk_data_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('s', 't', 's', 's'):
+ {
+ status_t err =
+ mLastTrack->sampleTable->setSyncSampleParams(
+ data_offset, chunk_data_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('e', 's', 'd', 's'):
+ {
+ if (chunk_data_size < 4) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t buffer[256];
+ if (chunk_data_size > (off_t)sizeof(buffer)) {
+ return ERROR_BUFFER_TOO_SMALL;
+ }
+
+ if (mDataSource->read_at(
+ data_offset, buffer, chunk_data_size) < chunk_data_size) {
+ return ERROR_IO;
+ }
+
+ if (U32_AT(buffer) != 0) {
+ // Should be version 0, flags 0.
+ return ERROR_MALFORMED;
+ }
+
+ mLastTrack->meta->setData(
+ kKeyESDS, kTypeESDS, &buffer[4], chunk_data_size - 4);
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('a', 'v', 'c', 'C'):
+ {
+ char buffer[256];
+ if (chunk_data_size > (off_t)sizeof(buffer)) {
+ return ERROR_BUFFER_TOO_SMALL;
+ }
+
+ if (mDataSource->read_at(
+ data_offset, buffer, chunk_data_size) < chunk_data_size) {
+ return ERROR_IO;
+ }
+
+ mLastTrack->meta->setData(
+ kKeyAVCC, kTypeAVCC, buffer, chunk_data_size);
+
+ *offset += chunk_size;
+ break;
+ }
+
+ default:
+ {
+ *offset += chunk_size;
+ break;
+ }
+ }
+
+ return OK;
+}
+
+status_t MPEG4Extractor::getTrack(int index, MediaSource **source) {
+ *source = NULL;
+
+ if (index < 0) {
+ return ERROR_OUT_OF_RANGE;
+ }
+
+ status_t err;
+ if ((err = readMetaData()) != OK) {
+ return err;
+ }
+
+ Track *track = mFirstTrack;
+ while (index > 0) {
+ if (track == NULL) {
+ return ERROR_OUT_OF_RANGE;
+ }
+
+ track = track->next;
+ --index;
+ }
+
+ *source = new MPEG4Source(
+ track->meta, mDataSource, track->sampleTable);
+
+ return OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+MPEG4Source::MPEG4Source(
+ const sp<MetaData> &format,
+ DataSource *dataSource, SampleTable *sampleTable)
+ : mFormat(format),
+ mDataSource(dataSource),
+ mTimescale(0),
+ mSampleTable(sampleTable),
+ mCurrentSampleIndex(0),
+ mIsAVC(false),
+ mStarted(false),
+ mGroup(NULL),
+ mBuffer(NULL),
+ mBufferOffset(0),
+ mBufferSizeRemaining(0),
+ mNeedsNALFraming(false) {
+ const char *mime;
+ bool success = mFormat->findCString(kKeyMIMEType, &mime);
+ assert(success);
+
+ success = mFormat->findInt32(kKeyTimeScale, &mTimescale);
+ assert(success);
+
+ mIsAVC = !strcasecmp(mime, "video/avc");
+}
+
+MPEG4Source::~MPEG4Source() {
+ if (mStarted) {
+ stop();
+ }
+}
+
+status_t MPEG4Source::start(MetaData *params) {
+ assert(!mStarted);
+
+ int32_t val;
+ if (mIsAVC && params && params->findInt32(kKeyNeedsNALFraming, &val)
+ && val != 0) {
+ mNeedsNALFraming = true;
+ } else {
+ mNeedsNALFraming = false;
+ }
+
+ mGroup = new MediaBufferGroup;
+
+ size_t max_size;
+ status_t err = mSampleTable->getMaxSampleSize(&max_size);
+ assert(err == OK);
+
+ // Add padding for de-framing of AVC content just in case.
+ mGroup->add_buffer(new MediaBuffer(max_size + 2));
+
+ mStarted = true;
+
+ return OK;
+}
+
+status_t MPEG4Source::stop() {
+ assert(mStarted);
+
+ if (mBuffer != NULL) {
+ mBuffer->release();
+ mBuffer = NULL;
+ }
+
+ delete mGroup;
+ mGroup = NULL;
+
+ mStarted = false;
+ mCurrentSampleIndex = 0;
+
+ return OK;
+}
+
+sp<MetaData> MPEG4Source::getFormat() {
+ return mFormat;
+}
+
+status_t MPEG4Source::read(
+ MediaBuffer **out, const ReadOptions *options) {
+ assert(mStarted);
+
+ *out = NULL;
+
+ int64_t seekTimeUs;
+ if (options && options->getSeekTo(&seekTimeUs)) {
+ uint32_t sampleIndex;
+ status_t err = mSampleTable->findClosestSample(
+ seekTimeUs * mTimescale / 1000000,
+ &sampleIndex, SampleTable::kSyncSample_Flag);
+
+ if (err != OK) {
+ return err;
+ }
+
+ mCurrentSampleIndex = sampleIndex;
+ if (mBuffer != NULL) {
+ mBuffer->release();
+ mBuffer = NULL;
+ }
+
+ // fall through
+ }
+
+ if (mBuffer == NULL) {
+ off_t offset;
+ size_t size;
+ status_t err = mSampleTable->getSampleOffsetAndSize(
+ mCurrentSampleIndex, &offset, &size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ uint32_t dts;
+ err = mSampleTable->getDecodingTime(mCurrentSampleIndex, &dts);
+
+ if (err != OK) {
+ return err;
+ }
+
+ err = mGroup->acquire_buffer(&mBuffer);
+ if (err != OK) {
+ assert(mBuffer == NULL);
+ return err;
+ }
+
+ assert(mBuffer->size() + 2 >= size);
+
+ ssize_t num_bytes_read =
+ mDataSource->read_at(offset, (uint8_t *)mBuffer->data() + 2, size);
+
+ if (num_bytes_read < (ssize_t)size) {
+ mBuffer->release();
+ mBuffer = NULL;
+
+ return err;
+ }
+
+ mBuffer->set_range(2, size);
+ mBuffer->meta_data()->clear();
+ mBuffer->meta_data()->setInt32(kKeyTimeUnits, dts);
+ mBuffer->meta_data()->setInt32(kKeyTimeScale, mTimescale);
+
+ ++mCurrentSampleIndex;
+
+ mBufferOffset = 2;
+ mBufferSizeRemaining = size;
+ }
+
+ if (!mIsAVC) {
+ *out = mBuffer;
+ mBuffer = NULL;
+
+ return OK;
+ }
+
+ uint8_t *data = (uint8_t *)mBuffer->data() + mBufferOffset;
+ assert(mBufferSizeRemaining >= 2);
+
+ size_t nal_length = (data[0] << 8) | data[1];
+ assert(mBufferSizeRemaining >= 2 + nal_length);
+
+ if (mNeedsNALFraming) {
+ // Insert marker.
+ data[-2] = data[-1] = data[0] = 0;
+ data[1] = 1;
+
+ mBuffer->set_range(mBufferOffset - 2, nal_length + 4);
+ } else {
+ mBuffer->set_range(mBufferOffset + 2, nal_length);
+ }
+
+ mBufferOffset += nal_length + 2;
+ mBufferSizeRemaining -= nal_length + 2;
+
+ if (mBufferSizeRemaining > 0) {
+ *out = mBuffer->clone();
+ } else {
+ *out = mBuffer;
+ mBuffer = NULL;
+ }
+
+ return OK;
+}
+
+bool SniffMPEG4(DataSource *source, String8 *mimeType, float *confidence) {
+ uint8_t header[8];
+
+ ssize_t n = source->read_at(4, header, sizeof(header));
+ if (n < (ssize_t)sizeof(header)) {
+ return false;
+ }
+
+ if (!memcmp(header, "ftyp3gp", 7) || !memcmp(header, "ftypmp42", 8)
+ || !memcmp(header, "ftypisom", 8) || !memcmp(header, "ftypM4V ", 8)) {
+ *mimeType = "video/mp4";
+ *confidence = 0.1;
+
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
new file mode 100644
index 000000000000..6bdf282179f3
--- /dev/null
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -0,0 +1,641 @@
+/*
+ * 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.
+ */
+
+#include <arpa/inet.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <ctype.h>
+#include <pthread.h>
+
+#include <media/stagefright/MPEG4Writer.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+class MPEG4Writer::Track {
+public:
+ Track(MPEG4Writer *owner, const sp<MetaData> &meta, MediaSource *source);
+ ~Track();
+
+ void start();
+ void stop();
+
+ int64_t getDuration() const;
+ void writeTrackHeader(int32_t trackID);
+
+private:
+ MPEG4Writer *mOwner;
+ sp<MetaData> mMeta;
+ MediaSource *mSource;
+ volatile bool mDone;
+
+ pthread_t mThread;
+
+ struct SampleInfo {
+ size_t size;
+ off_t offset;
+ int64_t timestamp;
+ };
+ List<SampleInfo> mSampleInfos;
+
+ void *mCodecSpecificData;
+ size_t mCodecSpecificDataSize;
+
+ static void *ThreadWrapper(void *me);
+ void threadEntry();
+
+ Track(const Track &);
+ Track &operator=(const Track &);
+};
+
+MPEG4Writer::MPEG4Writer(const char *filename)
+ : mFile(fopen(filename, "wb")),
+ mOffset(0),
+ mMdatOffset(0) {
+ assert(mFile != NULL);
+}
+
+MPEG4Writer::~MPEG4Writer() {
+ stop();
+
+ for (List<Track *>::iterator it = mTracks.begin();
+ it != mTracks.end(); ++it) {
+ delete *it;
+ }
+ mTracks.clear();
+}
+
+void MPEG4Writer::addSource(const sp<MetaData> &meta, MediaSource *source) {
+ Track *track = new Track(this, meta, source);
+ mTracks.push_back(track);
+}
+
+void MPEG4Writer::start() {
+ if (mFile == NULL) {
+ return;
+ }
+
+ beginBox("ftyp");
+ writeFourcc("isom");
+ writeInt32(0);
+ writeFourcc("isom");
+ endBox();
+
+ mMdatOffset = mOffset;
+ write("\x00\x00\x00\x01mdat????????", 16);
+
+ for (List<Track *>::iterator it = mTracks.begin();
+ it != mTracks.end(); ++it) {
+ (*it)->start();
+ }
+}
+
+void MPEG4Writer::stop() {
+ if (mFile == NULL) {
+ return;
+ }
+
+ int64_t max_duration = 0;
+ for (List<Track *>::iterator it = mTracks.begin();
+ it != mTracks.end(); ++it) {
+ (*it)->stop();
+
+ int64_t duration = (*it)->getDuration();
+ if (duration > max_duration) {
+ max_duration = duration;
+ }
+ }
+
+ // Fix up the size of the 'mdat' chunk.
+ fseek(mFile, mMdatOffset + 8, SEEK_SET);
+ int64_t size = mOffset - mMdatOffset;
+ size = hton64(size);
+ fwrite(&size, 1, 8, mFile);
+ fseek(mFile, mOffset, SEEK_SET);
+
+ time_t now = time(NULL);
+
+ beginBox("moov");
+
+ beginBox("mvhd");
+ writeInt32(0); // version=0, flags=0
+ writeInt32(now); // creation time
+ writeInt32(now); // modification time
+ writeInt32(1000); // timescale
+ writeInt32(max_duration);
+ writeInt32(0x10000); // rate
+ writeInt16(0x100); // volume
+ writeInt16(0); // reserved
+ writeInt32(0); // reserved
+ writeInt32(0); // reserved
+ writeInt32(0x10000); // matrix
+ writeInt32(0);
+ writeInt32(0);
+ writeInt32(0);
+ writeInt32(0x10000);
+ writeInt32(0);
+ writeInt32(0);
+ writeInt32(0);
+ writeInt32(0x40000000);
+ writeInt32(0); // predefined
+ writeInt32(0); // predefined
+ writeInt32(0); // predefined
+ writeInt32(0); // predefined
+ writeInt32(0); // predefined
+ writeInt32(0); // predefined
+ writeInt32(mTracks.size() + 1); // nextTrackID
+ endBox(); // mvhd
+
+ int32_t id = 1;
+ for (List<Track *>::iterator it = mTracks.begin();
+ it != mTracks.end(); ++it, ++id) {
+ (*it)->writeTrackHeader(id);
+ }
+ endBox(); // moov
+
+ assert(mBoxes.empty());
+
+ fclose(mFile);
+ mFile = NULL;
+}
+
+off_t MPEG4Writer::addSample(MediaBuffer *buffer) {
+ Mutex::Autolock autoLock(mLock);
+
+ off_t old_offset = mOffset;
+
+ fwrite((const uint8_t *)buffer->data() + buffer->range_offset(),
+ 1, buffer->range_length(), mFile);
+
+ mOffset += buffer->range_length();
+
+ return old_offset;
+}
+
+void MPEG4Writer::beginBox(const char *fourcc) {
+ assert(strlen(fourcc) == 4);
+
+ mBoxes.push_back(mOffset);
+
+ writeInt32(0);
+ writeFourcc(fourcc);
+}
+
+void MPEG4Writer::endBox() {
+ assert(!mBoxes.empty());
+
+ off_t offset = *--mBoxes.end();
+ mBoxes.erase(--mBoxes.end());
+
+ fseek(mFile, offset, SEEK_SET);
+ writeInt32(mOffset - offset);
+ mOffset -= 4;
+ fseek(mFile, mOffset, SEEK_SET);
+}
+
+void MPEG4Writer::writeInt8(int8_t x) {
+ fwrite(&x, 1, 1, mFile);
+ ++mOffset;
+}
+
+void MPEG4Writer::writeInt16(int16_t x) {
+ x = htons(x);
+ fwrite(&x, 1, 2, mFile);
+ mOffset += 2;
+}
+
+void MPEG4Writer::writeInt32(int32_t x) {
+ x = htonl(x);
+ fwrite(&x, 1, 4, mFile);
+ mOffset += 4;
+}
+
+void MPEG4Writer::writeInt64(int64_t x) {
+ x = hton64(x);
+ fwrite(&x, 1, 8, mFile);
+ mOffset += 8;
+}
+
+void MPEG4Writer::writeCString(const char *s) {
+ size_t n = strlen(s);
+
+ fwrite(s, 1, n + 1, mFile);
+ mOffset += n + 1;
+}
+
+void MPEG4Writer::writeFourcc(const char *s) {
+ assert(strlen(s) == 4);
+ fwrite(s, 1, 4, mFile);
+ mOffset += 4;
+}
+
+void MPEG4Writer::write(const void *data, size_t size) {
+ fwrite(data, 1, size, mFile);
+ mOffset += size;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+MPEG4Writer::Track::Track(
+ MPEG4Writer *owner, const sp<MetaData> &meta, MediaSource *source)
+ : mOwner(owner),
+ mMeta(meta),
+ mSource(source),
+ mDone(false),
+ mCodecSpecificData(NULL),
+ mCodecSpecificDataSize(0) {
+}
+
+MPEG4Writer::Track::~Track() {
+ stop();
+
+ if (mCodecSpecificData != NULL) {
+ free(mCodecSpecificData);
+ mCodecSpecificData = NULL;
+ }
+}
+
+void MPEG4Writer::Track::start() {
+ mSource->start();
+
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+ mDone = false;
+
+ int err = pthread_create(&mThread, &attr, ThreadWrapper, this);
+ assert(err == 0);
+
+ pthread_attr_destroy(&attr);
+}
+
+void MPEG4Writer::Track::stop() {
+ if (mDone) {
+ return;
+ }
+
+ mDone = true;
+
+ void *dummy;
+ pthread_join(mThread, &dummy);
+
+ mSource->stop();
+}
+
+// static
+void *MPEG4Writer::Track::ThreadWrapper(void *me) {
+ Track *track = static_cast<Track *>(me);
+
+ track->threadEntry();
+
+ return NULL;
+}
+
+void MPEG4Writer::Track::threadEntry() {
+ bool is_mpeg4 = false;
+ sp<MetaData> meta = mSource->getFormat();
+ const char *mime;
+ meta->findCString(kKeyMIMEType, &mime);
+ is_mpeg4 = !strcasecmp(mime, "video/mp4v-es");
+
+ MediaBuffer *buffer;
+ while (!mDone && mSource->read(&buffer) == OK) {
+ if (buffer->range_length() == 0) {
+ buffer->release();
+ buffer = NULL;
+
+ continue;
+ }
+
+ if (mCodecSpecificData == NULL && is_mpeg4) {
+ const uint8_t *data =
+ (const uint8_t *)buffer->data() + buffer->range_offset();
+
+ const size_t size = buffer->range_length();
+
+ size_t offset = 0;
+ while (offset + 3 < size) {
+ if (data[offset] == 0x00 && data[offset + 1] == 0x00
+ && data[offset + 2] == 0x01 && data[offset + 3] == 0xb6) {
+ break;
+ }
+
+ ++offset;
+ }
+
+ assert(offset + 3 < size);
+
+ mCodecSpecificDataSize = offset;
+ mCodecSpecificData = malloc(offset);
+ memcpy(mCodecSpecificData, data, offset);
+
+ buffer->set_range(buffer->range_offset() + offset, size - offset);
+ }
+
+ off_t offset = mOwner->addSample(buffer);
+
+ SampleInfo info;
+ info.size = buffer->range_length();
+ info.offset = offset;
+
+ int32_t units, scale;
+ bool success =
+ buffer->meta_data()->findInt32(kKeyTimeUnits, &units);
+ assert(success);
+ success =
+ buffer->meta_data()->findInt32(kKeyTimeScale, &scale);
+ assert(success);
+
+ info.timestamp = (int64_t)units * 1000 / scale;
+
+ mSampleInfos.push_back(info);
+
+ buffer->release();
+ buffer = NULL;
+ }
+}
+
+int64_t MPEG4Writer::Track::getDuration() const {
+ return 10000; // XXX
+}
+
+void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) {
+ const char *mime;
+ bool success = mMeta->findCString(kKeyMIMEType, &mime);
+ assert(success);
+
+ bool is_audio = !strncasecmp(mime, "audio/", 6);
+
+ time_t now = time(NULL);
+
+ mOwner->beginBox("trak");
+
+ mOwner->beginBox("tkhd");
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt32(now); // creation time
+ mOwner->writeInt32(now); // modification time
+ mOwner->writeInt32(trackID);
+ mOwner->writeInt32(0); // reserved
+ mOwner->writeInt32(getDuration());
+ mOwner->writeInt32(0); // reserved
+ mOwner->writeInt32(0); // reserved
+ mOwner->writeInt16(0); // layer
+ mOwner->writeInt16(0); // alternate group
+ mOwner->writeInt16(is_audio ? 0x100 : 0); // volume
+ mOwner->writeInt16(0); // reserved
+
+ mOwner->writeInt32(0x10000); // matrix
+ mOwner->writeInt32(0);
+ mOwner->writeInt32(0);
+ mOwner->writeInt32(0);
+ mOwner->writeInt32(0x10000);
+ mOwner->writeInt32(0);
+ mOwner->writeInt32(0);
+ mOwner->writeInt32(0);
+ mOwner->writeInt32(0x40000000);
+
+ if (is_audio) {
+ mOwner->writeInt32(0);
+ mOwner->writeInt32(0);
+ } else {
+ int32_t width, height;
+ bool success = mMeta->findInt32(kKeyWidth, &width);
+ success = success && mMeta->findInt32(kKeyHeight, &height);
+ assert(success);
+
+ mOwner->writeInt32(width);
+ mOwner->writeInt32(height);
+ }
+ mOwner->endBox(); // tkhd
+
+ mOwner->beginBox("mdia");
+
+ mOwner->beginBox("mdhd");
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt32(now); // creation time
+ mOwner->writeInt32(now); // modification time
+ mOwner->writeInt32(1000); // timescale
+ mOwner->writeInt32(getDuration());
+ mOwner->writeInt16(0); // language code XXX
+ mOwner->writeInt16(0); // predefined
+ mOwner->endBox();
+
+ mOwner->beginBox("hdlr");
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt32(0); // predefined
+ mOwner->writeFourcc(is_audio ? "soun" : "vide");
+ mOwner->writeInt32(0); // reserved
+ mOwner->writeInt32(0); // reserved
+ mOwner->writeInt32(0); // reserved
+ mOwner->writeCString(""); // name
+ mOwner->endBox();
+
+ mOwner->beginBox("minf");
+
+ mOwner->beginBox("dinf");
+ mOwner->beginBox("dref");
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt32(1);
+ mOwner->beginBox("url ");
+ mOwner->writeInt32(1); // version=0, flags=1
+ mOwner->endBox(); // url
+ mOwner->endBox(); // dref
+ mOwner->endBox(); // dinf
+
+ if (is_audio) {
+ mOwner->beginBox("smhd");
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt16(0); // balance
+ mOwner->writeInt16(0); // reserved
+ mOwner->endBox();
+ } else {
+ mOwner->beginBox("vmhd");
+ mOwner->writeInt32(0x00000001); // version=0, flags=1
+ mOwner->writeInt16(0); // graphics mode
+ mOwner->writeInt16(0); // opcolor
+ mOwner->writeInt16(0);
+ mOwner->writeInt16(0);
+ mOwner->endBox();
+ }
+ mOwner->endBox(); // minf
+
+ mOwner->beginBox("stbl");
+
+ mOwner->beginBox("stsd");
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt32(1); // entry count
+ if (is_audio) {
+ mOwner->beginBox("xxxx"); // audio format XXX
+ mOwner->writeInt32(0); // reserved
+ mOwner->writeInt16(0); // reserved
+ mOwner->writeInt16(0); // data ref index
+ mOwner->writeInt32(0); // reserved
+ mOwner->writeInt32(0); // reserved
+ mOwner->writeInt16(2); // channel count
+ mOwner->writeInt16(16); // sample size
+ mOwner->writeInt16(0); // predefined
+ mOwner->writeInt16(0); // reserved
+
+ int32_t samplerate;
+ bool success = mMeta->findInt32(kKeySampleRate, &samplerate);
+ assert(success);
+
+ mOwner->writeInt32(samplerate << 16);
+ mOwner->endBox();
+ } else {
+ if (!strcasecmp("video/mp4v-es", mime)) {
+ mOwner->beginBox("mp4v");
+ } else if (!strcasecmp("video/3gpp", mime)) {
+ mOwner->beginBox("s263");
+ } else {
+ assert(!"should not be here, unknown mime type.");
+ }
+
+ mOwner->writeInt32(0); // reserved
+ mOwner->writeInt16(0); // reserved
+ mOwner->writeInt16(0); // data ref index
+ mOwner->writeInt16(0); // predefined
+ mOwner->writeInt16(0); // reserved
+ mOwner->writeInt32(0); // predefined
+ mOwner->writeInt32(0); // predefined
+ mOwner->writeInt32(0); // predefined
+
+ int32_t width, height;
+ bool success = mMeta->findInt32(kKeyWidth, &width);
+ success = success && mMeta->findInt32(kKeyHeight, &height);
+ assert(success);
+
+ mOwner->writeInt16(width);
+ mOwner->writeInt16(height);
+ mOwner->writeInt32(0x480000); // horiz resolution
+ mOwner->writeInt32(0x480000); // vert resolution
+ mOwner->writeInt32(0); // reserved
+ mOwner->writeInt16(1); // frame count
+ mOwner->write(" ", 32);
+ mOwner->writeInt16(0x18); // depth
+ mOwner->writeInt16(-1); // predefined
+
+ assert(23 + mCodecSpecificDataSize < 128);
+
+ if (!strcasecmp("video/mp4v-es", mime)) {
+ mOwner->beginBox("esds");
+
+ mOwner->writeInt32(0); // version=0, flags=0
+
+ mOwner->writeInt8(0x03); // ES_DescrTag
+ mOwner->writeInt8(23 + mCodecSpecificDataSize);
+ mOwner->writeInt16(0x0000); // ES_ID
+ mOwner->writeInt8(0x1f);
+
+ mOwner->writeInt8(0x04); // DecoderConfigDescrTag
+ mOwner->writeInt8(15 + mCodecSpecificDataSize);
+ mOwner->writeInt8(0x20); // objectTypeIndication ISO/IEC 14492-2
+ mOwner->writeInt8(0x11); // streamType VisualStream
+
+ static const uint8_t kData[] = {
+ 0x01, 0x77, 0x00,
+ 0x00, 0x03, 0xe8, 0x00,
+ 0x00, 0x03, 0xe8, 0x00
+ };
+ mOwner->write(kData, sizeof(kData));
+
+ mOwner->writeInt8(0x05); // DecoderSpecificInfoTag
+
+ mOwner->writeInt8(mCodecSpecificDataSize);
+ mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
+
+ static const uint8_t kData2[] = {
+ 0x06, // SLConfigDescriptorTag
+ 0x01,
+ 0x02
+ };
+ mOwner->write(kData2, sizeof(kData2));
+
+ mOwner->endBox(); // esds
+ } else if (!strcasecmp("video/3gpp", mime)) {
+ mOwner->beginBox("d263");
+
+ mOwner->writeInt32(0); // vendor
+ mOwner->writeInt8(0); // decoder version
+ mOwner->writeInt8(10); // level: 10
+ mOwner->writeInt8(0); // profile: 0
+
+ mOwner->endBox(); // d263
+ }
+ mOwner->endBox(); // mp4v or s263
+ }
+ mOwner->endBox(); // stsd
+
+ mOwner->beginBox("stts");
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt32(mSampleInfos.size() - 1);
+
+ List<SampleInfo>::iterator it = mSampleInfos.begin();
+ int64_t last = (*it).timestamp;
+ ++it;
+ while (it != mSampleInfos.end()) {
+ mOwner->writeInt32(1);
+ mOwner->writeInt32((*it).timestamp - last);
+
+ last = (*it).timestamp;
+
+ ++it;
+ }
+ mOwner->endBox(); // stts
+
+ mOwner->beginBox("stsz");
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt32(0); // default sample size
+ mOwner->writeInt32(mSampleInfos.size());
+ for (List<SampleInfo>::iterator it = mSampleInfos.begin();
+ it != mSampleInfos.end(); ++it) {
+ mOwner->writeInt32((*it).size);
+ }
+ mOwner->endBox(); // stsz
+
+ mOwner->beginBox("stsc");
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt32(mSampleInfos.size());
+ int32_t n = 1;
+ for (List<SampleInfo>::iterator it = mSampleInfos.begin();
+ it != mSampleInfos.end(); ++it, ++n) {
+ mOwner->writeInt32(n);
+ mOwner->writeInt32(1);
+ mOwner->writeInt32(1);
+ }
+ mOwner->endBox(); // stsc
+
+ mOwner->beginBox("co64");
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt32(mSampleInfos.size());
+ for (List<SampleInfo>::iterator it = mSampleInfos.begin();
+ it != mSampleInfos.end(); ++it, ++n) {
+ mOwner->writeInt64((*it).offset);
+ }
+ mOwner->endBox(); // co64
+
+ mOwner->endBox(); // stbl
+ mOwner->endBox(); // mdia
+ mOwner->endBox(); // trak
+}
+
+} // namespace android
diff --git a/media/libstagefright/MediaBuffer.cpp b/media/libstagefright/MediaBuffer.cpp
new file mode 100644
index 000000000000..cd78dbd7c0bc
--- /dev/null
+++ b/media/libstagefright/MediaBuffer.cpp
@@ -0,0 +1,174 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "MediaBuffer"
+#include <utils/Log.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MetaData.h>
+
+namespace android {
+
+// XXX make this truly atomic.
+static int atomic_add(int *value, int delta) {
+ int prev_value = *value;
+ *value += delta;
+
+ return prev_value;
+}
+
+MediaBuffer::MediaBuffer(void *data, size_t size)
+ : mObserver(NULL),
+ mNextBuffer(NULL),
+ mRefCount(0),
+ mData(data),
+ mSize(size),
+ mRangeOffset(0),
+ mRangeLength(size),
+ mOwnsData(false),
+ mMetaData(new MetaData),
+ mOriginal(NULL) {
+}
+
+MediaBuffer::MediaBuffer(size_t size)
+ : mObserver(NULL),
+ mNextBuffer(NULL),
+ mRefCount(0),
+ mData(malloc(size)),
+ mSize(size),
+ mRangeOffset(0),
+ mRangeLength(size),
+ mOwnsData(true),
+ mMetaData(new MetaData),
+ mOriginal(NULL) {
+}
+
+void MediaBuffer::release() {
+ if (mObserver == NULL) {
+ assert(mRefCount == 0);
+ delete this;
+ return;
+ }
+
+ int prevCount = atomic_add(&mRefCount, -1);
+ if (prevCount == 1) {
+ if (mObserver == NULL) {
+ delete this;
+ return;
+ }
+
+ mObserver->signalBufferReturned(this);
+ }
+ assert(prevCount > 0);
+}
+
+void MediaBuffer::claim() {
+ assert(mObserver != NULL);
+ assert(mRefCount == 1);
+
+ mRefCount = 0;
+}
+
+void MediaBuffer::add_ref() {
+ atomic_add(&mRefCount, 1);
+}
+
+void *MediaBuffer::data() const {
+ return mData;
+}
+
+size_t MediaBuffer::size() const {
+ return mSize;
+}
+
+size_t MediaBuffer::range_offset() const {
+ return mRangeOffset;
+}
+
+size_t MediaBuffer::range_length() const {
+ return mRangeLength;
+}
+
+void MediaBuffer::set_range(size_t offset, size_t length) {
+ if (offset < 0 || offset + length > mSize) {
+ LOGE("offset = %d, length = %d, mSize = %d", offset, length, mSize);
+ }
+ assert(offset >= 0 && offset + length <= mSize);
+
+ mRangeOffset = offset;
+ mRangeLength = length;
+}
+
+sp<MetaData> MediaBuffer::meta_data() {
+ return mMetaData;
+}
+
+void MediaBuffer::reset() {
+ mMetaData->clear();
+ set_range(0, mSize);
+}
+
+MediaBuffer::~MediaBuffer() {
+ assert(mObserver == NULL);
+
+ if (mOwnsData && mData != NULL) {
+ free(mData);
+ mData = NULL;
+ }
+
+ if (mOriginal != NULL) {
+ mOriginal->release();
+ mOriginal = NULL;
+ }
+}
+
+void MediaBuffer::setObserver(MediaBufferObserver *observer) {
+ assert(observer == NULL || mObserver == NULL);
+ mObserver = observer;
+}
+
+void MediaBuffer::setNextBuffer(MediaBuffer *buffer) {
+ mNextBuffer = buffer;
+}
+
+MediaBuffer *MediaBuffer::nextBuffer() {
+ return mNextBuffer;
+}
+
+int MediaBuffer::refcount() const {
+ return mRefCount;
+}
+
+MediaBuffer *MediaBuffer::clone() {
+ MediaBuffer *buffer = new MediaBuffer(mData, mSize);
+ buffer->set_range(mRangeOffset, mRangeLength);
+ buffer->mMetaData = new MetaData(*mMetaData.get());
+
+ add_ref();
+ buffer->mOriginal = this;
+
+ return buffer;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/MediaBufferGroup.cpp b/media/libstagefright/MediaBufferGroup.cpp
new file mode 100644
index 000000000000..aec7722df6a0
--- /dev/null
+++ b/media/libstagefright/MediaBufferGroup.cpp
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "MediaBufferGroup"
+#include <utils/Log.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferGroup.h>
+
+namespace android {
+
+MediaBufferGroup::MediaBufferGroup()
+ : mFirstBuffer(NULL),
+ mLastBuffer(NULL) {
+}
+
+MediaBufferGroup::~MediaBufferGroup() {
+ MediaBuffer *next;
+ for (MediaBuffer *buffer = mFirstBuffer; buffer != NULL;
+ buffer = next) {
+ next = buffer->nextBuffer();
+
+ assert(buffer->refcount() == 0);
+
+ buffer->setObserver(NULL);
+ buffer->release();
+ }
+}
+
+void MediaBufferGroup::add_buffer(MediaBuffer *buffer) {
+ Mutex::Autolock autoLock(mLock);
+
+ buffer->setObserver(this);
+
+ if (mLastBuffer) {
+ mLastBuffer->setNextBuffer(buffer);
+ } else {
+ mFirstBuffer = buffer;
+ }
+
+ mLastBuffer = buffer;
+}
+
+status_t MediaBufferGroup::acquire_buffer(MediaBuffer **out) {
+ Mutex::Autolock autoLock(mLock);
+
+ for (;;) {
+ for (MediaBuffer *buffer = mFirstBuffer;
+ buffer != NULL; buffer = buffer->nextBuffer()) {
+ if (buffer->refcount() == 0) {
+ buffer->add_ref();
+ buffer->reset();
+
+ *out = buffer;
+ goto exit;
+ }
+ }
+
+ // All buffers are in use. Block until one of them is returned to us.
+ mCondition.wait(mLock);
+ }
+
+exit:
+ return OK;
+}
+
+void MediaBufferGroup::signalBufferReturned(MediaBuffer *) {
+ Mutex::Autolock autoLock(mLock);
+ mCondition.signal();
+}
+
+} // namespace android
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
new file mode 100644
index 000000000000..bc667946ee5c
--- /dev/null
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaExtractor"
+#include <utils/Log.h>
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MP3Extractor.h>
+#include <media/stagefright/MPEG4Extractor.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <utils/String8.h>
+
+namespace android {
+
+// static
+MediaExtractor *MediaExtractor::Create(DataSource *source, const char *mime) {
+ String8 tmp;
+ if (mime == NULL) {
+ float confidence;
+ if (!source->sniff(&tmp, &confidence)) {
+ LOGE("FAILED to autodetect media content.");
+
+ return NULL;
+ }
+
+ mime = tmp.string();
+ LOGI("Autodetected media content as '%s' with confidence %.2f",
+ mime, confidence);
+ }
+
+ if (!strcasecmp(mime, "video/mp4") || !strcasecmp(mime, "audio/mp4")) {
+ return new MPEG4Extractor(source);
+ } else if (!strcasecmp(mime, "audio/mpeg")) {
+ return new MP3Extractor(source);
+ }
+
+ return NULL;
+}
+
+} // namespace android
diff --git a/media/libstagefright/MediaPlayerImpl.cpp b/media/libstagefright/MediaPlayerImpl.cpp
new file mode 100644
index 000000000000..78fcdee16a8f
--- /dev/null
+++ b/media/libstagefright/MediaPlayerImpl.cpp
@@ -0,0 +1,693 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaPlayerImpl"
+#include "utils/Log.h"
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <OMX_Component.h>
+
+#include <unistd.h>
+
+#include <media/stagefright/AudioPlayer.h>
+#include <media/stagefright/CachingDataSource.h>
+// #include <media/stagefright/CameraSource.h>
+#include <media/stagefright/HTTPDataSource.h>
+#include <media/stagefright/HTTPStream.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MediaPlayerImpl.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MmapSource.h>
+#include <media/stagefright/OMXDecoder.h>
+#include <media/stagefright/QComHardwareRenderer.h>
+#include <media/stagefright/ShoutcastSource.h>
+#include <media/stagefright/SoftwareRenderer.h>
+#include <media/stagefright/SurfaceRenderer.h>
+#include <media/stagefright/TimeSource.h>
+#include <ui/PixelFormat.h>
+#include <ui/Surface.h>
+
+namespace android {
+
+MediaPlayerImpl::MediaPlayerImpl(const char *uri)
+ : mInitCheck(NO_INIT),
+ mExtractor(NULL),
+ mTimeSource(NULL),
+ mAudioSource(NULL),
+ mAudioDecoder(NULL),
+ mAudioPlayer(NULL),
+ mVideoSource(NULL),
+ mVideoDecoder(NULL),
+ mVideoWidth(0),
+ mVideoHeight(0),
+ mVideoPosition(0),
+ mDuration(0),
+ mPlaying(false),
+ mPaused(false),
+ mRenderer(NULL),
+ mSeeking(false),
+ mFrameSize(0),
+ mUseSoftwareColorConversion(false) {
+ LOGI("MediaPlayerImpl(%s)", uri);
+ DataSource::RegisterDefaultSniffers();
+
+ status_t err = mClient.connect();
+ if (err != OK) {
+ LOGE("Failed to connect to OMXClient.");
+ return;
+ }
+
+ if (!strncasecmp("shoutcast://", uri, 12)) {
+ setAudioSource(makeShoutcastSource(uri));
+#if 0
+ } else if (!strncasecmp("camera:", uri, 7)) {
+ mVideoWidth = 480;
+ mVideoHeight = 320;
+ mVideoDecoder = CameraSource::Create();
+#endif
+ } else {
+ DataSource *source = NULL;
+ if (!strncasecmp("file://", uri, 7)) {
+ source = new MmapSource(uri + 7);
+ } else if (!strncasecmp("http://", uri, 7)) {
+ source = new HTTPDataSource(uri);
+ source = new CachingDataSource(source, 64 * 1024, 10);
+ } else {
+ // Assume it's a filename.
+ source = new MmapSource(uri);
+ }
+
+ mExtractor = MediaExtractor::Create(source);
+
+ if (mExtractor == NULL) {
+ return;
+ }
+ }
+
+ init();
+
+ mInitCheck = OK;
+}
+
+MediaPlayerImpl::MediaPlayerImpl(int fd, int64_t offset, int64_t length)
+ : mInitCheck(NO_INIT),
+ mExtractor(NULL),
+ mTimeSource(NULL),
+ mAudioSource(NULL),
+ mAudioDecoder(NULL),
+ mAudioPlayer(NULL),
+ mVideoSource(NULL),
+ mVideoDecoder(NULL),
+ mVideoWidth(0),
+ mVideoHeight(0),
+ mVideoPosition(0),
+ mDuration(0),
+ mPlaying(false),
+ mPaused(false),
+ mRenderer(NULL),
+ mSeeking(false),
+ mFrameSize(0),
+ mUseSoftwareColorConversion(false) {
+ LOGI("MediaPlayerImpl(%d, %lld, %lld)", fd, offset, length);
+ DataSource::RegisterDefaultSniffers();
+
+ status_t err = mClient.connect();
+ if (err != OK) {
+ LOGE("Failed to connect to OMXClient.");
+ return;
+ }
+
+ mExtractor = MediaExtractor::Create(
+ new MmapSource(fd, offset, length));
+
+ if (mExtractor == NULL) {
+ return;
+ }
+
+ init();
+
+ mInitCheck = OK;
+}
+
+status_t MediaPlayerImpl::initCheck() const {
+ return mInitCheck;
+}
+
+MediaPlayerImpl::~MediaPlayerImpl() {
+ stop();
+ setSurface(NULL);
+
+ LOGV("Shutting down audio.");
+ delete mAudioDecoder;
+ mAudioDecoder = NULL;
+
+ delete mAudioSource;
+ mAudioSource = NULL;
+
+ LOGV("Shutting down video.");
+ delete mVideoDecoder;
+ mVideoDecoder = NULL;
+
+ delete mVideoSource;
+ mVideoSource = NULL;
+
+ delete mExtractor;
+ mExtractor = NULL;
+
+ if (mInitCheck == OK) {
+ mClient.disconnect();
+ }
+
+ LOGV("~MediaPlayerImpl done.");
+}
+
+void MediaPlayerImpl::play() {
+ LOGI("play");
+
+ if (mPlaying) {
+ if (mPaused) {
+ if (mAudioSource != NULL) {
+ mAudioPlayer->resume();
+ }
+ mPaused = false;
+ }
+ return;
+ }
+
+ mPlaying = true;
+
+ if (mAudioSource != NULL) {
+ mAudioPlayer = new AudioPlayer(mAudioSink);
+ mAudioPlayer->setSource(mAudioDecoder);
+ mAudioPlayer->start();
+ mTimeSource = mAudioPlayer;
+ } else {
+ mTimeSource = new SystemTimeSource;
+ }
+
+ if (mVideoDecoder != NULL) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+ pthread_create(&mVideoThread, &attr, VideoWrapper, this);
+
+ pthread_attr_destroy(&attr);
+ }
+}
+
+void MediaPlayerImpl::pause() {
+ if (!mPlaying || mPaused) {
+ return;
+ }
+
+ if (mAudioSource != NULL) {
+ mAudioPlayer->pause();
+ }
+
+ mPaused = true;
+}
+
+void MediaPlayerImpl::stop() {
+ if (!mPlaying) {
+ return;
+ }
+
+ mPlaying = false;
+
+ if (mVideoDecoder != NULL) {
+ void *dummy;
+ pthread_join(mVideoThread, &dummy);
+ }
+
+ if (mAudioSource != NULL) {
+ mAudioPlayer->stop();
+
+ delete mAudioPlayer;
+ mAudioPlayer = NULL;
+ } else {
+ delete mTimeSource;
+ }
+
+ mTimeSource = NULL;
+}
+
+// static
+void *MediaPlayerImpl::VideoWrapper(void *me) {
+ ((MediaPlayerImpl *)me)->videoEntry();
+
+ return NULL;
+}
+
+void MediaPlayerImpl::videoEntry() {
+ bool firstFrame = true;
+ bool eof = false;
+
+ status_t err = mVideoDecoder->start();
+ assert(err == OK);
+
+ while (mPlaying) {
+ MediaBuffer *buffer;
+
+ MediaSource::ReadOptions options;
+ bool seeking = false;
+
+ {
+ Mutex::Autolock autoLock(mLock);
+ if (mSeeking) {
+ LOGI("seek-options to %lld", mSeekTimeUs);
+ options.setSeekTo(mSeekTimeUs);
+
+ mSeeking = false;
+ seeking = true;
+ eof = false;
+ }
+ }
+
+ if (eof || mPaused) {
+ usleep(100000);
+ continue;
+ }
+
+ status_t err = mVideoDecoder->read(&buffer, &options);
+ assert((err == OK && buffer != NULL) || (err != OK && buffer == NULL));
+
+ if (err == ERROR_END_OF_STREAM || err != OK) {
+ eof = true;
+ continue;
+ }
+
+ if (buffer->range_length() == 0) {
+ // The final buffer is empty.
+ buffer->release();
+ continue;
+ }
+
+ int32_t units, scale;
+ bool success =
+ buffer->meta_data()->findInt32(kKeyTimeUnits, &units);
+ assert(success);
+ success =
+ buffer->meta_data()->findInt32(kKeyTimeScale, &scale);
+ assert(success);
+
+ int64_t pts_us = (int64_t)units * 1000000 / scale;
+ {
+ Mutex::Autolock autoLock(mLock);
+ mVideoPosition = pts_us;
+ }
+
+ if (seeking && mAudioPlayer != NULL) {
+ // Now that we know where exactly video seeked (taking sync-samples
+ // into account), we will seek the audio track to the same time.
+ mAudioPlayer->seekTo(pts_us);
+ }
+
+ if (firstFrame || seeking) {
+ mTimeSourceDeltaUs = mTimeSource->getRealTimeUs() - pts_us;
+ firstFrame = false;
+ }
+
+ displayOrDiscardFrame(buffer, pts_us);
+ }
+
+ mVideoDecoder->stop();
+}
+
+void MediaPlayerImpl::displayOrDiscardFrame(
+ MediaBuffer *buffer, int64_t pts_us) {
+ for (;;) {
+ if (!mPlaying || mPaused) {
+ buffer->release();
+ buffer = NULL;
+
+ return;
+ }
+
+ int64_t realtime_us, mediatime_us;
+ if (mAudioPlayer != NULL
+ && mAudioPlayer->getMediaTimeMapping(&realtime_us, &mediatime_us)) {
+ mTimeSourceDeltaUs = realtime_us - mediatime_us;
+ }
+
+ int64_t now_us = mTimeSource->getRealTimeUs();
+ now_us -= mTimeSourceDeltaUs;
+
+ int64_t delay_us = pts_us - now_us;
+
+ if (delay_us < -15000) {
+ // We're late.
+
+ LOGI("we're late by %lld ms, dropping a frame\n",
+ -delay_us / 1000);
+
+ buffer->release();
+ buffer = NULL;
+ return;
+ } else if (delay_us > 100000) {
+ LOGI("we're much too early (by %lld ms)\n",
+ delay_us / 1000);
+ usleep(100000);
+ continue;
+ } else if (delay_us > 0) {
+ usleep(delay_us);
+ }
+
+ break;
+ }
+
+ {
+ Mutex::Autolock autoLock(mLock);
+ if (mRenderer != NULL) {
+ sendFrameToISurface(buffer);
+ }
+ }
+
+ buffer->release();
+ buffer = NULL;
+}
+
+void MediaPlayerImpl::init() {
+ if (mExtractor != NULL) {
+ int num_tracks;
+ assert(mExtractor->countTracks(&num_tracks) == OK);
+
+ mDuration = 0;
+
+ for (int i = 0; i < num_tracks; ++i) {
+ const sp<MetaData> meta = mExtractor->getTrackMetaData(i);
+ assert(meta != NULL);
+
+ const char *mime;
+ if (!meta->findCString(kKeyMIMEType, &mime)) {
+ continue;
+ }
+
+ bool is_audio = false;
+ bool is_acceptable = false;
+ if (!strncasecmp(mime, "audio/", 6)) {
+ is_audio = true;
+ is_acceptable = (mAudioSource == NULL);
+ } else if (!strncasecmp(mime, "video/", 6)) {
+ is_acceptable = (mVideoSource == NULL);
+ }
+
+ if (!is_acceptable) {
+ continue;
+ }
+
+ MediaSource *source;
+ if (mExtractor->getTrack(i, &source) != OK) {
+ continue;
+ }
+
+ int32_t units, scale;
+ if (meta->findInt32(kKeyDuration, &units)
+ && meta->findInt32(kKeyTimeScale, &scale)) {
+ int64_t duration_us = (int64_t)units * 1000000 / scale;
+ if (duration_us > mDuration) {
+ mDuration = duration_us;
+ }
+ }
+
+ if (is_audio) {
+ setAudioSource(source);
+ } else {
+ setVideoSource(source);
+ }
+ }
+ }
+}
+
+void MediaPlayerImpl::setAudioSource(MediaSource *source) {
+ mAudioSource = source;
+
+ sp<MetaData> meta = source->getFormat();
+
+ mAudioDecoder = OMXDecoder::Create(&mClient, meta);
+ mAudioDecoder->setSource(source);
+}
+
+void MediaPlayerImpl::setVideoSource(MediaSource *source) {
+ LOGI("setVideoSource");
+ mVideoSource = source;
+
+ sp<MetaData> meta = source->getFormat();
+
+ bool success = meta->findInt32(kKeyWidth, &mVideoWidth);
+ assert(success);
+
+ success = meta->findInt32(kKeyHeight, &mVideoHeight);
+ assert(success);
+
+ mVideoDecoder = OMXDecoder::Create(&mClient, meta);
+ ((OMXDecoder *)mVideoDecoder)->setSource(source);
+
+ if (mISurface.get() != NULL || mSurface.get() != NULL) {
+ depopulateISurface();
+ populateISurface();
+ }
+}
+
+void MediaPlayerImpl::setSurface(const sp<Surface> &surface) {
+ LOGI("setSurface %p", surface.get());
+ Mutex::Autolock autoLock(mLock);
+
+ depopulateISurface();
+
+ mSurface = surface;
+ mISurface = NULL;
+
+ if (mSurface.get() != NULL) {
+ populateISurface();
+ }
+}
+
+void MediaPlayerImpl::setISurface(const sp<ISurface> &isurface) {
+ LOGI("setISurface %p", isurface.get());
+ Mutex::Autolock autoLock(mLock);
+
+ depopulateISurface();
+
+ mSurface = NULL;
+ mISurface = isurface;
+
+ if (mISurface.get() != NULL) {
+ populateISurface();
+ }
+}
+
+MediaSource *MediaPlayerImpl::makeShoutcastSource(const char *uri) {
+ if (strncasecmp(uri, "shoutcast://", 12)) {
+ return NULL;
+ }
+
+ string host;
+ string path;
+ int port;
+
+ char *slash = strchr(uri + 12, '/');
+ if (slash == NULL) {
+ host = uri + 12;
+ path = "/";
+ } else {
+ host = string(uri + 12, slash - (uri + 12));
+ path = slash;
+ }
+
+ char *colon = strchr(host.c_str(), ':');
+ if (colon == NULL) {
+ port = 80;
+ } else {
+ char *end;
+ long tmp = strtol(colon + 1, &end, 10);
+ assert(end > colon + 1);
+ assert(tmp > 0 && tmp < 65536);
+ port = tmp;
+
+ host = string(host, 0, colon - host.c_str());
+ }
+
+ LOGI("Connecting to host '%s', port %d, path '%s'",
+ host.c_str(), port, path.c_str());
+
+ HTTPStream *http = new HTTPStream;
+ int http_status;
+
+ for (;;) {
+ status_t err = http->connect(host.c_str(), port);
+ assert(err == OK);
+
+ err = http->send("GET ");
+ err = http->send(path.c_str());
+ err = http->send(" HTTP/1.1\r\n");
+ err = http->send("Host: ");
+ err = http->send(host.c_str());
+ err = http->send("\r\n");
+ err = http->send("Icy-MetaData: 1\r\n\r\n");
+
+ assert(OK == http->receive_header(&http_status));
+
+ if (http_status == 301 || http_status == 302) {
+ string location;
+ assert(http->find_header_value("Location", &location));
+
+ assert(string(location, 0, 7) == "http://");
+ location.erase(0, 7);
+ string::size_type slashPos = location.find('/');
+ if (slashPos == string::npos) {
+ slashPos = location.size();
+ location += '/';
+ }
+
+ http->disconnect();
+
+ LOGI("Redirecting to %s\n", location.c_str());
+
+ host = string(location, 0, slashPos);
+
+ string::size_type colonPos = host.find(':');
+ if (colonPos != string::npos) {
+ const char *start = host.c_str() + colonPos + 1;
+ char *end;
+ long tmp = strtol(start, &end, 10);
+ assert(end > start && *end == '\0');
+
+ port = (tmp >= 0 && tmp < 65536) ? (int)tmp : 80;
+ } else {
+ port = 80;
+ }
+
+ path = string(location, slashPos);
+
+ continue;
+ }
+
+ break;
+ }
+
+ if (http_status != 200) {
+ LOGE("Connection failed: http_status = %d", http_status);
+ return NULL;
+ }
+
+ MediaSource *source = new ShoutcastSource(http);
+
+ return source;
+}
+
+bool MediaPlayerImpl::isPlaying() const {
+ return mPlaying && !mPaused;
+}
+
+int64_t MediaPlayerImpl::getDuration() {
+ return mDuration;
+}
+
+int64_t MediaPlayerImpl::getPosition() {
+ int64_t position = 0;
+ if (mVideoSource != NULL) {
+ Mutex::Autolock autoLock(mLock);
+ position = mVideoPosition;
+ } else if (mAudioPlayer != NULL) {
+ position = mAudioPlayer->getMediaTimeUs();
+ }
+
+ return position;
+}
+
+status_t MediaPlayerImpl::seekTo(int64_t time) {
+ LOGI("seekTo %lld", time);
+
+ if (mPaused) {
+ return UNKNOWN_ERROR;
+ }
+
+ if (mVideoSource == NULL && mAudioPlayer != NULL) {
+ mAudioPlayer->seekTo(time);
+ } else {
+ Mutex::Autolock autoLock(mLock);
+ mSeekTimeUs = time;
+ mSeeking = true;
+ }
+
+ return OK;
+}
+
+void MediaPlayerImpl::populateISurface() {
+ if (mVideoSource == NULL) {
+ return;
+ }
+
+ sp<MetaData> meta = mVideoDecoder->getFormat();
+
+ int32_t format;
+ const char *component;
+ int32_t decodedWidth, decodedHeight;
+ bool success = meta->findInt32(kKeyColorFormat, &format);
+ success = success && meta->findCString(kKeyDecoderComponent, &component);
+ success = success && meta->findInt32(kKeyWidth, &decodedWidth);
+ success = success && meta->findInt32(kKeyHeight, &decodedHeight);
+ assert(success);
+
+ if (mSurface.get() != NULL) {
+ mRenderer =
+ new SurfaceRenderer(
+ mSurface, mVideoWidth, mVideoHeight,
+ decodedWidth, decodedHeight);
+ } else if (format == OMX_COLOR_FormatYUV420Planar
+ && !strncasecmp(component, "OMX.qcom.video.decoder.", 23)) {
+ mRenderer =
+ new QComHardwareRenderer(
+ mISurface, mVideoWidth, mVideoHeight,
+ decodedWidth, decodedHeight);
+ } else {
+ LOGW("Using software renderer.");
+ mRenderer = new SoftwareRenderer(
+ mISurface, mVideoWidth, mVideoHeight,
+ decodedWidth, decodedHeight);
+ }
+}
+
+void MediaPlayerImpl::depopulateISurface() {
+ delete mRenderer;
+ mRenderer = NULL;
+}
+
+void MediaPlayerImpl::sendFrameToISurface(MediaBuffer *buffer) {
+ void *platformPrivate;
+ if (!buffer->meta_data()->findPointer(
+ kKeyPlatformPrivate, &platformPrivate)) {
+ platformPrivate = NULL;
+ }
+
+ mRenderer->render(
+ (const uint8_t *)buffer->data() + buffer->range_offset(),
+ buffer->range_length(),
+ platformPrivate);
+}
+
+void MediaPlayerImpl::setAudioSink(
+ const sp<MediaPlayerBase::AudioSink> &audioSink) {
+ LOGI("setAudioSink %p", audioSink.get());
+ mAudioSink = audioSink;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/MediaSource.cpp b/media/libstagefright/MediaSource.cpp
new file mode 100644
index 000000000000..ec89b7442597
--- /dev/null
+++ b/media/libstagefright/MediaSource.cpp
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#include <media/stagefright/MediaSource.h>
+
+namespace android {
+
+MediaSource::MediaSource() {}
+
+MediaSource::~MediaSource() {}
+
+////////////////////////////////////////////////////////////////////////////////
+
+MediaSource::ReadOptions::ReadOptions() {
+ reset();
+}
+
+void MediaSource::ReadOptions::reset() {
+ mOptions = 0;
+ mSeekTimeUs = 0;
+ mLatenessUs = 0;
+}
+
+void MediaSource::ReadOptions::setSeekTo(int64_t time_us) {
+ mOptions |= kSeekTo_Option;
+ mSeekTimeUs = time_us;
+}
+
+void MediaSource::ReadOptions::clearSeekTo() {
+ mOptions &= ~kSeekTo_Option;
+ mSeekTimeUs = 0;
+}
+
+bool MediaSource::ReadOptions::getSeekTo(int64_t *time_us) const {
+ *time_us = mSeekTimeUs;
+ return (mOptions & kSeekTo_Option) != 0;
+}
+
+void MediaSource::ReadOptions::setLateBy(int64_t lateness_us) {
+ mLatenessUs = lateness_us;
+}
+
+int64_t MediaSource::ReadOptions::getLateBy() const {
+ return mLatenessUs;
+}
+
+} // namespace android
diff --git a/media/libstagefright/MetaData.cpp b/media/libstagefright/MetaData.cpp
new file mode 100644
index 000000000000..5d23732be80e
--- /dev/null
+++ b/media/libstagefright/MetaData.cpp
@@ -0,0 +1,232 @@
+/*
+ * 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.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <media/stagefright/MetaData.h>
+
+namespace android {
+
+MetaData::MetaData() {
+}
+
+MetaData::MetaData(const MetaData &from)
+ : RefBase(),
+ mItems(from.mItems) {
+}
+
+MetaData::~MetaData() {
+ clear();
+}
+
+void MetaData::clear() {
+ mItems.clear();
+}
+
+bool MetaData::remove(uint32_t key) {
+ ssize_t i = mItems.indexOfKey(key);
+
+ if (i < 0) {
+ return false;
+ }
+
+ mItems.removeItemsAt(i);
+
+ return true;
+}
+
+bool MetaData::setCString(uint32_t key, const char *value) {
+ return setData(key, TYPE_C_STRING, value, strlen(value) + 1);
+}
+
+bool MetaData::setInt32(uint32_t key, int32_t value) {
+ return setData(key, TYPE_INT32, &value, sizeof(value));
+}
+
+bool MetaData::setFloat(uint32_t key, float value) {
+ return setData(key, TYPE_FLOAT, &value, sizeof(value));
+}
+
+bool MetaData::setPointer(uint32_t key, void *value) {
+ return setData(key, TYPE_POINTER, &value, sizeof(value));
+}
+
+bool MetaData::findCString(uint32_t key, const char **value) {
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (!findData(key, &type, &data, &size) || type != TYPE_C_STRING) {
+ return false;
+ }
+
+ *value = (const char *)data;
+
+ return true;
+}
+
+bool MetaData::findInt32(uint32_t key, int32_t *value) {
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (!findData(key, &type, &data, &size) || type != TYPE_INT32) {
+ return false;
+ }
+
+ assert(size == sizeof(*value));
+
+ *value = *(int32_t *)data;
+
+ return true;
+}
+
+bool MetaData::findFloat(uint32_t key, float *value) {
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (!findData(key, &type, &data, &size) || type != TYPE_FLOAT) {
+ return false;
+ }
+
+ assert(size == sizeof(*value));
+
+ *value = *(float *)data;
+
+ return true;
+}
+
+bool MetaData::findPointer(uint32_t key, void **value) {
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (!findData(key, &type, &data, &size) || type != TYPE_POINTER) {
+ return false;
+ }
+
+ assert(size == sizeof(*value));
+
+ *value = *(void **)data;
+
+ return true;
+}
+
+bool MetaData::setData(
+ uint32_t key, uint32_t type, const void *data, size_t size) {
+ bool overwrote_existing = true;
+
+ ssize_t i = mItems.indexOfKey(key);
+ if (i < 0) {
+ typed_data item;
+ i = mItems.add(key, item);
+
+ overwrote_existing = false;
+ }
+
+ typed_data &item = mItems.editValueAt(i);
+
+ item.setData(type, data, size);
+
+ return overwrote_existing;
+}
+
+bool MetaData::findData(uint32_t key, uint32_t *type,
+ const void **data, size_t *size) const {
+ ssize_t i = mItems.indexOfKey(key);
+
+ if (i < 0) {
+ return false;
+ }
+
+ const typed_data &item = mItems.valueAt(i);
+
+ item.getData(type, data, size);
+
+ return true;
+}
+
+MetaData::typed_data::typed_data()
+ : mType(0),
+ mSize(0) {
+}
+
+MetaData::typed_data::~typed_data() {
+ clear();
+}
+
+MetaData::typed_data::typed_data(const typed_data &from)
+ : mType(from.mType),
+ mSize(0) {
+ allocateStorage(from.mSize);
+ memcpy(storage(), from.storage(), mSize);
+}
+
+MetaData::typed_data &MetaData::typed_data::operator=(
+ const MetaData::typed_data &from) {
+ if (this != &from) {
+ clear();
+ mType = from.mType;
+ allocateStorage(from.mSize);
+ memcpy(storage(), from.storage(), mSize);
+ }
+
+ return *this;
+}
+
+void MetaData::typed_data::clear() {
+ freeStorage();
+
+ mType = 0;
+}
+
+void MetaData::typed_data::setData(
+ uint32_t type, const void *data, size_t size) {
+ clear();
+
+ mType = type;
+ allocateStorage(size);
+ memcpy(storage(), data, size);
+}
+
+void MetaData::typed_data::getData(
+ uint32_t *type, const void **data, size_t *size) const {
+ *type = mType;
+ *size = mSize;
+ *data = storage();
+}
+
+void MetaData::typed_data::allocateStorage(size_t size) {
+ mSize = size;
+
+ if (usesReservoir()) {
+ return;
+ }
+
+ u.ext_data = malloc(mSize);
+}
+
+void MetaData::typed_data::freeStorage() {
+ if (!usesReservoir()) {
+ if (u.ext_data) {
+ free(u.ext_data);
+ }
+ }
+
+ mSize = 0;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/MmapSource.cpp b/media/libstagefright/MmapSource.cpp
new file mode 100644
index 000000000000..7cb861cde932
--- /dev/null
+++ b/media/libstagefright/MmapSource.cpp
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MmapSource"
+#include <utils/Log.h>
+
+#include <sys/mman.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <media/stagefright/MmapSource.h>
+
+namespace android {
+
+MmapSource::MmapSource(const char *filename)
+ : mFd(open(filename, O_RDONLY)),
+ mBase(NULL),
+ mSize(0) {
+ LOGV("MmapSource '%s'", filename);
+ assert(mFd >= 0);
+
+ off_t size = lseek(mFd, 0, SEEK_END);
+ mSize = (size_t)size;
+
+ mBase = mmap(0, mSize, PROT_READ, MAP_FILE | MAP_SHARED, mFd, 0);
+
+ if (mBase == (void *)-1) {
+ mBase = NULL;
+
+ close(mFd);
+ mFd = -1;
+ }
+}
+
+MmapSource::MmapSource(int fd, int64_t offset, int64_t length)
+ : mFd(fd),
+ mBase(NULL),
+ mSize(length) {
+ LOGV("MmapSource fd:%d offset:%lld length:%lld", fd, offset, length);
+ assert(fd >= 0);
+
+ mBase = mmap(0, mSize, PROT_READ, MAP_FILE | MAP_SHARED, mFd, offset);
+
+ if (mBase == (void *)-1) {
+ mBase = NULL;
+
+ close(mFd);
+ mFd = -1;
+ }
+
+}
+
+MmapSource::~MmapSource() {
+ if (mFd != -1) {
+ munmap(mBase, mSize);
+ mBase = NULL;
+ mSize = 0;
+
+ close(mFd);
+ mFd = -1;
+ }
+}
+
+status_t MmapSource::InitCheck() const {
+ return mFd == -1 ? NO_INIT : OK;
+}
+
+ssize_t MmapSource::read_at(off_t offset, void *data, size_t size) {
+ LOGV("read_at offset:%ld data:%p size:%d", offset, data, size);
+ assert(offset >= 0);
+
+ size_t avail = 0;
+ if (offset >= 0 && offset < (off_t)mSize) {
+ avail = mSize - offset;
+ }
+
+ if (size > avail) {
+ size = avail;
+ }
+
+ memcpy(data, (const uint8_t *)mBase + offset, size);
+
+ return (ssize_t)size;
+}
+
+status_t MmapSource::getSize(off_t *size) {
+ *size = mSize;
+
+ return OK;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp
new file mode 100644
index 000000000000..1bc8a442201b
--- /dev/null
+++ b/media/libstagefright/OMXClient.cpp
@@ -0,0 +1,369 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "OMXClient"
+#include <utils/Log.h>
+
+#include <sys/socket.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <binder/IServiceManager.h>
+#include <media/IMediaPlayerService.h>
+#include <media/IOMX.h>
+#include <media/stagefright/OMXClient.h>
+
+namespace android {
+
+OMXClient::OMXClient()
+ : mSock(-1) {
+}
+
+OMXClient::~OMXClient() {
+ disconnect();
+}
+
+status_t OMXClient::connect() {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mSock >= 0) {
+ return UNKNOWN_ERROR;
+ }
+
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("media.player"));
+ sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
+
+ assert(service.get() != NULL);
+
+ mOMX = service->createOMX();
+ assert(mOMX.get() != NULL);
+
+#if IOMX_USES_SOCKETS
+ status_t result = mOMX->connect(&mSock);
+ if (result != OK) {
+ mSock = -1;
+
+ mOMX = NULL;
+ return result;
+ }
+
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+ int err = pthread_create(&mThread, &attr, ThreadWrapper, this);
+ assert(err == 0);
+
+ pthread_attr_destroy(&attr);
+#else
+ mReflector = new OMXClientReflector(this);
+#endif
+
+ return OK;
+}
+
+void OMXClient::disconnect() {
+ {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mSock < 0) {
+ return;
+ }
+
+ assert(mObservers.isEmpty());
+ }
+
+#if IOMX_USES_SOCKETS
+ omx_message msg;
+ msg.type = omx_message::DISCONNECT;
+ ssize_t n = send(mSock, &msg, sizeof(msg), 0);
+ assert(n > 0 && static_cast<size_t>(n) == sizeof(msg));
+
+ void *dummy;
+ pthread_join(mThread, &dummy);
+#else
+ mReflector->reset();
+ mReflector.clear();
+#endif
+}
+
+#if IOMX_USES_SOCKETS
+// static
+void *OMXClient::ThreadWrapper(void *me) {
+ ((OMXClient *)me)->threadEntry();
+
+ return NULL;
+}
+
+void OMXClient::threadEntry() {
+ bool done = false;
+ while (!done) {
+ omx_message msg;
+ ssize_t n = recv(mSock, &msg, sizeof(msg), 0);
+
+ if (n <= 0) {
+ break;
+ }
+
+ done = onOMXMessage(msg);
+ }
+
+ Mutex::Autolock autoLock(mLock);
+ close(mSock);
+ mSock = -1;
+}
+#endif
+
+status_t OMXClient::fillBuffer(IOMX::node_id node, IOMX::buffer_id buffer) {
+#if !IOMX_USES_SOCKETS
+ mOMX->fill_buffer(node, buffer);
+#else
+ if (mSock < 0) {
+ return UNKNOWN_ERROR;
+ }
+
+ omx_message msg;
+ msg.type = omx_message::FILL_BUFFER;
+ msg.u.buffer_data.node = node;
+ msg.u.buffer_data.buffer = buffer;
+
+ ssize_t n = send(mSock, &msg, sizeof(msg), 0);
+ assert(n > 0 && static_cast<size_t>(n) == sizeof(msg));
+#endif
+
+ return OK;
+}
+
+status_t OMXClient::emptyBuffer(
+ IOMX::node_id node, IOMX::buffer_id buffer,
+ OMX_U32 range_offset, OMX_U32 range_length,
+ OMX_U32 flags, OMX_TICKS timestamp) {
+#if !IOMX_USES_SOCKETS
+ mOMX->empty_buffer(
+ node, buffer, range_offset, range_length, flags, timestamp);
+#else
+ if (mSock < 0) {
+ return UNKNOWN_ERROR;
+ }
+
+ // XXX I don't like all this copying...
+
+ omx_message msg;
+ msg.type = omx_message::EMPTY_BUFFER;
+ msg.u.extended_buffer_data.node = node;
+ msg.u.extended_buffer_data.buffer = buffer;
+ msg.u.extended_buffer_data.range_offset = range_offset;
+ msg.u.extended_buffer_data.range_length = range_length;
+ msg.u.extended_buffer_data.flags = flags;
+ msg.u.extended_buffer_data.timestamp = timestamp;
+
+ ssize_t n = send(mSock, &msg, sizeof(msg), 0);
+ assert(n > 0 && static_cast<size_t>(n) == sizeof(msg));
+#endif
+
+ return OK;
+}
+
+status_t OMXClient::send_command(
+ IOMX::node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param) {
+#if !IOMX_USES_SOCKETS
+ return mOMX->send_command(node, cmd, param);
+#else
+ if (mSock < 0) {
+ return UNKNOWN_ERROR;
+ }
+
+ omx_message msg;
+ msg.type = omx_message::SEND_COMMAND;
+ msg.u.send_command_data.node = node;
+ msg.u.send_command_data.cmd = cmd;
+ msg.u.send_command_data.param = param;
+
+ ssize_t n = send(mSock, &msg, sizeof(msg), 0);
+ assert(n > 0 && static_cast<size_t>(n) == sizeof(msg));
+#endif
+
+ return OK;
+}
+
+status_t OMXClient::registerObserver(
+ IOMX::node_id node, OMXObserver *observer) {
+ Mutex::Autolock autoLock(&mLock);
+
+ ssize_t index = mObservers.indexOfKey(node);
+ if (index >= 0) {
+ return UNKNOWN_ERROR;
+ }
+
+ mObservers.add(node, observer);
+ observer->start();
+
+#if !IOMX_USES_SOCKETS
+ mOMX->observe_node(node, mReflector);
+#endif
+
+ return OK;
+}
+
+void OMXClient::unregisterObserver(IOMX::node_id node) {
+ Mutex::Autolock autoLock(mLock);
+
+ ssize_t index = mObservers.indexOfKey(node);
+ assert(index >= 0);
+
+ if (index < 0) {
+ return;
+ }
+
+ OMXObserver *observer = mObservers.valueAt(index);
+ observer->stop();
+ mObservers.removeItemsAt(index);
+}
+
+bool OMXClient::onOMXMessage(const omx_message &msg) {
+ bool done = false;
+
+ switch (msg.type) {
+ case omx_message::EVENT:
+ {
+ LOGV("OnEvent node:%p event:%d data1:%ld data2:%ld",
+ msg.u.event_data.node,
+ msg.u.event_data.event,
+ msg.u.event_data.data1,
+ msg.u.event_data.data2);
+
+ break;
+ }
+
+ case omx_message::FILL_BUFFER_DONE:
+ {
+ LOGV("FillBufferDone %p", msg.u.extended_buffer_data.buffer);
+ break;
+ }
+
+ case omx_message::EMPTY_BUFFER_DONE:
+ {
+ LOGV("EmptyBufferDone %p", msg.u.buffer_data.buffer);
+ break;
+ }
+
+#if IOMX_USES_SOCKETS
+ case omx_message::DISCONNECTED:
+ {
+ LOGV("Disconnected");
+ done = true;
+ break;
+ }
+#endif
+
+ default:
+ LOGE("received unknown omx_message type %d", msg.type);
+ break;
+ }
+
+ Mutex::Autolock autoLock(mLock);
+ ssize_t index = mObservers.indexOfKey(msg.u.buffer_data.node);
+
+ if (index >= 0) {
+ mObservers.editValueAt(index)->postMessage(msg);
+ }
+
+ return done;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+OMXObserver::OMXObserver() {
+}
+
+OMXObserver::~OMXObserver() {
+}
+
+void OMXObserver::start() {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+ int err = pthread_create(&mThread, &attr, ThreadWrapper, this);
+ assert(err == 0);
+
+ pthread_attr_destroy(&attr);
+}
+
+void OMXObserver::stop() {
+ omx_message msg;
+ msg.type = omx_message::QUIT_OBSERVER;
+ postMessage(msg);
+
+ void *dummy;
+ pthread_join(mThread, &dummy);
+}
+
+void OMXObserver::postMessage(const omx_message &msg) {
+ Mutex::Autolock autoLock(mLock);
+ mQueue.push_back(msg);
+ mQueueNotEmpty.signal();
+}
+
+// static
+void *OMXObserver::ThreadWrapper(void *me) {
+ static_cast<OMXObserver *>(me)->threadEntry();
+
+ return NULL;
+}
+
+void OMXObserver::threadEntry() {
+ for (;;) {
+ omx_message msg;
+
+ {
+ Mutex::Autolock autoLock(mLock);
+ while (mQueue.empty()) {
+ mQueueNotEmpty.wait(mLock);
+ }
+
+ msg = *mQueue.begin();
+ mQueue.erase(mQueue.begin());
+ }
+
+ if (msg.type == omx_message::QUIT_OBSERVER) {
+ break;
+ }
+
+ onOMXMessage(msg);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+OMXClientReflector::OMXClientReflector(OMXClient *client)
+ : mClient(client) {
+}
+
+void OMXClientReflector::on_message(const omx_message &msg) {
+ if (mClient != NULL) {
+ mClient->onOMXMessage(msg);
+ }
+}
+
+void OMXClientReflector::reset() {
+ mClient = NULL;
+}
+
+} // namespace android
diff --git a/media/libstagefright/OMXDecoder.cpp b/media/libstagefright/OMXDecoder.cpp
new file mode 100644
index 000000000000..c059a9df624a
--- /dev/null
+++ b/media/libstagefright/OMXDecoder.cpp
@@ -0,0 +1,1329 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "OMXDecoder"
+#include <utils/Log.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <OMX_Component.h>
+
+#include <media/stagefright/ESDS.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/OMXDecoder.h>
+
+namespace android {
+
+class OMXMediaBuffer : public MediaBuffer {
+public:
+ OMXMediaBuffer(IOMX::buffer_id buffer_id, const sp<IMemory> &mem)
+ : MediaBuffer(mem->pointer(),
+ mem->size()),
+ mBufferID(buffer_id),
+ mMem(mem) {
+ }
+
+ IOMX::buffer_id buffer_id() const { return mBufferID; }
+
+private:
+ IOMX::buffer_id mBufferID;
+ sp<IMemory> mMem;
+
+ OMXMediaBuffer(const OMXMediaBuffer &);
+ OMXMediaBuffer &operator=(const OMXMediaBuffer &);
+};
+
+struct CodecInfo {
+ const char *mime;
+ const char *codec;
+};
+
+static const CodecInfo kDecoderInfo[] = {
+ { "audio/mpeg", "OMX.PV.mp3dec" },
+ { "audio/3gpp", "OMX.PV.amrdec" },
+ { "audio/mp4a-latm", "OMX.PV.aacdec" },
+ { "video/mp4v-es", "OMX.qcom.video.decoder.mpeg4" },
+ { "video/mp4v-es", "OMX.PV.mpeg4dec" },
+ { "video/3gpp", "OMX.qcom.video.decoder.h263" },
+ { "video/3gpp", "OMX.PV.h263dec" },
+ { "video/avc", "OMX.qcom.video.decoder.avc" },
+ { "video/avc", "OMX.PV.avcdec" },
+};
+
+static const CodecInfo kEncoderInfo[] = {
+ { "audio/3gpp", "OMX.PV.amrencnb" },
+ { "audio/mp4a-latm", "OMX.PV.aacenc" },
+ { "video/mp4v-es", "OMX.qcom.video.encoder.mpeg4" },
+ { "video/mp4v-es", "OMX.PV.mpeg4enc" },
+ { "video/3gpp", "OMX.qcom.video.encoder.h263" },
+ { "video/3gpp", "OMX.PV.h263enc" },
+ { "video/avc", "OMX.PV.avcenc" },
+};
+
+static const char *GetCodec(const CodecInfo *info, size_t numInfos,
+ const char *mime, int index) {
+ assert(index >= 0);
+ for(size_t i = 0; i < numInfos; ++i) {
+ if (!strcasecmp(mime, info[i].mime)) {
+ if (index == 0) {
+ return info[i].codec;
+ }
+
+ --index;
+ }
+ }
+
+ return NULL;
+}
+
+// static
+OMXDecoder *OMXDecoder::Create(OMXClient *client, const sp<MetaData> &meta) {
+ const char *mime;
+ bool success = meta->findCString(kKeyMIMEType, &mime);
+ assert(success);
+
+ sp<IOMX> omx = client->interface();
+
+ const char *codec = NULL;
+ IOMX::node_id node = 0;
+ for (int index = 0;; ++index) {
+ codec = GetCodec(
+ kDecoderInfo, sizeof(kDecoderInfo) / sizeof(kDecoderInfo[0]),
+ mime, index);
+
+ if (!codec) {
+ return NULL;
+ }
+
+ LOGI("Attempting to allocate OMX node '%s'", codec);
+
+ status_t err = omx->allocate_node(codec, &node);
+ if (err == OK) {
+ break;
+ }
+ }
+
+ OMXDecoder *decoder = new OMXDecoder(client, node, mime, codec);
+
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (meta->findData(kKeyESDS, &type, &data, &size)) {
+ ESDS esds((const char *)data, size);
+ assert(esds.InitCheck() == OK);
+
+ const void *codec_specific_data;
+ size_t codec_specific_data_size;
+ esds.getCodecSpecificInfo(
+ &codec_specific_data, &codec_specific_data_size);
+
+ printf("found codec specific data of size %d\n",
+ codec_specific_data_size);
+
+ decoder->addCodecSpecificData(
+ codec_specific_data, codec_specific_data_size);
+ } else if (meta->findData(kKeyAVCC, &type, &data, &size)) {
+ printf("found avcc of size %d\n", size);
+
+ const uint8_t *ptr = (const uint8_t *)data + 6;
+ size -= 6;
+ while (size >= 2) {
+ size_t length = ptr[0] << 8 | ptr[1];
+
+ ptr += 2;
+ size -= 2;
+
+ // printf("length = %d, size = %d\n", length, size);
+
+ assert(size >= length);
+
+ decoder->addCodecSpecificData(ptr, length);
+
+ ptr += length;
+ size -= length;
+
+ if (size <= 1) {
+ break;
+ }
+
+ ptr++; // XXX skip trailing 0x01 byte???
+ --size;
+ }
+ }
+
+ return decoder;
+}
+
+// static
+OMXDecoder *OMXDecoder::CreateEncoder(
+ OMXClient *client, const sp<MetaData> &meta) {
+ const char *mime;
+ bool success = meta->findCString(kKeyMIMEType, &mime);
+ assert(success);
+
+ sp<IOMX> omx = client->interface();
+
+ const char *codec = NULL;
+ IOMX::node_id node = 0;
+ for (int index = 0;; ++index) {
+ codec = GetCodec(
+ kEncoderInfo, sizeof(kEncoderInfo) / sizeof(kEncoderInfo[0]),
+ mime, index);
+
+ if (!codec) {
+ return NULL;
+ }
+
+ LOGI("Attempting to allocate OMX node '%s'", codec);
+
+ status_t err = omx->allocate_node(codec, &node);
+ if (err == OK) {
+ break;
+ }
+ }
+
+ OMXDecoder *encoder = new OMXDecoder(client, node, mime, codec);
+
+ return encoder;
+}
+
+OMXDecoder::OMXDecoder(OMXClient *client, IOMX::node_id node,
+ const char *mime, const char *codec)
+ : mClient(client),
+ mOMX(mClient->interface()),
+ mNode(node),
+ mComponentName(strdup(codec)),
+ mIsMP3(!strcasecmp(mime, "audio/mpeg")),
+ mSource(NULL),
+ mCodecSpecificDataIterator(mCodecSpecificData.begin()),
+ mState(OMX_StateLoaded),
+ mPortStatusMask(kPortStatusActive << 2 | kPortStatusActive),
+ mShutdownInitiated(false),
+ mDealer(new MemoryDealer(3 * 1024 * 1024)),
+ mSeeking(false),
+ mStarted(false),
+ mErrorCondition(OK),
+ mReachedEndOfInput(false) {
+ mClient->registerObserver(mNode, this);
+
+ mBuffers.push(); // input buffers
+ mBuffers.push(); // output buffers
+}
+
+OMXDecoder::~OMXDecoder() {
+ if (mStarted) {
+ stop();
+ }
+
+ for (List<CodecSpecificData>::iterator it = mCodecSpecificData.begin();
+ it != mCodecSpecificData.end(); ++it) {
+ free((*it).data);
+ }
+ mCodecSpecificData.clear();
+
+ mClient->unregisterObserver(mNode);
+
+ status_t err = mOMX->free_node(mNode);
+ assert(err == OK);
+ mNode = 0;
+
+ free(mComponentName);
+ mComponentName = NULL;
+}
+
+void OMXDecoder::setSource(MediaSource *source) {
+ Mutex::Autolock autoLock(mLock);
+
+ assert(mSource == NULL);
+
+ mSource = source;
+ setup();
+}
+
+status_t OMXDecoder::start(MetaData *) {
+ assert(!mStarted);
+
+ // mDealer->dump("Decoder Dealer");
+
+ sp<MetaData> params = new MetaData;
+ if (!strcmp(mComponentName, "OMX.qcom.video.decoder.avc")) {
+ params->setInt32(kKeyNeedsNALFraming, true);
+ }
+
+ status_t err = mSource->start(params.get());
+
+ if (err != OK) {
+ return err;
+ }
+
+ postStart();
+
+ mStarted = true;
+
+ return OK;
+}
+
+status_t OMXDecoder::stop() {
+ assert(mStarted);
+
+ LOGI("Initiating OMX Node shutdown, busy polling.");
+ initiateShutdown();
+
+ // Important: initiateShutdown must be called first, _then_ release
+ // buffers we're holding onto.
+ while (!mOutputBuffers.empty()) {
+ MediaBuffer *buffer = *mOutputBuffers.begin();
+ mOutputBuffers.erase(mOutputBuffers.begin());
+
+ LOGV("releasing buffer %p.", buffer->data());
+
+ buffer->release();
+ buffer = NULL;
+ }
+
+ int attempt = 1;
+ while (mState != OMX_StateLoaded && attempt < 10) {
+ usleep(100000);
+
+ ++attempt;
+ }
+
+ if (mState != OMX_StateLoaded) {
+ LOGE("!!! OMX Node '%s' did NOT shutdown cleanly !!!", mComponentName);
+ } else {
+ LOGI("OMX Node '%s' has shutdown cleanly.", mComponentName);
+ }
+
+ mSource->stop();
+
+ mCodecSpecificDataIterator = mCodecSpecificData.begin();
+ mShutdownInitiated = false;
+ mSeeking = false;
+ mStarted = false;
+ mErrorCondition = OK;
+ mReachedEndOfInput = false;
+
+ return OK;
+}
+
+sp<MetaData> OMXDecoder::getFormat() {
+ return mOutputFormat;
+}
+
+status_t OMXDecoder::read(
+ MediaBuffer **out, const ReadOptions *options) {
+ assert(mStarted);
+
+ *out = NULL;
+
+ Mutex::Autolock autoLock(mLock);
+
+ if (mErrorCondition != OK && mErrorCondition != ERROR_END_OF_STREAM) {
+ // Errors are sticky.
+ return mErrorCondition;
+ }
+
+ int64_t seekTimeUs;
+ if (options && options->getSeekTo(&seekTimeUs)) {
+ LOGI("[%s] seeking to %lld us", mComponentName, seekTimeUs);
+
+ mErrorCondition = OK;
+ mReachedEndOfInput = false;
+
+ setPortStatus(kPortIndexInput, kPortStatusFlushing);
+ setPortStatus(kPortIndexOutput, kPortStatusFlushing);
+
+ mSeeking = true;
+ mSeekTimeUs = seekTimeUs;
+
+ while (!mOutputBuffers.empty()) {
+ OMXMediaBuffer *buffer =
+ static_cast<OMXMediaBuffer *>(*mOutputBuffers.begin());
+
+ // We could have used buffer->release() instead, but we're
+ // holding the lock and signalBufferReturned attempts to acquire
+ // the lock.
+ buffer->claim();
+ mBuffers.editItemAt(
+ kPortIndexOutput).push_back(buffer->buffer_id());
+ buffer = NULL;
+
+ mOutputBuffers.erase(mOutputBuffers.begin());
+ }
+
+ status_t err = mOMX->send_command(mNode, OMX_CommandFlush, -1);
+ assert(err == OK);
+
+ // Once flushing is completed buffers will again be scheduled to be
+ // filled/emptied.
+ }
+
+ while (mErrorCondition == OK && mOutputBuffers.empty()) {
+ mOutputBufferAvailable.wait(mLock);
+ }
+
+ if (!mOutputBuffers.empty()) {
+ MediaBuffer *buffer = *mOutputBuffers.begin();
+ mOutputBuffers.erase(mOutputBuffers.begin());
+
+ *out = buffer;
+
+ return OK;
+ } else {
+ assert(mErrorCondition != OK);
+ return mErrorCondition;
+ }
+}
+
+void OMXDecoder::addCodecSpecificData(const void *data, size_t size) {
+ CodecSpecificData specific;
+ specific.data = malloc(size);
+ memcpy(specific.data, data, size);
+ specific.size = size;
+
+ mCodecSpecificData.push_back(specific);
+ mCodecSpecificDataIterator = mCodecSpecificData.begin();
+}
+
+void OMXDecoder::onOMXMessage(const omx_message &msg) {
+ Mutex::Autolock autoLock(mLock);
+
+ switch (msg.type) {
+ case omx_message::START:
+ {
+ onStart();
+ break;
+ }
+
+ case omx_message::EVENT:
+ {
+ onEvent(msg.u.event_data.event, msg.u.event_data.data1,
+ msg.u.event_data.data2);
+ break;
+ }
+
+ case omx_message::EMPTY_BUFFER_DONE:
+ {
+ onEmptyBufferDone(msg.u.buffer_data.buffer);
+ break;
+ }
+
+ case omx_message::FILL_BUFFER_DONE:
+ case omx_message::INITIAL_FILL_BUFFER:
+ {
+ onFillBufferDone(msg);
+ break;
+ }
+
+ default:
+ LOGE("received unknown omx_message type %d", msg.type);
+ break;
+ }
+}
+
+void OMXDecoder::setAMRFormat() {
+ OMX_AUDIO_PARAM_AMRTYPE def;
+ def.nSize = sizeof(def);
+ def.nVersion.s.nVersionMajor = 1;
+ def.nVersion.s.nVersionMinor = 1;
+ def.nPortIndex = kPortIndexInput;
+
+ status_t err =
+ mOMX->get_parameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
+
+ assert(err == NO_ERROR);
+
+ def.eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF;
+ def.eAMRBandMode = OMX_AUDIO_AMRBandModeNB0;
+
+ err = mOMX->set_parameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
+ assert(err == NO_ERROR);
+}
+
+void OMXDecoder::setAACFormat() {
+ OMX_AUDIO_PARAM_AACPROFILETYPE def;
+ def.nSize = sizeof(def);
+ def.nVersion.s.nVersionMajor = 1;
+ def.nVersion.s.nVersionMinor = 1;
+ def.nPortIndex = kPortIndexInput;
+
+ status_t err =
+ mOMX->get_parameter(mNode, OMX_IndexParamAudioAac, &def, sizeof(def));
+ assert(err == NO_ERROR);
+
+ def.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4ADTS;
+
+ err = mOMX->set_parameter(mNode, OMX_IndexParamAudioAac, &def, sizeof(def));
+ assert(err == NO_ERROR);
+}
+
+void OMXDecoder::setVideoOutputFormat(OMX_U32 width, OMX_U32 height) {
+ LOGI("setVideoOutputFormat width=%ld, height=%ld", width, height);
+
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
+
+ bool is_encoder = strstr(mComponentName, ".encoder.") != NULL; // XXX
+
+ def.nSize = sizeof(def);
+ def.nVersion.s.nVersionMajor = 1;
+ def.nVersion.s.nVersionMinor = 1;
+ def.nPortIndex = is_encoder ? kPortIndexOutput : kPortIndexInput;
+
+ status_t err = mOMX->get_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+ assert(err == NO_ERROR);
+
+#if 1
+ // XXX Need a (much) better heuristic to compute input buffer sizes.
+ const size_t X = 64 * 1024;
+ if (def.nBufferSize < X) {
+ def.nBufferSize = X;
+ }
+#endif
+
+ assert(def.eDomain == OMX_PortDomainVideo);
+
+ video_def->nFrameWidth = width;
+ video_def->nFrameHeight = height;
+ // video_def.eCompressionFormat = OMX_VIDEO_CodingAVC;
+ video_def->eColorFormat = OMX_COLOR_FormatUnused;
+
+ err = mOMX->set_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ assert(err == NO_ERROR);
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ def.nSize = sizeof(def);
+ def.nVersion.s.nVersionMajor = 1;
+ def.nVersion.s.nVersionMinor = 1;
+ def.nPortIndex = is_encoder ? kPortIndexInput : kPortIndexOutput;
+
+ err = mOMX->get_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ assert(err == NO_ERROR);
+
+ assert(def.eDomain == OMX_PortDomainVideo);
+
+ def.nBufferSize =
+ (((width + 15) & -16) * ((height + 15) & -16) * 3) / 2; // YUV420
+
+ video_def->nFrameWidth = width;
+ video_def->nFrameHeight = height;
+ video_def->nStride = width;
+ // video_def->nSliceHeight = height;
+ video_def->eCompressionFormat = OMX_VIDEO_CodingUnused;
+// video_def->eColorFormat = OMX_COLOR_FormatYUV420Planar;
+
+ err = mOMX->set_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ assert(err == NO_ERROR);
+}
+
+void OMXDecoder::setup() {
+ const sp<MetaData> &meta = mSource->getFormat();
+
+ const char *mime;
+ bool success = meta->findCString(kKeyMIMEType, &mime);
+ assert(success);
+
+ if (!strcasecmp(mime, "audio/3gpp")) {
+ setAMRFormat();
+ } else if (!strcasecmp(mime, "audio/mp4a-latm")) {
+ setAACFormat();
+ } else if (!strncasecmp(mime, "video/", 6)) {
+ int32_t width, height;
+ bool success = meta->findInt32(kKeyWidth, &width);
+ success = success && meta->findInt32(kKeyHeight, &height);
+ assert(success);
+
+ setVideoOutputFormat(width, height);
+ }
+
+ // dumpPortDefinition(0);
+ // dumpPortDefinition(1);
+
+ mOutputFormat = new MetaData;
+ mOutputFormat->setCString(kKeyDecoderComponent, mComponentName);
+
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ def.nSize = sizeof(def);
+ def.nVersion.s.nVersionMajor = 1;
+ def.nVersion.s.nVersionMinor = 1;
+ def.nPortIndex = kPortIndexOutput;
+
+ status_t err = mOMX->get_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ assert(err == NO_ERROR);
+
+ switch (def.eDomain) {
+ case OMX_PortDomainAudio:
+ {
+ OMX_AUDIO_PORTDEFINITIONTYPE *audio_def = &def.format.audio;
+
+ assert(audio_def->eEncoding == OMX_AUDIO_CodingPCM);
+
+ OMX_AUDIO_PARAM_PCMMODETYPE params;
+ params.nSize = sizeof(params);
+ params.nVersion.s.nVersionMajor = 1;
+ params.nVersion.s.nVersionMinor = 1;
+ params.nPortIndex = kPortIndexOutput;
+
+ err = mOMX->get_parameter(
+ mNode, OMX_IndexParamAudioPcm, &params, sizeof(params));
+ assert(err == OK);
+
+ assert(params.eNumData == OMX_NumericalDataSigned);
+ assert(params.nBitPerSample == 16);
+ assert(params.ePCMMode == OMX_AUDIO_PCMModeLinear);
+
+ int32_t numChannels, sampleRate;
+ meta->findInt32(kKeyChannelCount, &numChannels);
+ meta->findInt32(kKeySampleRate, &sampleRate);
+
+ mOutputFormat->setCString(kKeyMIMEType, "audio/raw");
+ mOutputFormat->setInt32(kKeyChannelCount, numChannels);
+ mOutputFormat->setInt32(kKeySampleRate, sampleRate);
+ break;
+ }
+
+ case OMX_PortDomainVideo:
+ {
+ OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
+
+ if (video_def->eCompressionFormat == OMX_VIDEO_CodingUnused) {
+ mOutputFormat->setCString(kKeyMIMEType, "video/raw");
+ } else if (video_def->eCompressionFormat == OMX_VIDEO_CodingMPEG4) {
+ mOutputFormat->setCString(kKeyMIMEType, "video/mp4v-es");
+ } else if (video_def->eCompressionFormat == OMX_VIDEO_CodingH263) {
+ mOutputFormat->setCString(kKeyMIMEType, "video/3gpp");
+ } else if (video_def->eCompressionFormat == OMX_VIDEO_CodingAVC) {
+ mOutputFormat->setCString(kKeyMIMEType, "video/avc");
+ } else {
+ assert(!"Unknown compression format.");
+ }
+
+ if (!strcmp(mComponentName, "OMX.PV.avcdec")) {
+ // This component appears to be lying to me.
+ mOutputFormat->setInt32(
+ kKeyWidth, (video_def->nFrameWidth + 15) & -16);
+ mOutputFormat->setInt32(
+ kKeyHeight, (video_def->nFrameHeight + 15) & -16);
+ } else {
+ mOutputFormat->setInt32(kKeyWidth, video_def->nFrameWidth);
+ mOutputFormat->setInt32(kKeyHeight, video_def->nFrameHeight);
+ }
+
+ mOutputFormat->setInt32(kKeyColorFormat, video_def->eColorFormat);
+ break;
+ }
+
+ default:
+ {
+ assert(!"should not be here, neither audio nor video.");
+ break;
+ }
+ }
+}
+
+void OMXDecoder::onStart() {
+ bool needs_qcom_hack =
+ !strncmp(mComponentName, "OMX.qcom.video.", 15);
+
+ if (!needs_qcom_hack) {
+ status_t err =
+ mOMX->send_command(mNode, OMX_CommandStateSet, OMX_StateIdle);
+ assert(err == NO_ERROR);
+ }
+
+ allocateBuffers(kPortIndexInput);
+ allocateBuffers(kPortIndexOutput);
+
+ if (needs_qcom_hack) {
+ // XXX this should happen before AllocateBuffers, but qcom's
+ // h264 vdec disagrees.
+ status_t err =
+ mOMX->send_command(mNode, OMX_CommandStateSet, OMX_StateIdle);
+ assert(err == NO_ERROR);
+ }
+}
+
+void OMXDecoder::allocateBuffers(OMX_U32 port_index) {
+ assert(mBuffers[port_index].empty());
+
+ OMX_U32 num_buffers;
+ OMX_U32 buffer_size;
+
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ def.nSize = sizeof(def);
+ def.nVersion.s.nVersionMajor = 1;
+ def.nVersion.s.nVersionMinor = 1;
+ def.nVersion.s.nRevision = 0;
+ def.nVersion.s.nStep = 0;
+ def.nPortIndex = port_index;
+
+ status_t err = mOMX->get_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ assert(err == NO_ERROR);
+
+ num_buffers = def.nBufferCountActual;
+ buffer_size = def.nBufferSize;
+
+ LOGV("[%s] port %ld: allocating %ld buffers of size %ld each\n",
+ mComponentName, port_index, num_buffers, buffer_size);
+
+ for (OMX_U32 i = 0; i < num_buffers; ++i) {
+ sp<IMemory> mem = mDealer->allocate(buffer_size);
+ assert(mem.get() != NULL);
+
+ IOMX::buffer_id buffer;
+ status_t err;
+
+ if (port_index == kPortIndexInput
+ && !strncmp(mComponentName, "OMX.qcom.video.encoder.", 23)) {
+ // qcom's H.263 encoder appears to want to allocate its own input
+ // buffers.
+ err = mOMX->allocate_buffer_with_backup(mNode, port_index, mem, &buffer);
+ if (err != OK) {
+ LOGE("[%s] allocate_buffer_with_backup failed with error %d",
+ mComponentName, err);
+ }
+ } else if (port_index == kPortIndexOutput
+ && !strncmp(mComponentName, "OMX.qcom.video.decoder.", 23)) {
+#if 1
+ err = mOMX->allocate_buffer_with_backup(mNode, port_index, mem, &buffer);
+#else
+ // XXX This is fine as long as we are either running the player
+ // inside the media server process or we are using the
+ // QComHardwareRenderer to output the frames.
+ err = mOMX->allocate_buffer(mNode, port_index, buffer_size, &buffer);
+#endif
+ if (err != OK) {
+ LOGE("[%s] allocate_buffer_with_backup failed with error %d",
+ mComponentName, err);
+ }
+ } else {
+ err = mOMX->use_buffer(mNode, port_index, mem, &buffer);
+ if (err != OK) {
+ LOGE("[%s] use_buffer failed with error %d",
+ mComponentName, err);
+ }
+ }
+ assert(err == OK);
+
+ LOGV("allocated %s buffer %p.",
+ port_index == kPortIndexInput ? "INPUT" : "OUTPUT",
+ buffer);
+
+ mBuffers.editItemAt(port_index).push_back(buffer);
+ mBufferMap.add(buffer, mem);
+
+ if (port_index == kPortIndexOutput) {
+ OMXMediaBuffer *media_buffer = new OMXMediaBuffer(buffer, mem);
+ media_buffer->setObserver(this);
+
+ mMediaBufferMap.add(buffer, media_buffer);
+ }
+ }
+
+ LOGV("allocate %s buffers done.",
+ port_index == kPortIndexInput ? "INPUT" : "OUTPUT");
+}
+
+void OMXDecoder::onEvent(
+ OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+ LOGV("[%s] onEvent event=%d, data1=%ld, data2=%ld",
+ mComponentName, event, data1, data2);
+
+ switch (event) {
+ case OMX_EventCmdComplete: {
+ onEventCmdComplete(
+ static_cast<OMX_COMMANDTYPE>(data1), data2);
+
+ break;
+ }
+
+ case OMX_EventPortSettingsChanged: {
+ onEventPortSettingsChanged(data1);
+ break;
+ }
+
+ case OMX_EventBufferFlag: {
+ // initiateShutdown();
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+void OMXDecoder::onEventCmdComplete(OMX_COMMANDTYPE type, OMX_U32 data) {
+ switch (type) {
+ case OMX_CommandStateSet: {
+ OMX_STATETYPE state = static_cast<OMX_STATETYPE>(data);
+ onStateChanged(state);
+ break;
+ }
+
+ case OMX_CommandPortDisable: {
+ OMX_U32 port_index = data;
+ assert(getPortStatus(port_index) == kPortStatusDisabled);
+
+ status_t err =
+ mOMX->send_command(mNode, OMX_CommandPortEnable, port_index);
+
+ allocateBuffers(port_index);
+
+ break;
+ }
+
+ case OMX_CommandPortEnable: {
+ OMX_U32 port_index = data;
+ assert(getPortStatus(port_index) ==kPortStatusDisabled);
+ setPortStatus(port_index, kPortStatusActive);
+
+ assert(port_index == kPortIndexOutput);
+
+ BufferList *obuffers = &mBuffers.editItemAt(kPortIndexOutput);
+ while (!obuffers->empty()) {
+ IOMX::buffer_id buffer = *obuffers->begin();
+ obuffers->erase(obuffers->begin());
+
+ status_t err = mClient->fillBuffer(mNode, buffer);
+ assert(err == NO_ERROR);
+ }
+
+ break;
+ }
+
+ case OMX_CommandFlush: {
+ OMX_U32 port_index = data;
+ LOGV("Port %ld flush complete.", port_index);
+ assert(getPortStatus(port_index) == kPortStatusFlushing);
+
+ setPortStatus(port_index, kPortStatusActive);
+ BufferList *buffers = &mBuffers.editItemAt(port_index);
+ while (!buffers->empty()) {
+ IOMX::buffer_id buffer = *buffers->begin();
+ buffers->erase(buffers->begin());
+
+ if (port_index == kPortIndexInput) {
+ postEmptyBufferDone(buffer);
+ } else {
+ postInitialFillBuffer(buffer);
+ }
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+void OMXDecoder::onEventPortSettingsChanged(OMX_U32 port_index) {
+ assert(getPortStatus(port_index) == kPortStatusActive);
+ setPortStatus(port_index, kPortStatusDisabled);
+
+ status_t err =
+ mOMX->send_command(mNode, OMX_CommandPortDisable, port_index);
+ assert(err == NO_ERROR);
+}
+
+void OMXDecoder::onStateChanged(OMX_STATETYPE to) {
+ if (mState == OMX_StateLoaded) {
+ assert(to == OMX_StateIdle);
+
+ mState = to;
+
+ status_t err =
+ mOMX->send_command(mNode, OMX_CommandStateSet, OMX_StateExecuting);
+ assert(err == NO_ERROR);
+ } else if (mState == OMX_StateIdle) {
+ if (to == OMX_StateExecuting) {
+ mState = to;
+
+ BufferList *ibuffers = &mBuffers.editItemAt(kPortIndexInput);
+ while (!ibuffers->empty()) {
+ IOMX::buffer_id buffer = *ibuffers->begin();
+ ibuffers->erase(ibuffers->begin());
+
+ postEmptyBufferDone(buffer);
+ }
+
+ BufferList *obuffers = &mBuffers.editItemAt(kPortIndexOutput);
+ while (!obuffers->empty()) {
+ IOMX::buffer_id buffer = *obuffers->begin();
+ obuffers->erase(obuffers->begin());
+
+ postInitialFillBuffer(buffer);
+ }
+ } else {
+ assert(to == OMX_StateLoaded);
+
+ mState = to;
+
+ setPortStatus(kPortIndexInput, kPortStatusActive);
+ setPortStatus(kPortIndexOutput, kPortStatusActive);
+ }
+ } else if (mState == OMX_StateExecuting) {
+ assert(to == OMX_StateIdle);
+
+ mState = to;
+
+ LOGV("Executing->Idle complete, initiating Idle->Loaded");
+ status_t err =
+ mClient->send_command(mNode, OMX_CommandStateSet, OMX_StateLoaded);
+ assert(err == NO_ERROR);
+
+ BufferList *ibuffers = &mBuffers.editItemAt(kPortIndexInput);
+ for (BufferList::iterator it = ibuffers->begin();
+ it != ibuffers->end(); ++it) {
+ freeInputBuffer(*it);
+ }
+ ibuffers->clear();
+
+ BufferList *obuffers = &mBuffers.editItemAt(kPortIndexOutput);
+ for (BufferList::iterator it = obuffers->begin();
+ it != obuffers->end(); ++it) {
+ freeOutputBuffer(*it);
+ }
+ obuffers->clear();
+ }
+}
+
+void OMXDecoder::initiateShutdown() {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mShutdownInitiated) {
+ return;
+ }
+
+ if (mState == OMX_StateLoaded) {
+ return;
+ }
+
+ assert(mState == OMX_StateExecuting);
+
+ mShutdownInitiated = true;
+
+ status_t err =
+ mClient->send_command(mNode, OMX_CommandStateSet, OMX_StateIdle);
+ assert(err == NO_ERROR);
+
+ setPortStatus(kPortIndexInput, kPortStatusShutdown);
+ setPortStatus(kPortIndexOutput, kPortStatusShutdown);
+}
+
+void OMXDecoder::setPortStatus(OMX_U32 port_index, PortStatus status) {
+ int shift = 2 * port_index;
+
+ mPortStatusMask &= ~(3 << shift);
+ mPortStatusMask |= status << shift;
+}
+
+OMXDecoder::PortStatus OMXDecoder::getPortStatus(
+ OMX_U32 port_index) const {
+ int shift = 2 * port_index;
+
+ return static_cast<PortStatus>((mPortStatusMask >> shift) & 3);
+}
+
+void OMXDecoder::onEmptyBufferDone(IOMX::buffer_id buffer) {
+ LOGV("[%s] onEmptyBufferDone (%p)", mComponentName, buffer);
+
+ status_t err;
+ switch (getPortStatus(kPortIndexInput)) {
+ case kPortStatusDisabled:
+ freeInputBuffer(buffer);
+ err = NO_ERROR;
+ break;
+
+ case kPortStatusShutdown:
+ LOGV("We're shutting down, enqueue INPUT buffer %p.", buffer);
+ mBuffers.editItemAt(kPortIndexInput).push_back(buffer);
+ err = NO_ERROR;
+ break;
+
+ case kPortStatusFlushing:
+ LOGV("We're currently flushing, enqueue INPUT buffer %p.", buffer);
+ mBuffers.editItemAt(kPortIndexInput).push_back(buffer);
+ err = NO_ERROR;
+ break;
+
+ default:
+ onRealEmptyBufferDone(buffer);
+ err = NO_ERROR;
+ break;
+ }
+ assert(err == NO_ERROR);
+}
+
+void OMXDecoder::onFillBufferDone(const omx_message &msg) {
+ IOMX::buffer_id buffer = msg.u.extended_buffer_data.buffer;
+
+ LOGV("[%s] onFillBufferDone (%p)", mComponentName, buffer);
+
+ status_t err;
+ switch (getPortStatus(kPortIndexOutput)) {
+ case kPortStatusDisabled:
+ freeOutputBuffer(buffer);
+ err = NO_ERROR;
+ break;
+ case kPortStatusShutdown:
+ LOGV("We're shutting down, enqueue OUTPUT buffer %p.", buffer);
+ mBuffers.editItemAt(kPortIndexOutput).push_back(buffer);
+ err = NO_ERROR;
+ break;
+
+ case kPortStatusFlushing:
+ LOGV("We're currently flushing, enqueue OUTPUT buffer %p.", buffer);
+ mBuffers.editItemAt(kPortIndexOutput).push_back(buffer);
+ err = NO_ERROR;
+ break;
+
+ default:
+ {
+ if (msg.type == omx_message::INITIAL_FILL_BUFFER) {
+ err = mClient->fillBuffer(mNode, buffer);
+ } else {
+ LOGV("[%s] Filled OUTPUT buffer %p, flags=0x%08lx.",
+ mComponentName, buffer, msg.u.extended_buffer_data.flags);
+
+ onRealFillBufferDone(msg);
+ err = NO_ERROR;
+ }
+ break;
+ }
+ }
+ assert(err == NO_ERROR);
+}
+
+void OMXDecoder::onRealEmptyBufferDone(IOMX::buffer_id buffer) {
+ if (mReachedEndOfInput) {
+ // We already sent the EOS notification.
+
+ mBuffers.editItemAt(kPortIndexInput).push_back(buffer);
+ return;
+ }
+
+ const sp<IMemory> mem = mBufferMap.valueFor(buffer);
+ assert(mem.get() != NULL);
+
+ static const uint8_t kNALStartCode[4] = { 0x00, 0x00, 0x00, 0x01 };
+
+ if (mCodecSpecificDataIterator != mCodecSpecificData.end()) {
+ List<CodecSpecificData>::iterator it = mCodecSpecificDataIterator;
+
+ size_t range_length = 0;
+
+ if (!strcmp(mComponentName, "OMX.qcom.video.decoder.avc")) {
+ assert((*mCodecSpecificDataIterator).size + 4 <= mem->size());
+
+ memcpy(mem->pointer(), kNALStartCode, 4);
+
+ memcpy((uint8_t *)mem->pointer() + 4, (*it).data, (*it).size);
+ range_length = (*it).size + 4;
+ } else {
+ assert((*mCodecSpecificDataIterator).size <= mem->size());
+
+ memcpy((uint8_t *)mem->pointer(), (*it).data, (*it).size);
+ range_length = (*it).size;
+ }
+
+ ++mCodecSpecificDataIterator;
+
+ status_t err = mClient->emptyBuffer(
+ mNode, buffer, 0, range_length,
+ OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_CODECCONFIG,
+ 0);
+
+ assert(err == NO_ERROR);
+
+ return;
+ }
+
+ LOGV("[%s] waiting for input data", mComponentName);
+
+ MediaBuffer *input_buffer;
+ for (;;) {
+ status_t err;
+
+ if (mSeeking) {
+ MediaSource::ReadOptions options;
+ options.setSeekTo(mSeekTimeUs);
+
+ mSeeking = false;
+
+ err = mSource->read(&input_buffer, &options);
+ } else {
+ err = mSource->read(&input_buffer);
+ }
+ assert((err == OK && input_buffer != NULL)
+ || (err != OK && input_buffer == NULL));
+
+ if (err == ERROR_END_OF_STREAM) {
+ LOGE("[%s] Reached end of stream.", mComponentName);
+ mReachedEndOfInput = true;
+ } else {
+ LOGV("[%s] got input data", mComponentName);
+ }
+
+ if (err != OK) {
+ status_t err2 = mClient->emptyBuffer(
+ mNode, buffer, 0, 0, OMX_BUFFERFLAG_EOS, 0);
+
+ assert(err2 == NO_ERROR);
+ return;
+ }
+
+ if (mSeeking) {
+ input_buffer->release();
+ input_buffer = NULL;
+
+ continue;
+ }
+
+ break;
+ }
+
+ const uint8_t *src_data =
+ (const uint8_t *)input_buffer->data() + input_buffer->range_offset();
+
+ size_t src_length = input_buffer->range_length();
+ if (src_length == 195840) {
+ // When feeding the output of the AVC decoder into the H263 encoder,
+ // buffer sizes mismatch if width % 16 != 0 || height % 16 != 0.
+ src_length = 194400; // XXX HACK
+ } else if (src_length == 115200) {
+ src_length = 114240; // XXX HACK
+ }
+
+ if (src_length > mem->size()) {
+ LOGE("src_length=%d > mem->size() = %d\n",
+ src_length, mem->size());
+ }
+
+ assert(src_length <= mem->size());
+ memcpy(mem->pointer(), src_data, src_length);
+
+ OMX_U32 flags = 0;
+ if (!mIsMP3) {
+ // Only mp3 audio data may be streamed, all other data is assumed
+ // to be fed into the decoder at frame boundaries.
+ flags |= OMX_BUFFERFLAG_ENDOFFRAME;
+ }
+
+ int32_t units, scale;
+ bool success =
+ input_buffer->meta_data()->findInt32(kKeyTimeUnits, &units);
+
+ success = success &&
+ input_buffer->meta_data()->findInt32(kKeyTimeScale, &scale);
+
+ OMX_TICKS timestamp = 0;
+
+ if (success) {
+ // XXX units should be microseconds but PV treats them as milliseconds.
+ timestamp = ((OMX_S64)units * 1000) / scale;
+ }
+
+ input_buffer->release();
+ input_buffer = NULL;
+
+ LOGV("[%s] Calling EmptyBuffer on buffer %p",
+ mComponentName, buffer);
+
+ status_t err2 = mClient->emptyBuffer(
+ mNode, buffer, 0, src_length, flags, timestamp);
+ assert(err2 == OK);
+}
+
+void OMXDecoder::onRealFillBufferDone(const omx_message &msg) {
+ OMXMediaBuffer *media_buffer =
+ mMediaBufferMap.valueFor(msg.u.extended_buffer_data.buffer);
+
+ media_buffer->set_range(
+ msg.u.extended_buffer_data.range_offset,
+ msg.u.extended_buffer_data.range_length);
+
+ media_buffer->add_ref();
+
+ media_buffer->meta_data()->clear();
+
+ media_buffer->meta_data()->setInt32(
+ kKeyTimeUnits, msg.u.extended_buffer_data.timestamp);
+ media_buffer->meta_data()->setInt32(kKeyTimeScale, 1000);
+
+ if (msg.u.extended_buffer_data.flags & OMX_BUFFERFLAG_SYNCFRAME) {
+ media_buffer->meta_data()->setInt32(kKeyIsSyncFrame, true);
+ }
+
+ media_buffer->meta_data()->setPointer(
+ kKeyPlatformPrivate,
+ msg.u.extended_buffer_data.platform_private);
+
+ if (msg.u.extended_buffer_data.flags & OMX_BUFFERFLAG_EOS) {
+ mErrorCondition = ERROR_END_OF_STREAM;
+ }
+
+ mOutputBuffers.push_back(media_buffer);
+ mOutputBufferAvailable.signal();
+}
+
+void OMXDecoder::signalBufferReturned(MediaBuffer *_buffer) {
+ Mutex::Autolock autoLock(mLock);
+
+ OMXMediaBuffer *media_buffer = static_cast<OMXMediaBuffer *>(_buffer);
+
+ IOMX::buffer_id buffer = media_buffer->buffer_id();
+
+ PortStatus outputStatus = getPortStatus(kPortIndexOutput);
+ if (outputStatus == kPortStatusShutdown
+ || outputStatus == kPortStatusFlushing) {
+ mBuffers.editItemAt(kPortIndexOutput).push_back(buffer);
+ } else {
+ LOGV("[%s] Calling FillBuffer on buffer %p.", mComponentName, buffer);
+
+ status_t err = mClient->fillBuffer(mNode, buffer);
+ assert(err == NO_ERROR);
+ }
+}
+
+void OMXDecoder::freeInputBuffer(IOMX::buffer_id buffer) {
+ LOGV("freeInputBuffer %p", buffer);
+
+ status_t err = mOMX->free_buffer(mNode, kPortIndexInput, buffer);
+ assert(err == NO_ERROR);
+ mBufferMap.removeItem(buffer);
+
+ LOGV("freeInputBuffer %p done", buffer);
+}
+
+void OMXDecoder::freeOutputBuffer(IOMX::buffer_id buffer) {
+ LOGV("freeOutputBuffer %p", buffer);
+
+ status_t err = mOMX->free_buffer(mNode, kPortIndexOutput, buffer);
+ assert(err == NO_ERROR);
+ mBufferMap.removeItem(buffer);
+
+ ssize_t index = mMediaBufferMap.indexOfKey(buffer);
+ assert(index >= 0);
+ MediaBuffer *mbuffer = mMediaBufferMap.editValueAt(index);
+ mMediaBufferMap.removeItemsAt(index);
+ mbuffer->setObserver(NULL);
+ mbuffer->release();
+ mbuffer = NULL;
+
+ LOGV("freeOutputBuffer %p done", buffer);
+}
+
+void OMXDecoder::dumpPortDefinition(OMX_U32 port_index) {
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ def.nSize = sizeof(def);
+ def.nVersion.s.nVersionMajor = 1;
+ def.nVersion.s.nVersionMinor = 1;
+ def.nPortIndex = port_index;
+
+ status_t err = mOMX->get_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ assert(err == NO_ERROR);
+
+ LOGI("DumpPortDefinition on port %ld", port_index);
+ LOGI("nBufferCountActual = %ld, nBufferCountMin = %ld, nBufferSize = %ld",
+ def.nBufferCountActual, def.nBufferCountMin, def.nBufferSize);
+ switch (def.eDomain) {
+ case OMX_PortDomainAudio:
+ {
+ LOGI("eDomain = AUDIO");
+
+ if (port_index == kPortIndexOutput) {
+ OMX_AUDIO_PORTDEFINITIONTYPE *audio_def = &def.format.audio;
+ assert(audio_def->eEncoding == OMX_AUDIO_CodingPCM);
+
+ OMX_AUDIO_PARAM_PCMMODETYPE params;
+ params.nSize = sizeof(params);
+ params.nVersion.s.nVersionMajor = 1;
+ params.nVersion.s.nVersionMinor = 1;
+ params.nPortIndex = port_index;
+
+ err = mOMX->get_parameter(
+ mNode, OMX_IndexParamAudioPcm, &params, sizeof(params));
+ assert(err == OK);
+
+ assert(params.nChannels == 1 || params.bInterleaved);
+ assert(params.eNumData == OMX_NumericalDataSigned);
+ assert(params.nBitPerSample == 16);
+ assert(params.ePCMMode == OMX_AUDIO_PCMModeLinear);
+
+ LOGI("nChannels = %ld, nSamplingRate = %ld",
+ params.nChannels, params.nSamplingRate);
+ }
+
+ break;
+ }
+
+ case OMX_PortDomainVideo:
+ {
+ LOGI("eDomain = VIDEO");
+
+ OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
+ LOGI("nFrameWidth = %ld, nFrameHeight = %ld, nStride = %ld, "
+ "nSliceHeight = %ld",
+ video_def->nFrameWidth, video_def->nFrameHeight,
+ video_def->nStride, video_def->nSliceHeight);
+ LOGI("nBitrate = %ld, xFrameRate = %.2f",
+ video_def->nBitrate, video_def->xFramerate / 65536.0f);
+ LOGI("eCompressionFormat = %d, eColorFormat = %d",
+ video_def->eCompressionFormat, video_def->eColorFormat);
+
+ break;
+ }
+
+ default:
+ LOGI("eDomain = UNKNOWN");
+ break;
+ }
+}
+
+void OMXDecoder::postStart() {
+ omx_message msg;
+ msg.type = omx_message::START;
+ postMessage(msg);
+}
+
+void OMXDecoder::postEmptyBufferDone(IOMX::buffer_id buffer) {
+ omx_message msg;
+ msg.type = omx_message::EMPTY_BUFFER_DONE;
+ msg.u.buffer_data.node = mNode;
+ msg.u.buffer_data.buffer = buffer;
+ postMessage(msg);
+}
+
+void OMXDecoder::postInitialFillBuffer(IOMX::buffer_id buffer) {
+ omx_message msg;
+ msg.type = omx_message::INITIAL_FILL_BUFFER;
+ msg.u.buffer_data.node = mNode;
+ msg.u.buffer_data.buffer = buffer;
+ postMessage(msg);
+}
+
+} // namespace android
diff --git a/media/libstagefright/QComHardwareRenderer.cpp b/media/libstagefright/QComHardwareRenderer.cpp
new file mode 100644
index 000000000000..5a2379272cee
--- /dev/null
+++ b/media/libstagefright/QComHardwareRenderer.cpp
@@ -0,0 +1,139 @@
+/*
+ * 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.
+ */
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <binder/MemoryHeapBase.h>
+#include <binder/MemoryHeapPmem.h>
+#include <media/stagefright/QComHardwareRenderer.h>
+#include <ui/ISurface.h>
+
+namespace android {
+
+////////////////////////////////////////////////////////////////////////////////
+
+typedef struct PLATFORM_PRIVATE_ENTRY
+{
+ /* Entry type */
+ uint32_t type;
+
+ /* Pointer to platform specific entry */
+ void *entry;
+
+} PLATFORM_PRIVATE_ENTRY;
+
+typedef struct PLATFORM_PRIVATE_LIST
+{
+ /* Number of entries */
+ uint32_t nEntries;
+
+ /* Pointer to array of platform specific entries *
+ * Contiguous block of PLATFORM_PRIVATE_ENTRY elements */
+ PLATFORM_PRIVATE_ENTRY *entryList;
+
+} PLATFORM_PRIVATE_LIST;
+
+// data structures for tunneling buffers
+typedef struct PLATFORM_PRIVATE_PMEM_INFO
+{
+ /* pmem file descriptor */
+ uint32_t pmem_fd;
+ uint32_t offset;
+
+} PLATFORM_PRIVATE_PMEM_INFO;
+
+#define PLATFORM_PRIVATE_PMEM 1
+
+QComHardwareRenderer::QComHardwareRenderer(
+ const sp<ISurface> &surface,
+ size_t displayWidth, size_t displayHeight,
+ size_t decodedWidth, size_t decodedHeight)
+ : mISurface(surface),
+ mDisplayWidth(displayWidth),
+ mDisplayHeight(displayHeight),
+ mDecodedWidth(decodedWidth),
+ mDecodedHeight(decodedHeight),
+ mFrameSize((mDecodedWidth * mDecodedHeight * 3) / 2) {
+ assert(mISurface.get() != NULL);
+ assert(mDecodedWidth > 0);
+ assert(mDecodedHeight > 0);
+}
+
+QComHardwareRenderer::~QComHardwareRenderer() {
+ mISurface->unregisterBuffers();
+}
+
+void QComHardwareRenderer::render(
+ const void *data, size_t size, void *platformPrivate) {
+ size_t offset;
+ if (!getOffset(platformPrivate, &offset)) {
+ return;
+ }
+
+ mISurface->postBuffer(offset);
+}
+
+bool QComHardwareRenderer::getOffset(void *platformPrivate, size_t *offset) {
+ *offset = 0;
+
+ PLATFORM_PRIVATE_LIST *list = (PLATFORM_PRIVATE_LIST *)platformPrivate;
+ for (uint32_t i = 0; i < list->nEntries; ++i) {
+ if (list->entryList[i].type != PLATFORM_PRIVATE_PMEM) {
+ continue;
+ }
+
+ PLATFORM_PRIVATE_PMEM_INFO *info =
+ (PLATFORM_PRIVATE_PMEM_INFO *)list->entryList[i].entry;
+
+ if (info != NULL) {
+ if (mMemoryHeap.get() == NULL) {
+ publishBuffers(info->pmem_fd);
+ }
+
+ if (mMemoryHeap.get() == NULL) {
+ return false;
+ }
+
+ *offset = info->offset;
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void QComHardwareRenderer::publishBuffers(uint32_t pmem_fd) {
+ sp<MemoryHeapBase> master =
+ reinterpret_cast<MemoryHeapBase *>(pmem_fd);
+
+ master->setDevice("/dev/pmem");
+
+ mMemoryHeap = new MemoryHeapPmem(master, 0);
+ mMemoryHeap->slap();
+
+ ISurface::BufferHeap bufferHeap(
+ mDisplayWidth, mDisplayHeight,
+ mDecodedWidth, mDecodedHeight,
+ PIXEL_FORMAT_YCbCr_420_SP,
+ mMemoryHeap);
+
+ status_t err = mISurface->registerBuffers(bufferHeap);
+ assert(err == OK);
+}
+
+} // namespace android
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
new file mode 100644
index 000000000000..8f1fa67e7781
--- /dev/null
+++ b/media/libstagefright/SampleTable.cpp
@@ -0,0 +1,598 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "SampleTable"
+#include <utils/Log.h>
+
+#include <arpa/inet.h>
+#include <assert.h>
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/SampleTable.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+static const uint32_t kChunkOffsetType32 = FOURCC('s', 't', 'c', 'o');
+static const uint32_t kChunkOffsetType64 = FOURCC('c', 'o', '6', '4');
+static const uint32_t kSampleSizeType32 = FOURCC('s', 't', 's', 'z');
+static const uint32_t kSampleSizeTypeCompact = FOURCC('s', 't', 'z', '2');
+
+SampleTable::SampleTable(DataSource *source)
+ : mDataSource(source),
+ mChunkOffsetOffset(-1),
+ mChunkOffsetType(0),
+ mNumChunkOffsets(0),
+ mSampleToChunkOffset(-1),
+ mNumSampleToChunkOffsets(0),
+ mSampleSizeOffset(-1),
+ mSampleSizeFieldSize(0),
+ mDefaultSampleSize(0),
+ mNumSampleSizes(0),
+ mTimeToSampleCount(0),
+ mTimeToSample(NULL),
+ mSyncSampleOffset(-1),
+ mNumSyncSamples(0) {
+}
+
+SampleTable::~SampleTable() {
+ delete[] mTimeToSample;
+ mTimeToSample = NULL;
+}
+
+status_t SampleTable::setChunkOffsetParams(
+ uint32_t type, off_t data_offset, off_t data_size) {
+ if (mChunkOffsetOffset >= 0) {
+ return ERROR_MALFORMED;
+ }
+
+ assert(type == kChunkOffsetType32 || type == kChunkOffsetType64);
+
+ mChunkOffsetOffset = data_offset;
+ mChunkOffsetType = type;
+
+ if (data_size < 8) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t header[8];
+ if (mDataSource->read_at(
+ data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
+ return ERROR_IO;
+ }
+
+ if (U32_AT(header) != 0) {
+ // Expected version = 0, flags = 0.
+ return ERROR_MALFORMED;
+ }
+
+ mNumChunkOffsets = U32_AT(&header[4]);
+
+ if (mChunkOffsetType == kChunkOffsetType32) {
+ if (data_size < 8 + mNumChunkOffsets * 4) {
+ return ERROR_MALFORMED;
+ }
+ } else {
+ if (data_size < 8 + mNumChunkOffsets * 8) {
+ return ERROR_MALFORMED;
+ }
+ }
+
+ return OK;
+}
+
+status_t SampleTable::setSampleToChunkParams(
+ off_t data_offset, off_t data_size) {
+ if (mSampleToChunkOffset >= 0) {
+ return ERROR_MALFORMED;
+ }
+
+ mSampleToChunkOffset = data_offset;
+
+ if (data_size < 8) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t header[8];
+ if (mDataSource->read_at(
+ data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
+ return ERROR_IO;
+ }
+
+ if (U32_AT(header) != 0) {
+ // Expected version = 0, flags = 0.
+ return ERROR_MALFORMED;
+ }
+
+ mNumSampleToChunkOffsets = U32_AT(&header[4]);
+
+ if (data_size < 8 + mNumSampleToChunkOffsets * 12) {
+ return ERROR_MALFORMED;
+ }
+
+ return OK;
+}
+
+status_t SampleTable::setSampleSizeParams(
+ uint32_t type, off_t data_offset, off_t data_size) {
+ if (mSampleSizeOffset >= 0) {
+ return ERROR_MALFORMED;
+ }
+
+ assert(type == kSampleSizeType32 || type == kSampleSizeTypeCompact);
+
+ mSampleSizeOffset = data_offset;
+
+ if (data_size < 12) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t header[12];
+ if (mDataSource->read_at(
+ data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
+ return ERROR_IO;
+ }
+
+ if (U32_AT(header) != 0) {
+ // Expected version = 0, flags = 0.
+ return ERROR_MALFORMED;
+ }
+
+ mDefaultSampleSize = U32_AT(&header[4]);
+ mNumSampleSizes = U32_AT(&header[8]);
+
+ if (type == kSampleSizeType32) {
+ mSampleSizeFieldSize = 32;
+
+ if (mDefaultSampleSize != 0) {
+ return OK;
+ }
+
+ if (data_size < 12 + mNumSampleSizes * 4) {
+ return ERROR_MALFORMED;
+ }
+ } else {
+ if ((mDefaultSampleSize & 0xffffff00) != 0) {
+ // The high 24 bits are reserved and must be 0.
+ return ERROR_MALFORMED;
+ }
+
+ mSampleSizeFieldSize = mDefaultSampleSize & 0xf;
+ mDefaultSampleSize = 0;
+
+ if (mSampleSizeFieldSize != 4 && mSampleSizeFieldSize != 8
+ && mSampleSizeFieldSize != 16) {
+ return ERROR_MALFORMED;
+ }
+
+ if (data_size < 12 + (mNumSampleSizes * mSampleSizeFieldSize + 4) / 8) {
+ return ERROR_MALFORMED;
+ }
+ }
+
+ return OK;
+}
+
+status_t SampleTable::setTimeToSampleParams(
+ off_t data_offset, off_t data_size) {
+ if (mTimeToSample != NULL || data_size < 8) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t header[8];
+ if (mDataSource->read_at(
+ data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
+ return ERROR_IO;
+ }
+
+ if (U32_AT(header) != 0) {
+ // Expected version = 0, flags = 0.
+ return ERROR_MALFORMED;
+ }
+
+ mTimeToSampleCount = U32_AT(&header[4]);
+ mTimeToSample = new uint32_t[mTimeToSampleCount * 2];
+
+ size_t size = sizeof(uint32_t) * mTimeToSampleCount * 2;
+ if (mDataSource->read_at(
+ data_offset + 8, mTimeToSample, size) < (ssize_t)size) {
+ return ERROR_IO;
+ }
+
+ for (uint32_t i = 0; i < mTimeToSampleCount * 2; ++i) {
+ mTimeToSample[i] = ntohl(mTimeToSample[i]);
+ }
+
+ return OK;
+}
+
+status_t SampleTable::setSyncSampleParams(off_t data_offset, off_t data_size) {
+ if (mSyncSampleOffset >= 0 || data_size < 8) {
+ return ERROR_MALFORMED;
+ }
+
+ mSyncSampleOffset = data_offset;
+
+ uint8_t header[8];
+ if (mDataSource->read_at(
+ data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
+ return ERROR_IO;
+ }
+
+ if (U32_AT(header) != 0) {
+ // Expected version = 0, flags = 0.
+ return ERROR_MALFORMED;
+ }
+
+ mNumSyncSamples = U32_AT(&header[4]);
+
+ if (mNumSyncSamples < 2) {
+ LOGW("Table of sync samples is empty or has only a single entry!");
+ }
+ return OK;
+}
+
+uint32_t SampleTable::countChunkOffsets() const {
+ return mNumChunkOffsets;
+}
+
+status_t SampleTable::getChunkOffset(uint32_t chunk_index, off_t *offset) {
+ *offset = 0;
+
+ if (mChunkOffsetOffset < 0) {
+ return ERROR_MALFORMED;
+ }
+
+ if (chunk_index >= mNumChunkOffsets) {
+ return ERROR_OUT_OF_RANGE;
+ }
+
+ if (mChunkOffsetType == kChunkOffsetType32) {
+ uint32_t offset32;
+
+ if (mDataSource->read_at(
+ mChunkOffsetOffset + 8 + 4 * chunk_index,
+ &offset32,
+ sizeof(offset32)) < (ssize_t)sizeof(offset32)) {
+ return ERROR_IO;
+ }
+
+ *offset = ntohl(offset32);
+ } else {
+ assert(mChunkOffsetOffset == kChunkOffsetType64);
+
+ uint64_t offset64;
+ if (mDataSource->read_at(
+ mChunkOffsetOffset + 8 + 8 * chunk_index,
+ &offset64,
+ sizeof(offset64)) < (ssize_t)sizeof(offset64)) {
+ return ERROR_IO;
+ }
+
+ *offset = ntoh64(offset64);
+ }
+
+ return OK;
+}
+
+status_t SampleTable::getChunkForSample(
+ uint32_t sample_index,
+ uint32_t *chunk_index,
+ uint32_t *chunk_relative_sample_index,
+ uint32_t *desc_index) {
+ *chunk_index = 0;
+ *chunk_relative_sample_index = 0;
+ *desc_index = 0;
+
+ if (mSampleToChunkOffset < 0) {
+ return ERROR_MALFORMED;
+ }
+
+ if (sample_index >= countSamples()) {
+ return ERROR_END_OF_STREAM;
+ }
+
+ uint32_t first_chunk = 0;
+ uint32_t samples_per_chunk = 0;
+ uint32_t chunk_desc_index = 0;
+
+ uint32_t index = 0;
+ while (index < mNumSampleToChunkOffsets) {
+ uint8_t buffer[12];
+ if (mDataSource->read_at(mSampleToChunkOffset + 8 + index * 12,
+ buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
+ return ERROR_IO;
+ }
+
+ uint32_t stop_chunk = U32_AT(buffer);
+ if (sample_index < (stop_chunk - first_chunk) * samples_per_chunk) {
+ break;
+ }
+
+ sample_index -= (stop_chunk - first_chunk) * samples_per_chunk;
+ first_chunk = stop_chunk;
+ samples_per_chunk = U32_AT(&buffer[4]);
+ chunk_desc_index = U32_AT(&buffer[8]);
+
+ ++index;
+ }
+
+ *chunk_index = sample_index / samples_per_chunk + first_chunk - 1;
+ *chunk_relative_sample_index = sample_index % samples_per_chunk;
+ *desc_index = chunk_desc_index;
+
+ return OK;
+}
+
+uint32_t SampleTable::countSamples() const {
+ return mNumSampleSizes;
+}
+
+status_t SampleTable::getSampleSize(
+ uint32_t sample_index, size_t *sample_size) {
+ *sample_size = 0;
+
+ if (mSampleSizeOffset < 0) {
+ return ERROR_MALFORMED;
+ }
+
+ if (sample_index >= mNumSampleSizes) {
+ return ERROR_OUT_OF_RANGE;
+ }
+
+ if (mDefaultSampleSize > 0) {
+ *sample_size = mDefaultSampleSize;
+ return OK;
+ }
+
+ switch (mSampleSizeFieldSize) {
+ case 32:
+ {
+ if (mDataSource->read_at(
+ mSampleSizeOffset + 12 + 4 * sample_index,
+ sample_size, sizeof(*sample_size)) < (ssize_t)sizeof(*sample_size)) {
+ return ERROR_IO;
+ }
+
+ *sample_size = ntohl(*sample_size);
+ break;
+ }
+
+ case 16:
+ {
+ uint16_t x;
+ if (mDataSource->read_at(
+ mSampleSizeOffset + 12 + 2 * sample_index,
+ &x, sizeof(x)) < (ssize_t)sizeof(x)) {
+ return ERROR_IO;
+ }
+
+ *sample_size = ntohs(x);
+ break;
+ }
+
+ case 8:
+ {
+ uint8_t x;
+ if (mDataSource->read_at(
+ mSampleSizeOffset + 12 + sample_index,
+ &x, sizeof(x)) < (ssize_t)sizeof(x)) {
+ return ERROR_IO;
+ }
+
+ *sample_size = x;
+ break;
+ }
+
+ default:
+ {
+ assert(mSampleSizeFieldSize == 4);
+
+ uint8_t x;
+ if (mDataSource->read_at(
+ mSampleSizeOffset + 12 + sample_index / 2,
+ &x, sizeof(x)) < (ssize_t)sizeof(x)) {
+ return ERROR_IO;
+ }
+
+ *sample_size = (sample_index & 1) ? x & 0x0f : x >> 4;
+ break;
+ }
+ }
+
+ return OK;
+}
+
+status_t SampleTable::getSampleOffsetAndSize(
+ uint32_t sample_index, off_t *offset, size_t *size) {
+ Mutex::Autolock autoLock(mLock);
+
+ *offset = 0;
+ *size = 0;
+
+ uint32_t chunk_index;
+ uint32_t chunk_relative_sample_index;
+ uint32_t desc_index;
+ status_t err = getChunkForSample(
+ sample_index, &chunk_index, &chunk_relative_sample_index,
+ &desc_index);
+
+ if (err != OK) {
+ return err;
+ }
+
+ err = getChunkOffset(chunk_index, offset);
+
+ if (err != OK) {
+ return err;
+ }
+
+ for (uint32_t j = 0; j < chunk_relative_sample_index; ++j) {
+ size_t sample_size;
+ err = getSampleSize(sample_index - j - 1, &sample_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ *offset += sample_size;
+ }
+
+ err = getSampleSize(sample_index, size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ return OK;
+}
+
+status_t SampleTable::getMaxSampleSize(size_t *max_size) {
+ Mutex::Autolock autoLock(mLock);
+
+ *max_size = 0;
+
+ for (uint32_t i = 0; i < mNumSampleSizes; ++i) {
+ size_t sample_size;
+ status_t err = getSampleSize(i, &sample_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (sample_size > *max_size) {
+ *max_size = sample_size;
+ }
+ }
+
+ return OK;
+}
+
+status_t SampleTable::getDecodingTime(uint32_t sample_index, uint32_t *time) {
+ // XXX FIXME idiotic (for the common use-case) O(n) algorithm below...
+
+ Mutex::Autolock autoLock(mLock);
+
+ if (sample_index >= mNumSampleSizes) {
+ return ERROR_OUT_OF_RANGE;
+ }
+
+ uint32_t cur_sample = 0;
+ *time = 0;
+ for (uint32_t i = 0; i < mTimeToSampleCount; ++i) {
+ uint32_t n = mTimeToSample[2 * i];
+ uint32_t delta = mTimeToSample[2 * i + 1];
+
+ if (sample_index < cur_sample + n) {
+ *time += delta * (sample_index - cur_sample);
+
+ return OK;
+ }
+
+ *time += delta * n;
+ cur_sample += n;
+ }
+
+ return ERROR_OUT_OF_RANGE;
+}
+
+status_t SampleTable::findClosestSample(
+ uint32_t req_time, uint32_t *sample_index, uint32_t flags) {
+ Mutex::Autolock autoLock(mLock);
+
+ uint32_t cur_sample = 0;
+ uint32_t time = 0;
+ for (uint32_t i = 0; i < mTimeToSampleCount; ++i) {
+ uint32_t n = mTimeToSample[2 * i];
+ uint32_t delta = mTimeToSample[2 * i + 1];
+
+ if (req_time < time + n * delta) {
+ int j = (req_time - time) / delta;
+
+ *sample_index = cur_sample + j;
+
+ if (flags & kSyncSample_Flag) {
+ return findClosestSyncSample(*sample_index, sample_index);
+ }
+
+ return OK;
+ }
+
+ time += delta * n;
+ cur_sample += n;
+ }
+
+ return ERROR_OUT_OF_RANGE;
+}
+
+status_t SampleTable::findClosestSyncSample(
+ uint32_t start_sample_index, uint32_t *sample_index) {
+ *sample_index = 0;
+
+ if (mSyncSampleOffset < 0) {
+ // All samples are sync-samples.
+ *sample_index = start_sample_index;
+ return OK;
+ }
+
+ uint32_t x;
+ uint32_t left = 0;
+ uint32_t right = mNumSyncSamples;
+ while (left < right) {
+ uint32_t mid = (left + right) / 2;
+ if (mDataSource->read_at(
+ mSyncSampleOffset + 8 + (mid - 1) * 4, &x, 4) != 4) {
+ return ERROR_IO;
+ }
+
+ x = ntohl(x);
+
+ if (x < (start_sample_index + 1)) {
+ left = mid + 1;
+ } else if (x > (start_sample_index + 1)) {
+ right = mid;
+ } else {
+ break;
+ }
+ }
+
+#if 1
+ // Make sure we return a sample at or _after_ the requested one.
+ // Seeking to a particular time in a media source containing audio and
+ // video will most likely be able to sync fairly close to the requested
+ // time in the audio track but may only be able to seek a fair distance
+ // from the requested time in the video track.
+ // If we seek the video track to a time earlier than the audio track,
+ // we'll cause the video track to be late for quite a while, the decoder
+ // trying to catch up.
+ // If we seek the video track to a time later than the audio track,
+ // audio will start playing fine while no video will be output for a
+ // while, the video decoder will not stress the system.
+ if (mDataSource->read_at(
+ mSyncSampleOffset + 8 + (left - 1) * 4, &x, 4) != 4) {
+ return ERROR_IO;
+ }
+ x = ntohl(x);
+ assert((x - 1) >= start_sample_index);
+#endif
+
+ *sample_index = x - 1;
+
+ return OK;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/ShoutcastSource.cpp b/media/libstagefright/ShoutcastSource.cpp
new file mode 100644
index 000000000000..17b626e8bea9
--- /dev/null
+++ b/media/libstagefright/ShoutcastSource.cpp
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+#include <stdlib.h>
+
+#include <media/stagefright/HTTPStream.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/ShoutcastSource.h>
+#include <media/stagefright/string.h>
+
+namespace android {
+
+ShoutcastSource::ShoutcastSource(HTTPStream *http)
+ : mHttp(http),
+ mMetaDataOffset(0),
+ mBytesUntilMetaData(0),
+ mGroup(NULL),
+ mStarted(false) {
+ string metaint;
+ if (mHttp->find_header_value("icy-metaint", &metaint)) {
+ char *end;
+ const char *start = metaint.c_str();
+ mMetaDataOffset = strtol(start, &end, 10);
+ assert(end > start && *end == '\0');
+ assert(mMetaDataOffset > 0);
+
+ mBytesUntilMetaData = mMetaDataOffset;
+ }
+}
+
+ShoutcastSource::~ShoutcastSource() {
+ if (mStarted) {
+ stop();
+ }
+
+ delete mHttp;
+ mHttp = NULL;
+}
+
+status_t ShoutcastSource::start(MetaData *) {
+ assert(!mStarted);
+
+ mGroup = new MediaBufferGroup;
+ mGroup->add_buffer(new MediaBuffer(4096)); // XXX
+
+ mStarted = true;
+
+ return OK;
+}
+
+status_t ShoutcastSource::stop() {
+ assert(mStarted);
+
+ delete mGroup;
+ mGroup = NULL;
+
+ mStarted = false;
+
+ return OK;
+}
+
+sp<MetaData> ShoutcastSource::getFormat() {
+ sp<MetaData> meta = new MetaData;
+ meta->setCString(kKeyMIMEType, "audio/mpeg");
+ meta->setInt32(kKeySampleRate, 44100);
+ meta->setInt32(kKeyChannelCount, 2); // XXX
+
+ return meta;
+}
+
+status_t ShoutcastSource::read(
+ MediaBuffer **out, const ReadOptions *options) {
+ assert(mStarted);
+
+ *out = NULL;
+
+ int64_t seekTimeUs;
+ if (options && options->getSeekTo(&seekTimeUs)) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ MediaBuffer *buffer;
+ status_t err = mGroup->acquire_buffer(&buffer);
+ if (err != OK) {
+ return err;
+ }
+
+ *out = buffer;
+
+ size_t num_bytes = buffer->size();
+ if (mMetaDataOffset > 0 && num_bytes > mBytesUntilMetaData) {
+ num_bytes = mBytesUntilMetaData;
+ }
+
+ ssize_t n = mHttp->receive(buffer->data(), num_bytes);
+
+ if (n <= 0) {
+ return (status_t)n;
+ }
+
+ buffer->set_range(0, n);
+
+ mBytesUntilMetaData -= (size_t)n;
+
+ if (mBytesUntilMetaData == 0) {
+ unsigned char num_16_byte_blocks = 0;
+ n = mHttp->receive((char *)&num_16_byte_blocks, 1);
+ assert(n == 1);
+
+ char meta[255 * 16];
+ size_t meta_size = num_16_byte_blocks * 16;
+ size_t meta_length = 0;
+ while (meta_length < meta_size) {
+ n = mHttp->receive(&meta[meta_length], meta_size - meta_length);
+ if (n <= 0) {
+ return (status_t)n;
+ }
+
+ meta_length += (size_t) n;
+ }
+
+ while (meta_length > 0 && meta[meta_length - 1] == '\0') {
+ --meta_length;
+ }
+
+ if (meta_length > 0) {
+ // Technically we should probably attach this meta data to the
+ // next buffer. XXX
+ buffer->meta_data()->setData('shou', 'shou', meta, meta_length);
+ }
+
+ mBytesUntilMetaData = mMetaDataOffset;
+ }
+
+ return OK;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/SoftwareRenderer.cpp b/media/libstagefright/SoftwareRenderer.cpp
new file mode 100644
index 000000000000..66b6b07a6be2
--- /dev/null
+++ b/media/libstagefright/SoftwareRenderer.cpp
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "SoftwareRenderer"
+#include <utils/Log.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <binder/MemoryHeapBase.h>
+#include <media/stagefright/SoftwareRenderer.h>
+#include <ui/ISurface.h>
+
+namespace android {
+
+#define QCOM_YUV 0
+
+SoftwareRenderer::SoftwareRenderer(
+ const sp<ISurface> &surface,
+ size_t displayWidth, size_t displayHeight,
+ size_t decodedWidth, size_t decodedHeight)
+ : mISurface(surface),
+ mDisplayWidth(displayWidth),
+ mDisplayHeight(displayHeight),
+ mDecodedWidth(decodedWidth),
+ mDecodedHeight(decodedHeight),
+ mFrameSize(mDecodedWidth * mDecodedHeight * 2), // RGB565
+ mMemoryHeap(new MemoryHeapBase(2 * mFrameSize)),
+ mIndex(0) {
+ assert(mISurface.get() != NULL);
+ assert(mDecodedWidth > 0);
+ assert(mDecodedHeight > 0);
+ assert(mMemoryHeap->heapID() >= 0);
+
+ ISurface::BufferHeap bufferHeap(
+ mDisplayWidth, mDisplayHeight,
+ mDecodedWidth, mDecodedHeight,
+ PIXEL_FORMAT_RGB_565,
+ mMemoryHeap);
+
+ status_t err = mISurface->registerBuffers(bufferHeap);
+ assert(err == OK);
+}
+
+SoftwareRenderer::~SoftwareRenderer() {
+ mISurface->unregisterBuffers();
+}
+
+void SoftwareRenderer::render(
+ const void *data, size_t size, void *platformPrivate) {
+ assert(size >= (mDecodedWidth * mDecodedHeight * 3) / 2);
+
+ static const signed kClipMin = -278;
+ static const signed kClipMax = 535;
+ static uint8_t kClip[kClipMax - kClipMin + 1];
+ static uint8_t *kAdjustedClip = &kClip[-kClipMin];
+
+ static bool clipInitialized = false;
+
+ if (!clipInitialized) {
+ for (signed i = kClipMin; i <= kClipMax; ++i) {
+ kClip[i - kClipMin] = (i < 0) ? 0 : (i > 255) ? 255 : (uint8_t)i;
+ }
+ clipInitialized = true;
+ }
+
+ size_t offset = mIndex * mFrameSize;
+
+ void *dst = (uint8_t *)mMemoryHeap->getBase() + offset;
+
+ uint32_t *dst_ptr = (uint32_t *)dst;
+
+ const uint8_t *src_y = (const uint8_t *)data;
+
+ const uint8_t *src_u =
+ (const uint8_t *)src_y + mDecodedWidth * mDecodedHeight;
+
+#if !QCOM_YUV
+ const uint8_t *src_v =
+ (const uint8_t *)src_u + (mDecodedWidth / 2) * (mDecodedHeight / 2);
+#endif
+
+ for (size_t y = 0; y < mDecodedHeight; ++y) {
+ for (size_t x = 0; x < mDecodedWidth; x += 2) {
+ // B = 1.164 * (Y - 16) + 2.018 * (U - 128)
+ // G = 1.164 * (Y - 16) - 0.813 * (V - 128) - 0.391 * (U - 128)
+ // R = 1.164 * (Y - 16) + 1.596 * (V - 128)
+
+ // B = 298/256 * (Y - 16) + 517/256 * (U - 128)
+ // G = .................. - 208/256 * (V - 128) - 100/256 * (U - 128)
+ // R = .................. + 409/256 * (V - 128)
+
+ // min_B = (298 * (- 16) + 517 * (- 128)) / 256 = -277
+ // min_G = (298 * (- 16) - 208 * (255 - 128) - 100 * (255 - 128)) / 256 = -172
+ // min_R = (298 * (- 16) + 409 * (- 128)) / 256 = -223
+
+ // max_B = (298 * (255 - 16) + 517 * (255 - 128)) / 256 = 534
+ // max_G = (298 * (255 - 16) - 208 * (- 128) - 100 * (- 128)) / 256 = 432
+ // max_R = (298 * (255 - 16) + 409 * (255 - 128)) / 256 = 481
+
+ // clip range -278 .. 535
+
+ signed y1 = (signed)src_y[x] - 16;
+ signed y2 = (signed)src_y[x + 1] - 16;
+
+#if QCOM_YUV
+ signed u = (signed)src_u[x & ~1] - 128;
+ signed v = (signed)src_u[(x & ~1) + 1] - 128;
+#else
+ signed u = (signed)src_u[x / 2] - 128;
+ signed v = (signed)src_v[x / 2] - 128;
+#endif
+
+ signed u_b = u * 517;
+ signed u_g = -u * 100;
+ signed v_g = -v * 208;
+ signed v_r = v * 409;
+
+ signed tmp1 = y1 * 298;
+ signed b1 = (tmp1 + u_b) / 256;
+ signed g1 = (tmp1 + v_g + u_g) / 256;
+ signed r1 = (tmp1 + v_r) / 256;
+
+ signed tmp2 = y2 * 298;
+ signed b2 = (tmp2 + u_b) / 256;
+ signed g2 = (tmp2 + v_g + u_g) / 256;
+ signed r2 = (tmp2 + v_r) / 256;
+
+ uint32_t rgb1 =
+ ((kAdjustedClip[r1] >> 3) << 11)
+ | ((kAdjustedClip[g1] >> 2) << 5)
+ | (kAdjustedClip[b1] >> 3);
+
+ uint32_t rgb2 =
+ ((kAdjustedClip[r2] >> 3) << 11)
+ | ((kAdjustedClip[g2] >> 2) << 5)
+ | (kAdjustedClip[b2] >> 3);
+
+ dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
+ }
+
+ src_y += mDecodedWidth;
+
+ if (y & 1) {
+#if QCOM_YUV
+ src_u += mDecodedWidth;
+#else
+ src_u += mDecodedWidth / 2;
+ src_v += mDecodedWidth / 2;
+#endif
+ }
+
+ dst_ptr += mDecodedWidth / 2;
+ }
+
+ mISurface->postBuffer(offset);
+ mIndex = 1 - mIndex;
+}
+
+} // namespace android
diff --git a/media/libstagefright/SurfaceRenderer.cpp b/media/libstagefright/SurfaceRenderer.cpp
new file mode 100644
index 000000000000..e54288d97ab3
--- /dev/null
+++ b/media/libstagefright/SurfaceRenderer.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "SurfaceRenderer"
+#include <utils/Log.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <media/stagefright/SurfaceRenderer.h>
+#include <ui/Surface.h>
+
+namespace android {
+
+SurfaceRenderer::SurfaceRenderer(
+ const sp<Surface> &surface,
+ size_t displayWidth, size_t displayHeight,
+ size_t decodedWidth, size_t decodedHeight)
+ : mSurface(surface),
+ mDisplayWidth(displayWidth),
+ mDisplayHeight(displayHeight),
+ mDecodedWidth(decodedWidth),
+ mDecodedHeight(decodedHeight) {
+}
+
+SurfaceRenderer::~SurfaceRenderer() {
+}
+
+void SurfaceRenderer::render(
+ const void *data, size_t size, void *platformPrivate) {
+ Surface::SurfaceInfo info;
+ status_t err = mSurface->lock(&info);
+ if (err != OK) {
+ return;
+ }
+
+ const uint8_t *src = (const uint8_t *)data;
+ uint8_t *dst = (uint8_t *)info.bits;
+
+ for (size_t i = 0; i < mDisplayHeight; ++i) {
+ memcpy(dst, src, mDisplayWidth);
+ src += mDecodedWidth;
+ dst += mDisplayWidth;
+ }
+ src += (mDecodedHeight - mDisplayHeight) * mDecodedWidth;
+
+ for (size_t i = 0; i < (mDisplayHeight + 1) / 2; ++i) {
+ memcpy(dst, src, (mDisplayWidth + 1) & ~1);
+ src += (mDecodedWidth + 1) & ~1;
+ dst += (mDisplayWidth + 1) & ~1;
+ }
+
+ mSurface->unlockAndPost();
+}
+
+} // namespace android
diff --git a/media/libstagefright/TimeSource.cpp b/media/libstagefright/TimeSource.cpp
new file mode 100644
index 000000000000..d987fbf1c731
--- /dev/null
+++ b/media/libstagefright/TimeSource.cpp
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+#include <stddef.h>
+#include <sys/time.h>
+
+#include <media/stagefright/TimeSource.h>
+
+namespace android {
+
+SystemTimeSource::SystemTimeSource()
+ : mStartTimeUs(GetSystemTimeUs()) {
+}
+
+int64_t SystemTimeSource::getRealTimeUs() {
+ return GetSystemTimeUs() - mStartTimeUs;
+}
+
+// static
+int64_t SystemTimeSource::GetSystemTimeUs() {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+
+ return (int64_t)tv.tv_sec * 1000000 + tv.tv_usec;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/TimedEventQueue.cpp b/media/libstagefright/TimedEventQueue.cpp
new file mode 100644
index 000000000000..2f8a19f86dd4
--- /dev/null
+++ b/media/libstagefright/TimedEventQueue.cpp
@@ -0,0 +1,207 @@
+/*
+ * 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.
+ */
+
+#undef __STRICT_ANSI__
+#define __STDINT_LIMITS
+#define __STDC_LIMIT_MACROS
+#include <stdint.h>
+
+#define LOG_TAG "TimedEventQueue"
+#include <utils/Log.h>
+
+#include <sys/time.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <media/stagefright/TimedEventQueue.h>
+
+namespace android {
+
+TimedEventQueue::TimedEventQueue()
+ : mRunning(false),
+ mStopped(false) {
+}
+
+TimedEventQueue::~TimedEventQueue() {
+ stop();
+}
+
+void TimedEventQueue::start() {
+ if (mRunning) {
+ return;
+ }
+
+ mStopped = false;
+
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+ pthread_create(&mThread, &attr, ThreadWrapper, this);
+
+ pthread_attr_destroy(&attr);
+
+ mRunning = true;
+}
+
+void TimedEventQueue::stop(bool flush) {
+ if (!mRunning) {
+ return;
+ }
+
+ if (flush) {
+ postEventToBack(new StopEvent);
+ } else {
+ postTimedEvent(new StopEvent, INT64_MIN);
+ }
+
+ void *dummy;
+ pthread_join(mThread, &dummy);
+
+ mQueue.clear();
+
+ mRunning = false;
+}
+
+void TimedEventQueue::postEvent(const sp<Event> &event) {
+ // Reserve an earlier timeslot an INT64_MIN to be able to post
+ // the StopEvent to the absolute head of the queue.
+ postTimedEvent(event, INT64_MIN + 1);
+}
+
+void TimedEventQueue::postEventToBack(const sp<Event> &event) {
+ postTimedEvent(event, INT64_MAX);
+}
+
+void TimedEventQueue::postEventWithDelay(
+ const sp<Event> &event, int64_t delay_us) {
+ assert(delay_us >= 0);
+ postTimedEvent(event, getRealTimeUs() + delay_us);
+}
+
+void TimedEventQueue::postTimedEvent(
+ const sp<Event> &event, int64_t realtime_us) {
+ Mutex::Autolock autoLock(mLock);
+
+ List<QueueItem>::iterator it = mQueue.begin();
+ while (it != mQueue.end() && realtime_us >= (*it).realtime_us) {
+ ++it;
+ }
+
+ QueueItem item;
+ item.event = event;
+ item.realtime_us = realtime_us;
+
+ if (it == mQueue.begin()) {
+ mQueueHeadChangedCondition.signal();
+ }
+
+ mQueue.insert(it, item);
+
+ mQueueNotEmptyCondition.signal();
+}
+
+bool TimedEventQueue::cancelEvent(const sp<Event> &event) {
+ Mutex::Autolock autoLock(mLock);
+
+ List<QueueItem>::iterator it = mQueue.begin();
+ while (it != mQueue.end() && (*it).event != event) {
+ ++it;
+ }
+
+ if (it == mQueue.end()) {
+ return false;
+ }
+
+ if (it == mQueue.begin()) {
+ mQueueHeadChangedCondition.signal();
+ }
+
+ mQueue.erase(it);
+
+ return true;
+}
+
+// static
+int64_t TimedEventQueue::getRealTimeUs() {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+
+ return (int64_t)tv.tv_sec * 1000000 + tv.tv_usec;
+}
+
+// static
+void *TimedEventQueue::ThreadWrapper(void *me) {
+ static_cast<TimedEventQueue *>(me)->threadEntry();
+
+ return NULL;
+}
+
+void TimedEventQueue::threadEntry() {
+ for (;;) {
+ int64_t now_us;
+ sp<Event> event;
+
+ {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mStopped) {
+ break;
+ }
+
+ while (mQueue.empty()) {
+ mQueueNotEmptyCondition.wait(mLock);
+ }
+
+ List<QueueItem>::iterator it;
+ for (;;) {
+ it = mQueue.begin();
+
+ now_us = getRealTimeUs();
+ int64_t when_us = (*it).realtime_us;
+
+ int64_t delay_us;
+ if (when_us < 0 || when_us == INT64_MAX) {
+ delay_us = 0;
+ } else {
+ delay_us = when_us - now_us;
+ }
+
+ if (delay_us <= 0) {
+ break;
+ }
+
+ status_t err = mQueueHeadChangedCondition.waitRelative(
+ mLock, delay_us * 1000);
+
+ if (err == -ETIMEDOUT) {
+ now_us = getRealTimeUs();
+ break;
+ }
+ }
+
+ event = (*it).event;
+ mQueue.erase(it);
+ }
+
+ // Fire event with the lock NOT held.
+ event->fire(this, now_us);
+ }
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
new file mode 100644
index 000000000000..2720f93e14e0
--- /dev/null
+++ b/media/libstagefright/Utils.cpp
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+#include <arpa/inet.h>
+
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+uint16_t U16_AT(const uint8_t *ptr) {
+ return ptr[0] << 8 | ptr[1];
+}
+
+uint32_t U32_AT(const uint8_t *ptr) {
+ return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
+}
+
+uint64_t U64_AT(const uint8_t *ptr) {
+ return ((uint64_t)U32_AT(ptr)) << 32 | U32_AT(ptr + 4);
+}
+
+// XXX warning: these won't work on big-endian host.
+uint64_t ntoh64(uint64_t x) {
+ return ((uint64_t)ntohl(x & 0xffffffff) << 32) | ntohl(x >> 32);
+}
+
+uint64_t hton64(uint64_t x) {
+ return ((uint64_t)htonl(x & 0xffffffff) << 32) | htonl(x >> 32);
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk
new file mode 100644
index 000000000000..9c6d475bbf50
--- /dev/null
+++ b/media/libstagefright/omx/Android.mk
@@ -0,0 +1,27 @@
+ifeq ($(BUILD_WITH_STAGEFRIGHT),true)
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# Set up the OpenCore variables.
+include external/opencore/Config.mk
+LOCAL_C_INCLUDES := $(PV_INCLUDES)
+LOCAL_CFLAGS := $(PV_CFLAGS_MINUS_VISIBILITY)
+
+LOCAL_SRC_FILES:= \
+ OMX.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libbinder \
+ libmedia \
+ libutils \
+ libui \
+ libopencore_common
+
+LOCAL_PRELINK_MODULE:= false
+
+LOCAL_MODULE:= libstagefright_omx
+
+include $(BUILD_SHARED_LIBRARY)
+
+endif
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
new file mode 100644
index 000000000000..daaa741ed26c
--- /dev/null
+++ b/media/libstagefright/omx/OMX.cpp
@@ -0,0 +1,623 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "OMX"
+#include <utils/Log.h>
+
+#include <sys/socket.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include "OMX.h"
+#include "pv_omxcore.h"
+
+#include <binder/IMemory.h>
+
+#include <OMX_Component.h>
+
+namespace android {
+
+class NodeMeta {
+public:
+ NodeMeta(OMX *owner)
+ : mOwner(owner),
+ mHandle(NULL) {
+ }
+
+ OMX *owner() const {
+ return mOwner;
+ }
+
+ void setHandle(OMX_HANDLETYPE handle) {
+ assert(mHandle == NULL);
+ mHandle = handle;
+ }
+
+ OMX_HANDLETYPE handle() const {
+ return mHandle;
+ }
+
+ void setObserver(const sp<IOMXObserver> &observer) {
+ mObserver = observer;
+ }
+
+ sp<IOMXObserver> observer() {
+ return mObserver;
+ }
+
+private:
+ OMX *mOwner;
+ OMX_HANDLETYPE mHandle;
+ sp<IOMXObserver> mObserver;
+
+ NodeMeta(const NodeMeta &);
+ NodeMeta &operator=(const NodeMeta &);
+};
+
+class BufferMeta {
+public:
+ BufferMeta(OMX *owner, const sp<IMemory> &mem, bool is_backup = false)
+ : mOwner(owner),
+ mMem(mem),
+ mIsBackup(is_backup) {
+ }
+
+ BufferMeta(OMX *owner, size_t size)
+ : mOwner(owner),
+ mSize(size),
+ mIsBackup(false) {
+ }
+
+ void CopyFromOMX(const OMX_BUFFERHEADERTYPE *header) {
+ if (!mIsBackup) {
+ return;
+ }
+
+ memcpy((OMX_U8 *)mMem->pointer() + header->nOffset,
+ header->pBuffer + header->nOffset,
+ header->nFilledLen);
+ }
+
+ void CopyToOMX(const OMX_BUFFERHEADERTYPE *header) {
+ if (!mIsBackup) {
+ return;
+ }
+
+ memcpy(header->pBuffer + header->nOffset,
+ (const OMX_U8 *)mMem->pointer() + header->nOffset,
+ header->nFilledLen);
+ }
+
+private:
+ OMX *mOwner;
+ sp<IMemory> mMem;
+ size_t mSize;
+ bool mIsBackup;
+
+ BufferMeta(const BufferMeta &);
+ BufferMeta &operator=(const BufferMeta &);
+};
+
+// static
+OMX_CALLBACKTYPE OMX::kCallbacks = {
+ &OnEvent, &OnEmptyBufferDone, &OnFillBufferDone
+};
+
+// static
+OMX_ERRORTYPE OMX::OnEvent(
+ OMX_IN OMX_HANDLETYPE hComponent,
+ OMX_IN OMX_PTR pAppData,
+ OMX_IN OMX_EVENTTYPE eEvent,
+ OMX_IN OMX_U32 nData1,
+ OMX_IN OMX_U32 nData2,
+ OMX_IN OMX_PTR pEventData) {
+ NodeMeta *meta = static_cast<NodeMeta *>(pAppData);
+ return meta->owner()->OnEvent(meta, eEvent, nData1, nData2, pEventData);
+}
+
+// static
+OMX_ERRORTYPE OMX::OnEmptyBufferDone(
+ OMX_IN OMX_HANDLETYPE hComponent,
+ OMX_IN OMX_PTR pAppData,
+ OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) {
+ NodeMeta *meta = static_cast<NodeMeta *>(pAppData);
+ return meta->owner()->OnEmptyBufferDone(meta, pBuffer);
+}
+
+// static
+OMX_ERRORTYPE OMX::OnFillBufferDone(
+ OMX_IN OMX_HANDLETYPE hComponent,
+ OMX_IN OMX_PTR pAppData,
+ OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) {
+ NodeMeta *meta = static_cast<NodeMeta *>(pAppData);
+ return meta->owner()->OnFillBufferDone(meta, pBuffer);
+}
+
+OMX::OMX()
+#if IOMX_USES_SOCKETS
+ : mSock(-1)
+#endif
+{
+}
+
+OMX::~OMX() {
+#if IOMX_USES_SOCKETS
+ assert(mSock < 0);
+#endif
+}
+
+#if IOMX_USES_SOCKETS
+status_t OMX::connect(int *sd) {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mSock >= 0) {
+ return UNKNOWN_ERROR;
+ }
+
+ int sockets[2];
+ if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets) < 0) {
+ return UNKNOWN_ERROR;
+ }
+
+ mSock = sockets[0];
+ *sd = sockets[1];
+
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+ int err = pthread_create(&mThread, &attr, ThreadWrapper, this);
+ assert(err == 0);
+
+ pthread_attr_destroy(&attr);
+
+ return OK;
+}
+
+// static
+void *OMX::ThreadWrapper(void *me) {
+ ((OMX *)me)->threadEntry();
+
+ return NULL;
+}
+
+void OMX::threadEntry() {
+ bool done = false;
+ while (!done) {
+ omx_message msg;
+ ssize_t n = recv(mSock, &msg, sizeof(msg), 0);
+
+ if (n <= 0) {
+ break;
+ }
+
+ Mutex::Autolock autoLock(mLock);
+
+ switch (msg.type) {
+ case omx_message::FILL_BUFFER:
+ {
+ OMX_BUFFERHEADERTYPE *header =
+ static_cast<OMX_BUFFERHEADERTYPE *>(
+ msg.u.buffer_data.buffer);
+
+ header->nFilledLen = 0;
+ header->nOffset = 0;
+ header->hMarkTargetComponent = NULL;
+ header->nFlags = 0;
+
+ NodeMeta *node_meta = static_cast<NodeMeta *>(
+ msg.u.buffer_data.node);
+
+ LOGV("FillThisBuffer buffer=%p", header);
+
+ OMX_ERRORTYPE err =
+ OMX_FillThisBuffer(node_meta->handle(), header);
+ assert(err == OMX_ErrorNone);
+ break;
+ }
+
+ case omx_message::EMPTY_BUFFER:
+ {
+ OMX_BUFFERHEADERTYPE *header =
+ static_cast<OMX_BUFFERHEADERTYPE *>(
+ msg.u.extended_buffer_data.buffer);
+
+ header->nFilledLen = msg.u.extended_buffer_data.range_length;
+ header->nOffset = msg.u.extended_buffer_data.range_offset;
+ header->hMarkTargetComponent = NULL;
+ header->nFlags = msg.u.extended_buffer_data.flags;
+ header->nTimeStamp = msg.u.extended_buffer_data.timestamp;
+
+ BufferMeta *buffer_meta =
+ static_cast<BufferMeta *>(header->pAppPrivate);
+ buffer_meta->CopyToOMX(header);
+
+ NodeMeta *node_meta = static_cast<NodeMeta *>(
+ msg.u.extended_buffer_data.node);
+
+ LOGV("EmptyThisBuffer buffer=%p", header);
+
+ OMX_ERRORTYPE err =
+ OMX_EmptyThisBuffer(node_meta->handle(), header);
+ assert(err == OMX_ErrorNone);
+ break;
+ }
+
+ case omx_message::SEND_COMMAND:
+ {
+ NodeMeta *node_meta = static_cast<NodeMeta *>(
+ msg.u.send_command_data.node);
+
+ OMX_ERRORTYPE err =
+ OMX_SendCommand(
+ node_meta->handle(), msg.u.send_command_data.cmd,
+ msg.u.send_command_data.param, NULL);
+ assert(err == OMX_ErrorNone);
+ break;
+ }
+
+ case omx_message::DISCONNECT:
+ {
+ omx_message msg;
+ msg.type = omx_message::DISCONNECTED;
+ ssize_t n = send(mSock, &msg, sizeof(msg), 0);
+ assert(n > 0 && static_cast<size_t>(n) == sizeof(msg));
+ done = true;
+ break;
+ }
+
+ default:
+ LOGE("received unknown omx_message type %d", msg.type);
+ break;
+ }
+ }
+
+ Mutex::Autolock autoLock(mLock);
+ close(mSock);
+ mSock = -1;
+}
+#endif
+
+status_t OMX::list_nodes(List<String8> *list) {
+ OMX_MasterInit(); // XXX Put this somewhere else.
+
+ list->clear();
+
+ OMX_U32 index = 0;
+ char componentName[256];
+ while (OMX_MasterComponentNameEnum(componentName, sizeof(componentName), index)
+ == OMX_ErrorNone) {
+ list->push_back(String8(componentName));
+
+ ++index;
+ }
+
+ return OK;
+}
+
+status_t OMX::allocate_node(const char *name, node_id *node) {
+ Mutex::Autolock autoLock(mLock);
+
+ *node = 0;
+
+ OMX_MasterInit(); // XXX Put this somewhere else.
+
+ NodeMeta *meta = new NodeMeta(this);
+
+ OMX_HANDLETYPE handle;
+ OMX_ERRORTYPE err = OMX_MasterGetHandle(
+ &handle, const_cast<char *>(name), meta, &kCallbacks);
+
+ if (err != OMX_ErrorNone) {
+ LOGE("FAILED to allocate omx component '%s'", name);
+
+ delete meta;
+ meta = NULL;
+
+ return UNKNOWN_ERROR;
+ }
+
+ meta->setHandle(handle);
+
+ *node = meta;
+
+ return OK;
+}
+
+status_t OMX::free_node(node_id node) {
+ Mutex::Autolock autoLock(mLock);
+
+ NodeMeta *meta = static_cast<NodeMeta *>(node);
+
+ OMX_ERRORTYPE err = OMX_MasterFreeHandle(meta->handle());
+
+ delete meta;
+ meta = NULL;
+
+ return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
+}
+
+status_t OMX::send_command(
+ node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param) {
+ Mutex::Autolock autoLock(mLock);
+
+#if IOMX_USES_SOCKETS
+ if (mSock < 0) {
+ return UNKNOWN_ERROR;
+ }
+#endif
+
+ NodeMeta *meta = static_cast<NodeMeta *>(node);
+ OMX_ERRORTYPE err = OMX_SendCommand(meta->handle(), cmd, param, NULL);
+
+ return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
+}
+
+status_t OMX::get_parameter(
+ node_id node, OMX_INDEXTYPE index,
+ void *params, size_t size) {
+ Mutex::Autolock autoLock(mLock);
+
+ NodeMeta *meta = static_cast<NodeMeta *>(node);
+ OMX_ERRORTYPE err = OMX_GetParameter(meta->handle(), index, params);
+
+ return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
+}
+
+status_t OMX::set_parameter(
+ node_id node, OMX_INDEXTYPE index,
+ const void *params, size_t size) {
+ Mutex::Autolock autoLock(mLock);
+
+ NodeMeta *meta = static_cast<NodeMeta *>(node);
+ OMX_ERRORTYPE err =
+ OMX_SetParameter(meta->handle(), index, const_cast<void *>(params));
+
+ return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
+}
+
+status_t OMX::use_buffer(
+ node_id node, OMX_U32 port_index, const sp<IMemory> &params,
+ buffer_id *buffer) {
+ Mutex::Autolock autoLock(mLock);
+
+ BufferMeta *buffer_meta = new BufferMeta(this, params);
+
+ OMX_BUFFERHEADERTYPE *header;
+
+ NodeMeta *node_meta = static_cast<NodeMeta *>(node);
+ OMX_ERRORTYPE err =
+ OMX_UseBuffer(node_meta->handle(), &header, port_index, buffer_meta,
+ params->size(), static_cast<OMX_U8 *>(params->pointer()));
+
+ if (err != OMX_ErrorNone) {
+ LOGE("OMX_UseBuffer failed with error %d (0x%08x)", err, err);
+
+ delete buffer_meta;
+ buffer_meta = NULL;
+
+ *buffer = 0;
+ return UNKNOWN_ERROR;
+ }
+
+ *buffer = header;
+
+ return OK;
+}
+
+status_t OMX::allocate_buffer(
+ node_id node, OMX_U32 port_index, size_t size,
+ buffer_id *buffer) {
+ Mutex::Autolock autoLock(mLock);
+
+ BufferMeta *buffer_meta = new BufferMeta(this, size);
+
+ OMX_BUFFERHEADERTYPE *header;
+
+ NodeMeta *node_meta = static_cast<NodeMeta *>(node);
+ OMX_ERRORTYPE err =
+ OMX_AllocateBuffer(node_meta->handle(), &header, port_index,
+ buffer_meta, size);
+
+ if (err != OMX_ErrorNone) {
+ delete buffer_meta;
+ buffer_meta = NULL;
+
+ *buffer = 0;
+ return UNKNOWN_ERROR;
+ }
+
+ *buffer = header;
+
+ return OK;
+}
+
+status_t OMX::allocate_buffer_with_backup(
+ node_id node, OMX_U32 port_index, const sp<IMemory> &params,
+ buffer_id *buffer) {
+ Mutex::Autolock autoLock(mLock);
+
+ BufferMeta *buffer_meta = new BufferMeta(this, params, true);
+
+ OMX_BUFFERHEADERTYPE *header;
+
+ NodeMeta *node_meta = static_cast<NodeMeta *>(node);
+ OMX_ERRORTYPE err =
+ OMX_AllocateBuffer(
+ node_meta->handle(), &header, port_index, buffer_meta,
+ params->size());
+
+ if (err != OMX_ErrorNone) {
+ delete buffer_meta;
+ buffer_meta = NULL;
+
+ *buffer = 0;
+ return UNKNOWN_ERROR;
+ }
+
+ *buffer = header;
+
+ return OK;
+}
+
+status_t OMX::free_buffer(node_id node, OMX_U32 port_index, buffer_id buffer) {
+ OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer;
+ BufferMeta *buffer_meta = static_cast<BufferMeta *>(header->pAppPrivate);
+
+ NodeMeta *node_meta = static_cast<NodeMeta *>(node);
+ OMX_ERRORTYPE err =
+ OMX_FreeBuffer(node_meta->handle(), port_index, header);
+
+ delete buffer_meta;
+ buffer_meta = NULL;
+
+ return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
+}
+
+OMX_ERRORTYPE OMX::OnEvent(
+ NodeMeta *meta,
+ OMX_IN OMX_EVENTTYPE eEvent,
+ OMX_IN OMX_U32 nData1,
+ OMX_IN OMX_U32 nData2,
+ OMX_IN OMX_PTR pEventData) {
+ LOGV("OnEvent(%d, %ld, %ld)", eEvent, nData1, nData2);
+
+ omx_message msg;
+ msg.type = omx_message::EVENT;
+ msg.u.event_data.node = meta;
+ msg.u.event_data.event = eEvent;
+ msg.u.event_data.data1 = nData1;
+ msg.u.event_data.data2 = nData2;
+
+#if !IOMX_USES_SOCKETS
+ sp<IOMXObserver> observer = meta->observer();
+ if (observer.get() != NULL) {
+ observer->on_message(msg);
+ }
+#else
+ assert(mSock >= 0);
+
+ ssize_t n = send(mSock, &msg, sizeof(msg), 0);
+ assert(n > 0 && static_cast<size_t>(n) == sizeof(msg));
+#endif
+
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE OMX::OnEmptyBufferDone(
+ NodeMeta *meta, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) {
+ LOGV("OnEmptyBufferDone buffer=%p", pBuffer);
+
+ omx_message msg;
+ msg.type = omx_message::EMPTY_BUFFER_DONE;
+ msg.u.buffer_data.node = meta;
+ msg.u.buffer_data.buffer = pBuffer;
+
+#if !IOMX_USES_SOCKETS
+ sp<IOMXObserver> observer = meta->observer();
+ if (observer.get() != NULL) {
+ observer->on_message(msg);
+ }
+#else
+ assert(mSock >= 0);
+ ssize_t n = send(mSock, &msg, sizeof(msg), 0);
+ assert(n > 0 && static_cast<size_t>(n) == sizeof(msg));
+#endif
+
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE OMX::OnFillBufferDone(
+ NodeMeta *meta, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) {
+ LOGV("OnFillBufferDone buffer=%p", pBuffer);
+ BufferMeta *buffer_meta = static_cast<BufferMeta *>(pBuffer->pAppPrivate);
+ buffer_meta->CopyFromOMX(pBuffer);
+
+ omx_message msg;
+ msg.type = omx_message::FILL_BUFFER_DONE;
+ msg.u.extended_buffer_data.node = meta;
+ msg.u.extended_buffer_data.buffer = pBuffer;
+ msg.u.extended_buffer_data.range_offset = pBuffer->nOffset;
+ msg.u.extended_buffer_data.range_length = pBuffer->nFilledLen;
+ msg.u.extended_buffer_data.flags = pBuffer->nFlags;
+ msg.u.extended_buffer_data.timestamp = pBuffer->nTimeStamp;
+ msg.u.extended_buffer_data.platform_private = pBuffer->pPlatformPrivate;
+
+#if !IOMX_USES_SOCKETS
+ sp<IOMXObserver> observer = meta->observer();
+ if (observer.get() != NULL) {
+ observer->on_message(msg);
+ }
+#else
+ assert(mSock >= 0);
+
+ ssize_t n = send(mSock, &msg, sizeof(msg), 0);
+ assert(n > 0 && static_cast<size_t>(n) == sizeof(msg));
+#endif
+
+ return OMX_ErrorNone;
+}
+
+#if !IOMX_USES_SOCKETS
+status_t OMX::observe_node(
+ node_id node, const sp<IOMXObserver> &observer) {
+ NodeMeta *node_meta = static_cast<NodeMeta *>(node);
+
+ node_meta->setObserver(observer);
+
+ return OK;
+}
+
+void OMX::fill_buffer(node_id node, buffer_id buffer) {
+ OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer;
+ header->nFilledLen = 0;
+ header->nOffset = 0;
+ header->nFlags = 0;
+
+ NodeMeta *node_meta = static_cast<NodeMeta *>(node);
+
+ OMX_ERRORTYPE err =
+ OMX_FillThisBuffer(node_meta->handle(), header);
+ assert(err == OMX_ErrorNone);
+}
+
+void OMX::empty_buffer(
+ node_id node,
+ buffer_id buffer,
+ OMX_U32 range_offset, OMX_U32 range_length,
+ OMX_U32 flags, OMX_TICKS timestamp) {
+ OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer;
+ header->nFilledLen = range_length;
+ header->nOffset = range_offset;
+ header->nFlags = flags;
+ header->nTimeStamp = timestamp;
+
+ BufferMeta *buffer_meta =
+ static_cast<BufferMeta *>(header->pAppPrivate);
+ buffer_meta->CopyToOMX(header);
+
+ NodeMeta *node_meta = static_cast<NodeMeta *>(node);
+
+ OMX_ERRORTYPE err =
+ OMX_EmptyThisBuffer(node_meta->handle(), header);
+ assert(err == OMX_ErrorNone);
+}
+#endif
+
+} // namespace android
+
diff --git a/media/libstagefright/omx/OMX.h b/media/libstagefright/omx/OMX.h
new file mode 100644
index 000000000000..ed4e5dd3238c
--- /dev/null
+++ b/media/libstagefright/omx/OMX.h
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_OMX_H_
+#define ANDROID_OMX_H_
+
+#include <pthread.h>
+
+#include <media/IOMX.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class NodeMeta;
+
+class OMX : public BnOMX {
+public:
+ OMX();
+ virtual ~OMX();
+
+#if IOMX_USES_SOCKETS
+ virtual status_t connect(int *sd);
+#endif
+
+ virtual status_t list_nodes(List<String8> *list);
+
+ virtual status_t allocate_node(const char *name, node_id *node);
+ virtual status_t free_node(node_id node);
+
+ virtual status_t send_command(
+ node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param);
+
+ virtual status_t get_parameter(
+ node_id node, OMX_INDEXTYPE index,
+ void *params, size_t size);
+
+ virtual status_t set_parameter(
+ node_id node, OMX_INDEXTYPE index,
+ const void *params, size_t size);
+
+ virtual status_t use_buffer(
+ node_id node, OMX_U32 port_index, const sp<IMemory> &params,
+ buffer_id *buffer);
+
+ virtual status_t allocate_buffer(
+ node_id node, OMX_U32 port_index, size_t size,
+ buffer_id *buffer);
+
+ virtual status_t allocate_buffer_with_backup(
+ node_id node, OMX_U32 port_index, const sp<IMemory> &params,
+ buffer_id *buffer);
+
+ virtual status_t free_buffer(
+ node_id node, OMX_U32 port_index, buffer_id buffer);
+
+#if !IOMX_USES_SOCKETS
+ virtual status_t observe_node(
+ node_id node, const sp<IOMXObserver> &observer);
+
+ virtual void fill_buffer(node_id node, buffer_id buffer);
+
+ virtual void empty_buffer(
+ node_id node,
+ buffer_id buffer,
+ OMX_U32 range_offset, OMX_U32 range_length,
+ OMX_U32 flags, OMX_TICKS timestamp);
+#endif
+
+private:
+ static OMX_CALLBACKTYPE kCallbacks;
+
+#if IOMX_USES_SOCKETS
+ int mSock;
+ pthread_t mThread;
+
+ static void *ThreadWrapper(void *me);
+ void threadEntry();
+#endif
+
+ Mutex mLock;
+
+ static OMX_ERRORTYPE OnEvent(
+ OMX_IN OMX_HANDLETYPE hComponent,
+ OMX_IN OMX_PTR pAppData,
+ OMX_IN OMX_EVENTTYPE eEvent,
+ OMX_IN OMX_U32 nData1,
+ OMX_IN OMX_U32 nData2,
+ OMX_IN OMX_PTR pEventData);
+
+ static OMX_ERRORTYPE OnEmptyBufferDone(
+ OMX_IN OMX_HANDLETYPE hComponent,
+ OMX_IN OMX_PTR pAppData,
+ OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);
+
+ static OMX_ERRORTYPE OnFillBufferDone(
+ OMX_IN OMX_HANDLETYPE hComponent,
+ OMX_IN OMX_PTR pAppData,
+ OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);
+
+ OMX_ERRORTYPE OnEvent(
+ NodeMeta *meta,
+ OMX_IN OMX_EVENTTYPE eEvent,
+ OMX_IN OMX_U32 nData1,
+ OMX_IN OMX_U32 nData2,
+ OMX_IN OMX_PTR pEventData);
+
+ OMX_ERRORTYPE OnEmptyBufferDone(
+ NodeMeta *meta, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer);
+
+ OMX_ERRORTYPE OnFillBufferDone(
+ NodeMeta *meta, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer);
+
+ OMX(const OMX &);
+ OMX &operator=(const OMX &);
+};
+
+} // namespace android
+
+#endif // ANDROID_OMX_H_
diff --git a/media/libstagefright/string.cpp b/media/libstagefright/string.cpp
new file mode 100644
index 000000000000..5b1678403f8e
--- /dev/null
+++ b/media/libstagefright/string.cpp
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+#include <media/stagefright/string.h>
+
+namespace android {
+
+// static
+string::size_type string::npos = (string::size_type)-1;
+
+string::string() {
+}
+
+string::string(const char *s, size_t length)
+ : mString(s, length) {
+}
+
+string::string(const string &from, size_type start, size_type length)
+ : mString(from.c_str() + start, length) {
+}
+
+string::string(const char *s)
+ : mString(s) {
+}
+
+const char *string::c_str() const {
+ return mString.string();
+}
+
+string::size_type string::size() const {
+ return mString.length();
+}
+
+void string::clear() {
+ mString = String8();
+}
+
+string::size_type string::find(char c) const {
+ char s[2];
+ s[0] = c;
+ s[1] = '\0';
+
+ ssize_t index = mString.find(s);
+
+ return index < 0 ? npos : (size_type)index;
+}
+
+bool string::operator<(const string &other) const {
+ return mString < other.mString;
+}
+
+bool string::operator==(const string &other) const {
+ return mString == other.mString;
+}
+
+string &string::operator+=(char c) {
+ mString.append(&c, 1);
+
+ return *this;
+}
+
+void string::erase(size_t from, size_t length) {
+ String8 s(mString.string(), from);
+ s.append(mString.string() + from + length);
+
+ mString = s;
+}
+
+} // namespace android
+
diff --git a/media/sdutils/sdutil.cpp b/media/sdutils/sdutil.cpp
index 6f0cdfb2c525..fe1187897126 100644
--- a/media/sdutils/sdutil.cpp
+++ b/media/sdutils/sdutil.cpp
@@ -88,7 +88,7 @@ static int mount(const char* path) {
String16 string(path);
gMountService->mountMedia(string);
- for (int i = 0; i < 10; i++) {
+ for (int i = 0; i < 60; i++) {
if (isMounted(path)) {
return 0;
}
@@ -103,7 +103,7 @@ static int unmount(const char* path) {
String16 string(path);
gMountService->unmountMedia(string);
- for (int i = 0; i < 10; i++) {
+ for (int i = 0; i < 20; i++) {
if (!isMounted(path)) {
return 0;
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java
index 6edc2cc0d61b..2a4e9a04af62 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java
@@ -23,6 +23,7 @@ import com.android.mediaframeworktest.functional.MediaMimeTest;
import com.android.mediaframeworktest.functional.MediaPlayerApiTest;
import com.android.mediaframeworktest.functional.MediaRecorderTest;
import com.android.mediaframeworktest.functional.SimTonesTest;
+import com.android.mediaframeworktest.functional.MediaPlayerInvokeTest;
import junit.framework.TestSuite;
@@ -32,7 +33,7 @@ import android.test.InstrumentationTestSuite;
/**
* Instrumentation Test Runner for all MediaPlayer tests.
- *
+ *
* Running all tests:
*
* adb shell am instrument \
@@ -52,6 +53,7 @@ public class MediaFrameworkTestRunner extends InstrumentationTestRunner {
suite.addTestSuite(MediaRecorderTest.class);
suite.addTestSuite(MediaAudioTrackTest.class);
suite.addTestSuite(MediaMimeTest.class);
+ suite.addTestSuite(MediaPlayerInvokeTest.class);
return suite;
}
@@ -60,5 +62,3 @@ public class MediaFrameworkTestRunner extends InstrumentationTestRunner {
return MediaFrameworkTestRunner.class.getClassLoader();
}
}
-
-
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java
index 81d59da5c492..a203adc71c31 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java
@@ -24,16 +24,16 @@ import junit.framework.TestSuite;
/**
* Instrumentation Test Runner for all media framework unit tests.
- *
+ *
* Make sure that MediaFrameworkUnitTestRunner has been added to
* AndroidManifest.xml file, and then "make -j4 mediaframeworktest; adb sync"
* to build and upload mediaframeworktest to the phone or emulator.
- *
+ *
* Example on running all unit tests for a single class:
* adb shell am instrument -e class \
- * com.android.mediaframeworktest.unit.MediaMetadataRetrieverUnitTest \
+ * com.android.mediaframeworktest.unit.MediaMetadataRetrieverUnitTest \
* -w com.android.mediaframeworktest/.MediaFrameworkUnitTestRunner
- *
+ *
* Example on running all unit tests for the media framework:
* adb shell am instrument \
* -w com.android.mediaframeworktest/.MediaFrameworkUnitTestRunner
@@ -54,12 +54,12 @@ public class MediaFrameworkUnitTestRunner extends InstrumentationTestRunner {
public ClassLoader getLoader() {
return MediaFrameworkUnitTestRunner.class.getClassLoader();
}
-
+
// Running all unit tests checking the state machine may be time-consuming.
private void addMediaMetadataRetrieverStateUnitTests(TestSuite suite) {
suite.addTestSuite(MediaMetadataRetrieverTest.class);
}
-
+
// Running all unit tests checking the state machine may be time-consuming.
private void addMediaRecorderStateUnitTests(TestSuite suite) {
suite.addTestSuite(MediaRecorderPrepareStateUnitTest.class);
@@ -87,5 +87,6 @@ public class MediaFrameworkUnitTestRunner extends InstrumentationTestRunner {
suite.addTestSuite(MediaPlayerSetLoopingStateUnitTest.class);
suite.addTestSuite(MediaPlayerSetAudioStreamTypeStateUnitTest.class);
suite.addTestSuite(MediaPlayerSetVolumeStateUnitTest.class);
+ suite.addTestSuite(MediaPlayerMetadataParserTest.class);
}
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerInvokeTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerInvokeTest.java
new file mode 100644
index 000000000000..ab8b31155972
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerInvokeTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+package com.android.mediaframeworktest.functional;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.Suppress;
+
+import android.media.MediaPlayer;
+import android.os.Parcel;
+
+import java.util.Calendar;
+import java.util.Random;
+
+// Tests for the invoke method in the MediaPlayer.
+public class MediaPlayerInvokeTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+ private static final String TAG = "MediaPlayerInvokeTest";
+ private MediaPlayer mPlayer;
+ private Random rnd;
+
+ public MediaPlayerInvokeTest() {
+ super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+ rnd = new Random(Calendar.getInstance().getTimeInMillis());
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mPlayer = new MediaPlayer();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mPlayer.release();
+ super.tearDown();
+ }
+
+ // Generate a random number, sends it to the ping test player.
+ @MediumTest
+ public void testPing() throws Exception {
+ mPlayer.setDataSource("test:invoke_mock_media_player.so?url=ping");
+
+ Parcel request = mPlayer.newRequest();
+ Parcel reply = Parcel.obtain();
+
+ int val = rnd.nextInt();
+ request.writeInt(val);
+ assertEquals(0, mPlayer.invoke(request, reply));
+ assertEquals(val, reply.readInt());
+ }
+}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerMetadataParserTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerMetadataParserTest.java
new file mode 100644
index 000000000000..38f598a6abc9
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerMetadataParserTest.java
@@ -0,0 +1,432 @@
+/*
+ * 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.
+ */
+
+package com.android.mediaframeworktest.unit;
+import android.media.Metadata;
+import android.os.Parcel;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import java.util.Calendar;
+import java.util.Date;
+
+/*
+ * Check the Java layer that parses serialized metadata in Parcel
+ * works as expected.
+ *
+ */
+
+public class MediaPlayerMetadataParserTest extends AndroidTestCase {
+ private static final String TAG = "MediaPlayerMetadataTest";
+ private static final int kMarker = 0x4d455441; // 'M' 'E' 'T' 'A'
+ private static final int kHeaderSize = 8;
+
+ private Metadata mMetadata = null;
+ private Parcel mParcel = null;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mMetadata = new Metadata();
+ mParcel = Parcel.obtain();
+
+ resetParcel();
+ }
+
+ // Check parsing of the parcel fails. Make sure the parser rewind
+ // the parcel properly.
+ private void assertParseFail() throws Exception {
+ mParcel.setDataPosition(0);
+ assertFalse(mMetadata.parse(mParcel));
+ assertEquals(0, mParcel.dataPosition());
+ }
+
+ // Check parsing of the parcel is successful.
+ private void assertParse() throws Exception {
+ mParcel.setDataPosition(0);
+ assertTrue(mMetadata.parse(mParcel));
+ }
+
+ // Write the number of bytes from the start of the parcel to the
+ // current position at the beginning of the parcel (offset 0).
+ private void adjustSize() {
+ adjustSize(0);
+ }
+
+ // Write the number of bytes from the offset to the current
+ // position at position pointed by offset.
+ private void adjustSize(int offset) {
+ final int pos = mParcel.dataPosition();
+
+ mParcel.setDataPosition(offset);
+ mParcel.writeInt(pos - offset);
+ mParcel.setDataPosition(pos);
+ }
+
+ // Rewind the parcel and insert the header.
+ private void resetParcel() {
+ mParcel.setDataPosition(0);
+ // Most tests will use a properly formed parcel with a size
+ // and the meta marker so we add them by default.
+ mParcel.writeInt(-1); // Placeholder for the size
+ mParcel.writeInt(kMarker);
+ }
+
+ // ----------------------------------------------------------------------
+ // START OF THE TESTS
+
+
+ // There should be at least 8 bytes in the parcel, 4 for the size
+ // and 4 for the 'M' 'E' 'T' 'A' marker.
+ @SmallTest
+ public void testMissingSizeAndMarker() throws Exception {
+ for (int i = 0; i < kHeaderSize; ++i) {
+ mParcel.setDataPosition(0);
+ mParcel.setDataSize(i);
+
+ assertEquals(i, mParcel.dataAvail());
+ assertParseFail();
+ }
+ }
+
+ // There should be at least 'size' bytes in the parcel.
+ @SmallTest
+ public void testMissingData() throws Exception {
+ final int size = 20;
+
+ mParcel.writeInt(size);
+ mParcel.setDataSize(size - 1);
+ assertParseFail();
+ }
+
+ // Empty parcel is fine
+ @SmallTest
+ public void testEmptyIsOk() throws Exception {
+ adjustSize();
+ assertParse();
+ }
+
+ // ----------------------------------------------------------------------
+ // RECORDS
+ // ----------------------------------------------------------------------
+
+ // A record header should be at least 12 bytes long
+ @SmallTest
+ public void testRecordMissingId() throws Exception {
+ mParcel.writeInt(13); // record length
+ // misses metadata id and metadata type.
+ adjustSize();
+ assertParseFail();
+ }
+
+ @SmallTest
+ public void testRecordMissingType() throws Exception {
+ mParcel.writeInt(13); // record length lies
+ mParcel.writeInt(Metadata.TITLE);
+ // misses metadata type
+ adjustSize();
+ assertParseFail();
+ }
+
+ @SmallTest
+ public void testRecordWithZeroPayload() throws Exception {
+ mParcel.writeInt(0);
+ adjustSize();
+ assertParseFail();
+ }
+
+ // A record cannot be empty.
+ @SmallTest
+ public void testRecordMissingPayload() throws Exception {
+ mParcel.writeInt(12);
+ mParcel.writeInt(Metadata.TITLE);
+ mParcel.writeInt(Metadata.STRING_VAL);
+ // misses payload
+ adjustSize();
+ assertParseFail();
+ }
+
+ // Check records can be found.
+ @SmallTest
+ public void testRecordsFound() throws Exception {
+ writeStringRecord(Metadata.TITLE, "a title");
+ writeStringRecord(Metadata.GENRE, "comedy");
+ writeStringRecord(Metadata.firstCustomId(), "custom");
+ adjustSize();
+ assertParse();
+ assertTrue(mMetadata.has(Metadata.TITLE));
+ assertTrue(mMetadata.has(Metadata.GENRE));
+ assertTrue(mMetadata.has(Metadata.firstCustomId()));
+ assertFalse(mMetadata.has(Metadata.DRM_CRIPPLED));
+ assertEquals(3, mMetadata.keySet().size());
+ }
+
+ // Detects bad metadata type
+ @SmallTest
+ public void testBadMetadataType() throws Exception {
+ final int start = mParcel.dataPosition();
+ mParcel.writeInt(-1); // Placeholder for the length
+ mParcel.writeInt(Metadata.TITLE);
+ mParcel.writeInt(0); // Invalid type.
+ mParcel.writeString("dummy");
+ adjustSize(start);
+
+ adjustSize();
+ assertParseFail();
+ }
+
+ // Check a Metadata instance can be reused, i.e the parse method
+ // wipes out the existing states/keys.
+ @SmallTest
+ public void testParseClearState() throws Exception {
+ writeStringRecord(Metadata.TITLE, "a title");
+ writeStringRecord(Metadata.GENRE, "comedy");
+ writeStringRecord(Metadata.firstCustomId(), "custom");
+ adjustSize();
+ assertParse();
+
+ resetParcel();
+ writeStringRecord(Metadata.MIME_TYPE, "audio/mpg");
+ adjustSize();
+ assertParse();
+
+ // Only the mime type metadata should be present.
+ assertEquals(1, mMetadata.keySet().size());
+ assertTrue(mMetadata.has(Metadata.MIME_TYPE));
+
+ assertFalse(mMetadata.has(Metadata.TITLE));
+ assertFalse(mMetadata.has(Metadata.GENRE));
+ assertFalse(mMetadata.has(Metadata.firstCustomId()));
+ }
+
+ // ----------------------------------------------------------------------
+ // GETTERS
+ // ----------------------------------------------------------------------
+
+ // getString
+ @SmallTest
+ public void testGetString() throws Exception {
+ writeStringRecord(Metadata.TITLE, "a title");
+ writeStringRecord(Metadata.GENRE, "comedy");
+ adjustSize();
+ assertParse();
+
+ assertEquals("a title", mMetadata.getString(Metadata.TITLE));
+ assertEquals("comedy", mMetadata.getString(Metadata.GENRE));
+ }
+
+ // get an empty string.
+ @SmallTest
+ public void testGetEmptyString() throws Exception {
+ writeStringRecord(Metadata.TITLE, "");
+ adjustSize();
+ assertParse();
+
+ assertEquals("", mMetadata.getString(Metadata.TITLE));
+ }
+
+ // get a string when a NULL value was in the parcel
+ @SmallTest
+ public void testGetNullString() throws Exception {
+ writeStringRecord(Metadata.TITLE, null);
+ adjustSize();
+ assertParse();
+
+ assertEquals(null, mMetadata.getString(Metadata.TITLE));
+ }
+
+ // get a string when an integer is actually present
+ @SmallTest
+ public void testWrongType() throws Exception {
+ writeIntRecord(Metadata.DURATION, 5);
+ adjustSize();
+ assertParse();
+
+ try {
+ mMetadata.getString(Metadata.DURATION);
+ } catch (IllegalStateException ise) {
+ return;
+ }
+ fail("Exception was not thrown");
+ }
+
+ // getInt
+ @SmallTest
+ public void testGetInt() throws Exception {
+ writeIntRecord(Metadata.CD_TRACK_NUM, 1);
+ adjustSize();
+ assertParse();
+
+ assertEquals(1, mMetadata.getInt(Metadata.CD_TRACK_NUM));
+ }
+
+ // getBoolean
+ @SmallTest
+ public void testGetBoolean() throws Exception {
+ writeBooleanRecord(Metadata.DRM_CRIPPLED, true);
+ adjustSize();
+ assertParse();
+
+ assertEquals(true, mMetadata.getBoolean(Metadata.DRM_CRIPPLED));
+ }
+
+ // getLong
+ @SmallTest
+ public void testGetLong() throws Exception {
+ writeLongRecord(Metadata.DURATION, 1L);
+ adjustSize();
+ assertParse();
+
+ assertEquals(1L, mMetadata.getLong(Metadata.DURATION));
+ }
+
+ // getDouble
+ @SmallTest
+ public void testGetDouble() throws Exception {
+ writeDoubleRecord(Metadata.VIDEO_FRAME_RATE, 29.97);
+ adjustSize();
+ assertParse();
+
+ assertEquals(29.97, mMetadata.getDouble(Metadata.VIDEO_FRAME_RATE));
+ }
+
+ // getByteArray
+ @SmallTest
+ public void testGetByteArray() throws Exception {
+ byte data[] = new byte[]{1,2,3,4,5};
+
+ writeByteArrayRecord(Metadata.ALBUM_ART, data);
+ adjustSize();
+ assertParse();
+
+ byte res[] = mMetadata.getByteArray(Metadata.ALBUM_ART);
+ for (int i = 0; i < data.length; ++i) {
+ assertEquals(data[i], res[i]);
+ }
+ }
+
+ // getDate
+ @SmallTest
+ public void testGetDate() throws Exception {
+ writeDateRecord(Metadata.DATE, 0, "PST");
+ adjustSize();
+ assertParse();
+
+ assertEquals(new Date(0), mMetadata.getDate(Metadata.DATE));
+ }
+
+ // getTimedText
+ @SmallTest
+ public void testGetTimedText() throws Exception {
+ Date now = Calendar.getInstance().getTime();
+ writeTimedTextRecord(Metadata.CAPTION, now.getTime(),
+ 10, "Some caption");
+ adjustSize();
+ assertParse();
+
+ Metadata.TimedText caption = mMetadata.getTimedText(Metadata.CAPTION);
+ assertEquals("" + now + "-" + 10 + ":Some caption", caption.toString());
+ }
+
+ // ----------------------------------------------------------------------
+ // HELPERS TO APPEND RECORDS
+ // ----------------------------------------------------------------------
+
+ // Insert a string record at the current position.
+ private void writeStringRecord(int metadataId, String val) {
+ final int start = mParcel.dataPosition();
+ mParcel.writeInt(-1); // Placeholder for the length
+ mParcel.writeInt(metadataId);
+ mParcel.writeInt(Metadata.STRING_VAL);
+ mParcel.writeString(val);
+ adjustSize(start);
+ }
+
+ // Insert an int record at the current position.
+ private void writeIntRecord(int metadataId, int val) {
+ final int start = mParcel.dataPosition();
+ mParcel.writeInt(-1); // Placeholder for the length
+ mParcel.writeInt(metadataId);
+ mParcel.writeInt(Metadata.INTEGER_VAL);
+ mParcel.writeInt(val);
+ adjustSize(start);
+ }
+
+ // Insert a boolean record at the current position.
+ private void writeBooleanRecord(int metadataId, boolean val) {
+ final int start = mParcel.dataPosition();
+ mParcel.writeInt(-1); // Placeholder for the length
+ mParcel.writeInt(metadataId);
+ mParcel.writeInt(Metadata.BOOLEAN_VAL);
+ mParcel.writeInt(val ? 1 : 0);
+ adjustSize(start);
+ }
+
+ // Insert a Long record at the current position.
+ private void writeLongRecord(int metadataId, long val) {
+ final int start = mParcel.dataPosition();
+ mParcel.writeInt(-1); // Placeholder for the length
+ mParcel.writeInt(metadataId);
+ mParcel.writeInt(Metadata.LONG_VAL);
+ mParcel.writeLong(val);
+ adjustSize(start);
+ }
+
+ // Insert a Double record at the current position.
+ private void writeDoubleRecord(int metadataId, double val) {
+ final int start = mParcel.dataPosition();
+ mParcel.writeInt(-1); // Placeholder for the length
+ mParcel.writeInt(metadataId);
+ mParcel.writeInt(Metadata.DOUBLE_VAL);
+ mParcel.writeDouble(val);
+ adjustSize(start);
+ }
+
+ // Insert a ByteArray record at the current position.
+ private void writeByteArrayRecord(int metadataId, byte[] val) {
+ final int start = mParcel.dataPosition();
+ mParcel.writeInt(-1); // Placeholder for the length
+ mParcel.writeInt(metadataId);
+ mParcel.writeInt(Metadata.BYTE_ARRAY_VAL);
+ mParcel.writeByteArray(val);
+ adjustSize(start);
+ }
+
+ // Insert a Date record at the current position.
+ private void writeDateRecord(int metadataId, long time, String tz) {
+ final int start = mParcel.dataPosition();
+ mParcel.writeInt(-1); // Placeholder for the length
+ mParcel.writeInt(metadataId);
+ mParcel.writeInt(Metadata.DATE_VAL);
+ mParcel.writeLong(time);
+ mParcel.writeString(tz);
+ adjustSize(start);
+ }
+
+ // Insert a TimedText record at the current position.
+ private void writeTimedTextRecord(int metadataId, long begin,
+ int duration, String text) {
+ final int start = mParcel.dataPosition();
+ mParcel.writeInt(-1); // Placeholder for the length
+ mParcel.writeInt(metadataId);
+ mParcel.writeInt(Metadata.TIMED_TEXT_VAL);
+ mParcel.writeLong(begin);
+ mParcel.writeInt(duration);
+ mParcel.writeString(text);
+ adjustSize(start);
+ }
+}
diff --git a/media/tests/players/Android.mk b/media/tests/players/Android.mk
new file mode 100644
index 000000000000..eb50a514cba9
--- /dev/null
+++ b/media/tests/players/Android.mk
@@ -0,0 +1,29 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= invoke_mock_media_player.cpp
+
+LOCAL_SHARED_LIBRARIES:= \
+ libbinder \
+ libutils
+
+LOCAL_MODULE:= invoke_mock_media_player
+LOCAL_MODULE_TAGS := test eng
+LOCAL_PRELINK_MODULE:= false
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/tests/players/README b/media/tests/players/README
new file mode 100644
index 000000000000..edf9bd634286
--- /dev/null
+++ b/media/tests/players/README
@@ -0,0 +1,8 @@
+Native test players for system tests.
+
+For functional/system/performance tests, a native test player can be used.
+This directory contains the sources of such players.
+The class TestPlayerStub uses the dynamic loader to load any of them.
+
+
+
diff --git a/media/tests/players/invoke_mock_media_player.cpp b/media/tests/players/invoke_mock_media_player.cpp
new file mode 100644
index 000000000000..8d575a35b29c
--- /dev/null
+++ b/media/tests/players/invoke_mock_media_player.cpp
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "TestPlayerStub"
+#include "utils/Log.h"
+
+#include <string.h>
+
+#include <binder/Parcel.h>
+#include <media/MediaPlayerInterface.h>
+#include <utils/Errors.h>
+
+using android::INVALID_OPERATION;
+using android::ISurface;
+using android::MediaPlayerBase;
+using android::MetadataType;
+using android::OK;
+using android::Parcel;
+using android::SortedVector;
+using android::TEST_PLAYER;
+using android::UNKNOWN_ERROR;
+using android::player_type;
+using android::sp;
+using android::status_t;
+
+// This file contains a test player that is loaded via the
+// TestPlayerStub class. The player contains various implementation
+// of the invoke method that java tests can use.
+
+namespace {
+const char *kPing = "ping";
+
+class Player: public MediaPlayerBase
+{
+ public:
+ enum TestType {TEST_UNKNOWN, PING};
+ Player() {}
+ virtual ~Player() {}
+
+ virtual status_t initCheck() {return OK;}
+ virtual bool hardwareOutput() {return true;}
+
+ virtual status_t setDataSource(const char *url) {
+ LOGV("setDataSource %s", url);
+ mTest = TEST_UNKNOWN;
+ if (strncmp(url, kPing, strlen(kPing)) == 0) {
+ mTest = PING;
+ }
+ return OK;
+ }
+
+ virtual status_t setDataSource(int fd, int64_t offset, int64_t length) {return OK;}
+ virtual status_t setVideoSurface(const sp<ISurface>& surface) {return OK;}
+ virtual status_t prepare() {return OK;}
+ virtual status_t prepareAsync() {return OK;}
+ virtual status_t start() {return OK;}
+ virtual status_t stop() {return OK;}
+ virtual status_t pause() {return OK;}
+ virtual bool isPlaying() {return true;}
+ virtual status_t seekTo(int msec) {return OK;}
+ virtual status_t getCurrentPosition(int *msec) {return OK;}
+ virtual status_t getDuration(int *msec) {return OK;}
+ virtual status_t reset() {return OK;}
+ virtual status_t setLooping(int loop) {return OK;}
+ virtual player_type playerType() {return TEST_PLAYER;}
+ virtual status_t invoke(const Parcel& request, Parcel *reply);
+ virtual status_t getMetadata(const SortedVector<MetadataType>& ids,
+ Parcel *records) {return INVALID_OPERATION;}
+
+ private:
+ // Take a request, copy it to the reply.
+ void ping(const Parcel& request, Parcel *reply);
+
+ status_t mStatus;
+ TestType mTest;
+};
+
+status_t Player::invoke(const Parcel& request, Parcel *reply)
+{
+ switch (mTest) {
+ case PING:
+ ping(request, reply);
+ break;
+ default: mStatus = UNKNOWN_ERROR;
+ }
+ return mStatus;
+}
+
+void Player::ping(const Parcel& request, Parcel *reply)
+{
+ const size_t len = request.dataAvail();
+
+ reply->setData(static_cast<const uint8_t*>(request.readInplace(len)), len);
+ mStatus = OK;
+}
+
+}
+
+extern "C" android::MediaPlayerBase* newPlayer()
+{
+ LOGD("New invoke test player");
+ return new Player();
+}
+
+extern "C" android::status_t deletePlayer(android::MediaPlayerBase *player)
+{
+ LOGD("Delete invoke test player");
+ delete player;
+ return OK;
+}
diff --git a/obex/javax/obex/Authenticator.java b/obex/javax/obex/Authenticator.java
index 7246e9166b8f..ec226fb7ada3 100644
--- a/obex/javax/obex/Authenticator.java
+++ b/obex/javax/obex/Authenticator.java
@@ -34,53 +34,50 @@ package javax.obex;
/**
* This interface provides a way to respond to authentication challenge and
- * authentication response headers. When a client or server receives an
+ * authentication response headers. When a client or server receives an
* authentication challenge or authentication response header, the
* <code>onAuthenticationChallenge()</code> or
- * <code>onAuthenticationResponse()</code> will be called, respectively, by
- * the implementation.
+ * <code>onAuthenticationResponse()</code> will be called, respectively, by the
+ * implementation.
* <P>
* For more information on how the authentication procedure works in OBEX,
- * please review the IrOBEX specification at
- * <A HREF="http://www.irda.org">http://www.irda.org</A>.
+ * please review the IrOBEX specification at <A
+ * HREF="http://www.irda.org">http://www.irda.org</A>.
* <P>
* <STRONG>Authentication Challenges</STRONG>
* <P>
* When a client or server receives an authentication challenge header, the
- * <code>onAuthenticationChallenge()</code> method will be invoked by the
- * OBEX API implementation. The application will then return the user name
- * (if needed) and password via a <code>PasswordAuthentication</code> object.
- * The password in this object is not sent in the authentication response.
- * Instead, the 16-byte challenge received in the authentication challenge is
- * combined with the password returned from the
- * <code>onAuthenticationChallenge()</code> method and passed through the MD5
- * hash algorithm. The resulting value is sent in the authentication response
- * along with the user name if it was provided.
+ * <code>onAuthenticationChallenge()</code> method will be invoked by the OBEX
+ * API implementation. The application will then return the user name (if
+ * needed) and password via a <code>PasswordAuthentication</code> object. The
+ * password in this object is not sent in the authentication response. Instead,
+ * the 16-byte challenge received in the authentication challenge is combined
+ * with the password returned from the <code>onAuthenticationChallenge()</code>
+ * method and passed through the MD5 hash algorithm. The resulting value is sent
+ * in the authentication response along with the user name if it was provided.
* <P>
* <STRONG>Authentication Responses</STRONG>
* <P>
* When a client or server receives an authentication response header, the
* <code>onAuthenticationResponse()</code> method is invoked by the API
* implementation with the user name received in the authentication response
- * header. (The user name will be <code>null</code> if no user name was
- * provided in the authentication response header.) The application must
- * determine the correct password. This value should be returned from the
- * <code>onAuthenticationResponse()</code> method. If the authentication
- * request should fail without the implementation checking the password,
- * <code>null</code> should
- * be returned by the application. (This is needed for reasons like not
- * recognizing the user name, etc.) If the returned value is not
- * <code>null</code>, the OBEX API implementation will combine the password
+ * header. (The user name will be <code>null</code> if no user name was provided
+ * in the authentication response header.) The application must determine the
+ * correct password. This value should be returned from the
+ * <code>onAuthenticationResponse()</code> method. If the authentication request
+ * should fail without the implementation checking the password,
+ * <code>null</code> should be returned by the application. (This is needed for
+ * reasons like not recognizing the user name, etc.) If the returned value is
+ * not <code>null</code>, the OBEX API implementation will combine the password
* returned from the <code>onAuthenticationResponse()</code> method and
* challenge sent via the authentication challenge, apply the MD5 hash
* algorithm, and compare the result to the response hash received in the
- * authentication response header. If the values are not equal, an
- * <code>IOException</code> will be thrown if the client requested authentication.
- * If the server requested authentication, the
+ * authentication response header. If the values are not equal, an
+ * <code>IOException</code> will be thrown if the client requested
+ * authentication. If the server requested authentication, the
* <code>onAuthenticationFailure()</code> method will be called on the
- * <code>ServerRequestHandler</code> that failed authentication. The
- * connection is <B>not</B> closed if authentication failed.
- *
+ * <code>ServerRequestHandler</code> that failed authentication. The connection
+ * is <B>not</B> closed if authentication failed.
* @hide
*/
public interface Authenticator {
@@ -90,35 +87,29 @@ public interface Authenticator {
* header. It should respond to the challenge with a
* <code>PasswordAuthentication</code> that contains the correct user name
* and password for the challenge.
- *
- * @param description the description of which user name and password
- * should be used; if no description is provided in the authentication
- * challenge or the description is encoded in an encoding scheme that is
- * not supported, an empty string will be provided
- *
+ * @param description the description of which user name and password should
+ * be used; if no description is provided in the authentication
+ * challenge or the description is encoded in an encoding scheme that
+ * is not supported, an empty string will be provided
* @param isUserIdRequired <code>true</code> if the user ID is required;
- * <code>false</code> if the user ID is not required
- *
- * @param isFullAccess <code>true</code> if full access to the server
- * will be granted; <code>false</code> if read only access will be
- * granted
- *
- * @return a <code>PasswordAuthentication</code> object containing the
- * user name and password used for authentication
+ * <code>false</code> if the user ID is not required
+ * @param isFullAccess <code>true</code> if full access to the server will
+ * be granted; <code>false</code> if read only access will be granted
+ * @return a <code>PasswordAuthentication</code> object containing the user
+ * name and password used for authentication
*/
PasswordAuthentication onAuthenticationChallenge(String description, boolean isUserIdRequired,
boolean isFullAccess);
/**
* Called when a client or server receives an authentication response
- * header. This method will provide the user name and expect the correct
+ * header. This method will provide the user name and expect the correct
* password to be returned.
- *
- * @param userName the user name provided in the authentication response;
- * may be <code>null</code>
- *
+ * @param userName the user name provided in the authentication response; may
+ * be <code>null</code>
* @return the correct password for the user name provided; if
- * <code>null</code> is returned then the authentication request failed
+ * <code>null</code> is returned then the authentication request
+ * failed
*/
byte[] onAuthenticationResponse(byte[] userName);
}
diff --git a/obex/javax/obex/BaseStream.java b/obex/javax/obex/BaseStream.java
index c32717fad272..022ad4fb4a34 100644
--- a/obex/javax/obex/BaseStream.java
+++ b/obex/javax/obex/BaseStream.java
@@ -37,46 +37,39 @@ import java.io.IOException;
/**
* This interface defines the methods needed by a parent that uses the
* PrivateInputStream and PrivateOutputStream objects defined in this package.
- *
* @hide
*/
public interface BaseStream {
/**
* Verifies that this object is still open.
- *
* @throws IOException if the object is closed
*/
void ensureOpen() throws IOException;
/**
- * Verifies that additional information may be sent. In other words, the
+ * Verifies that additional information may be sent. In other words, the
* operation is not done.
- *
* @throws IOException if the operation is completed
*/
void ensureNotDone() throws IOException;
/**
* Continues the operation since there is no data to read.
- *
- * @param sendEmpty <code>true</code> if the operation should send an
- * empty packet or not send anything if there is no data to send
- * @param inStream <code>true</code> if the stream is input stream or
- * is output stream
+ * @param sendEmpty <code>true</code> if the operation should send an empty
+ * packet or not send anything if there is no data to send
+ * @param inStream <code>true</code> if the stream is input stream or is
+ * output stream
* @return <code>true</code> if the operation was completed;
- * <code>false</code> if no operation took place
- *
+ * <code>false</code> if no operation took place
* @throws IOException if an IO error occurs
*/
boolean continueOperation(boolean sendEmpty, boolean inStream) throws IOException;
/**
* Called when the output or input stream is closed.
- *
* @param inStream <code>true</code> if the input stream is closed;
- * <code>false</code> if the output stream is closed
- *
+ * <code>false</code> if the output stream is closed
* @throws IOException if an IO error occurs
*/
void streamClosed(boolean inStream) throws IOException;
diff --git a/obex/javax/obex/ClientOperation.java b/obex/javax/obex/ClientOperation.java
index b3807afe0705..65663b1a5bb9 100644
--- a/obex/javax/obex/ClientOperation.java
+++ b/obex/javax/obex/ClientOperation.java
@@ -40,9 +40,8 @@ import java.io.DataOutputStream;
import java.io.ByteArrayOutputStream;
/**
- * This class implements the <code>Operation</code> interface. It will read
- * and write data via puts and gets.
- *
+ * This class implements the <code>Operation</code> interface. It will read and
+ * write data via puts and gets.
* @hide
*/
public final class ClientOperation implements Operation, BaseStream {
@@ -73,15 +72,14 @@ public final class ClientOperation implements Operation, BaseStream {
private boolean mEndOfBodySent;
- /**
+ /**
* Creates new OperationImpl to read and write data to a server
* @param maxSize the maximum packet size
* @param p the parent to this object
* @param type <code>true</code> if this is a get request;
- * <code>false</code. if this is a put request
- * @param headers the headers to set in the initial request
- *
- * @throws IOExcpetion if the an IO error occured
+ * <code>false</code. if this is a put request
+ * @param header the header to set in the initial request
+ * @throws IOException if the an IO error occurred
*/
public ClientOperation(int maxSize, ClientSession p, HeaderSet header, boolean type)
throws IOException {
@@ -126,20 +124,14 @@ public final class ClientOperation implements Operation, BaseStream {
}
/**
- * Sends an ABORT message to the server. By calling this method, the
+ * Sends an ABORT message to the server. By calling this method, the
* corresponding input and output streams will be closed along with this
* object.
- *
- * @throws IOException if the transaction has already ended or if an
- * OBEX server called this method
+ * @throws IOException if the transaction has already ended or if an OBEX
+ * server called this method
*/
public synchronized void abort() throws IOException {
ensureOpen();
- // need check again .
- // if(isDone) {
- // throw new IOException("Operation has already ended");
- // }
-
//no compatible with sun-ri
if ((mOperationDone) && (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE)) {
throw new IOException("Operation has already ended");
@@ -165,15 +157,14 @@ public final class ClientOperation implements Operation, BaseStream {
}
/**
- * Retrieves the response code retrieved from the server. Response codes
- * are defined in the <code>ResponseCodes</code> interface.
- *
+ * Retrieves the response code retrieved from the server. Response codes are
+ * defined in the <code>ResponseCodes</code> interface.
* @return the response code retrieved from the server
- *
* @throws IOException if an error occurred in the transport layer during
- * the transaction; if this method is called on a <code>HeaderSet</code>
- * object created by calling <code>createHeaderSet</code> in a
- * <code>ClientSession</code> object
+ * the transaction; if this method is called on a
+ * <code>HeaderSet</code> object created by calling
+ * <code>createHeaderSet</code> in a <code>ClientSession</code>
+ * object
*/
public synchronized int getResponseCode() throws IOException {
//avoid dup validateConnection
@@ -187,7 +178,6 @@ public final class ClientOperation implements Operation, BaseStream {
/**
* This method will always return <code>null</code>
- *
* @return <code>null</code>
*/
public String getEncoding() {
@@ -198,9 +188,8 @@ public final class ClientOperation implements Operation, BaseStream {
* Returns the type of content that the resource connected to is providing.
* E.g. if the connection is via HTTP, then the value of the content-type
* header field is returned.
- *
* @return the content type of the resource that the URL references, or
- * <code>null</code> if not known
+ * <code>null</code> if not known
*/
public String getType() {
try {
@@ -212,11 +201,10 @@ public final class ClientOperation implements Operation, BaseStream {
/**
* Returns the length of the content which is being provided. E.g. if the
- * connection is via HTTP, then the value of the content-length header
- * field is returned.
- *
+ * connection is via HTTP, then the value of the content-length header field
+ * is returned.
* @return the content length of the resource that this connection's URL
- * references, or -1 if the content length is not known
+ * references, or -1 if the content length is not known
*/
public long getLength() {
try {
@@ -234,9 +222,7 @@ public final class ClientOperation implements Operation, BaseStream {
/**
* Open and return an input stream for a connection.
- *
* @return an input stream
- *
* @throws IOException if an I/O error occurs
*/
public InputStream openInputStream() throws IOException {
@@ -259,11 +245,9 @@ public final class ClientOperation implements Operation, BaseStream {
return mPrivateInput;
}
- /**8
+ /**
* Open and return a data input stream for a connection.
- *
* @return an input stream
- *
* @throws IOException if an I/O error occurs
*/
public DataInputStream openDataInputStream() throws IOException {
@@ -272,9 +256,7 @@ public final class ClientOperation implements Operation, BaseStream {
/**
* Open and return an output stream for a connection.
- *
* @return an output stream
- *
* @throws IOException if an I/O error occurs
*/
public OutputStream openOutputStream() throws IOException {
@@ -301,9 +283,7 @@ public final class ClientOperation implements Operation, BaseStream {
/**
* Open and return a data output stream for a connection.
- *
* @return an output stream
- *
* @throws IOException if an I/O error occurs
*/
public DataOutputStream openDataOutputStream() throws IOException {
@@ -312,7 +292,6 @@ public final class ClientOperation implements Operation, BaseStream {
/**
* Closes the connection and ends the transaction
- *
* @throws IOException if the operation has already ended or is closed
*/
public void close() throws IOException {
@@ -324,11 +303,9 @@ public final class ClientOperation implements Operation, BaseStream {
/**
* Returns the headers that have been received during the operation.
- * Modifying the object returned has no effect on the headers that are
- * sent or retrieved.
- *
+ * Modifying the object returned has no effect on the headers that are sent
+ * or retrieved.
* @return the headers received during this <code>Operation</code>
- *
* @throws IOException if this <code>Operation</code> has been closed
*/
public HeaderSet getReceivedHeader() throws IOException {
@@ -340,15 +317,11 @@ public final class ClientOperation implements Operation, BaseStream {
/**
* Specifies the headers that should be sent in the next OBEX message that
* is sent.
- *
* @param headers the headers to send in the next message
- *
- * @throws IOException if this <code>Operation</code> has been closed
- * or the transaction has ended and no further messages will be exchanged
- *
+ * @throws IOException if this <code>Operation</code> has been closed or the
+ * transaction has ended and no further messages will be exchanged
* @throws IllegalArgumentException if <code>headers</code> was not created
- * by a call to <code>ServerRequestHandler.createHeaderSet()</code>
- *
+ * by a call to <code>ServerRequestHandler.createHeaderSet()</code>
* @throws NullPointerException if <code>headers</code> is <code>null</code>
*/
public void sendHeaders(HeaderSet headers) throws IOException {
@@ -370,62 +343,8 @@ public final class ClientOperation implements Operation, BaseStream {
}
/**
- * Reads a response from the server. It will populate the appropriate body
- * and headers.
- *
- * @return <code>true</code> if the transaction should end;
- * <code>false</code> if the transaction should not end
- *
- * @throws IOException if an IO error occurred
- */
- /*
- private boolean readResponse() throws IOException {
- mReplyHeader.responseCode = mInput.read();
- int packetLength = mInput.read();
- packetLength = (packetLength << 8) + mInput.read();
-
- if (packetLength > ObexHelper.MAX_PACKET_SIZE_INT) {
- if (mExceptionMessage != null) {
- abort();
- }
- throw new IOException("Received a packet that was too big");
- }
-
- if (packetLength > ObexHelper.BASE_PACKET_LENGTH) {
- int dataLength = packetLength - ObexHelper.BASE_PACKET_LENGTH;
- byte[] data = new byte[dataLength];
- int readLength = mInput.read(data);
- if (readLength != dataLength) {
- throw new IOException("Received a packet without data as decalred length");
- }
- byte[] body = ObexHelper.updateHeaderSet(mReplyHeader, data);
-
- if (body != null) {
- mPrivateInput.writeBytes(body, 1);
-
- /*
- * Determine if a body (0x48) header or an end of body (0x49)
- * was received. If we received an end of body and
- * a response code of OBEX_HTTP_OK, then the operation should
- * end.
- *
- if ((body[0] == 0x49) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_OK)) {
- return false;
- }
- }
- }
-
- if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
- return true;
- } else {
- return false;
- }
- }
- */
- /**
- * Verifies that additional information may be sent. In other words, the
+ * Verifies that additional information may be sent. In other words, the
* operation is not done.
- *
* @throws IOException if the operation is completed
*/
public void ensureNotDone() throws IOException {
@@ -436,7 +355,6 @@ public final class ClientOperation implements Operation, BaseStream {
/**
* Verifies that the connection is open and no exceptions should be thrown.
- *
* @throws IOException if an exception needs to be thrown
*/
public void ensureOpen() throws IOException {
@@ -452,7 +370,6 @@ public final class ClientOperation implements Operation, BaseStream {
/**
* Verifies that the connection is open and the proper data has been read.
- *
* @throws IOException if an IO error occurs
*/
private void validateConnection() throws IOException {
@@ -466,15 +383,12 @@ public final class ClientOperation implements Operation, BaseStream {
/**
* Sends a request to the client of the specified type
- *
- * @param response the response code to send back to the client
- *
+ * @param opCode the request code to send to the client
* @return <code>true</code> if there is more data to send;
- * <code>false</code> if there is no more data to send
- *
+ * <code>false</code> if there is no more data to send
* @throws IOException if an IO error occurs
*/
- private boolean sendRequest(int type) throws IOException {
+ private boolean sendRequest(int opCode) throws IOException {
boolean returnValue = false;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int bodyLength = -1;
@@ -519,7 +433,7 @@ public final class ClientOperation implements Operation, BaseStream {
byte[] sendHeader = new byte[end - start];
System.arraycopy(headerArray, start, sendHeader, 0, sendHeader.length);
- if (!mParent.sendRequest(type, sendHeader, mReplyHeader, mPrivateInput)) {
+ if (!mParent.sendRequest(opCode, sendHeader, mReplyHeader, mPrivateInput)) {
return false;
}
@@ -559,7 +473,7 @@ public final class ClientOperation implements Operation, BaseStream {
* (End of Body) otherwise, we need to send 0x48 (Body)
*/
if ((mPrivateOutput.isClosed()) && (!returnValue) && (!mEndOfBodySent)
- && ((type & 0x80) != 0)) {
+ && ((opCode & 0x80) != 0)) {
out.write(0x49);
mEndOfBodySent = true;
} else {
@@ -577,7 +491,7 @@ public final class ClientOperation implements Operation, BaseStream {
if (mPrivateOutputOpen && bodyLength <= 0 && !mEndOfBodySent) {
// only 0x82 or 0x83 can send 0x49
- if ((type & 0x80) == 0) {
+ if ((opCode & 0x80) == 0) {
out.write(0x48);
} else {
out.write(0x49);
@@ -591,13 +505,13 @@ public final class ClientOperation implements Operation, BaseStream {
}
if (out.size() == 0) {
- if (!mParent.sendRequest(type, null, mReplyHeader, mPrivateInput)) {
+ if (!mParent.sendRequest(opCode, null, mReplyHeader, mPrivateInput)) {
return false;
}
return returnValue;
}
if ((out.size() > 0)
- && (!mParent.sendRequest(type, out.toByteArray(), mReplyHeader, mPrivateInput))) {
+ && (!mParent.sendRequest(opCode, out.toByteArray(), mReplyHeader, mPrivateInput))) {
return false;
}
@@ -610,10 +524,9 @@ public final class ClientOperation implements Operation, BaseStream {
}
/**
- * This method starts the processing thread results. It will send the
- * initial request. If the response takes more then one packet, a thread
+ * This method starts the processing thread results. It will send the
+ * initial request. If the response takes more then one packet, a thread
* will be started to handle additional requests
- *
* @throws IOException if an IO error occurs
*/
private synchronized void startProcessing() throws IOException {
@@ -659,11 +572,10 @@ public final class ClientOperation implements Operation, BaseStream {
/**
* Continues the operation since there is no data to read.
- *
- * @param sendEmpty <code>true</code> if the operation should send an
- * empty packet or not send anything if there is no data to send
- * @param inStream <code>true</code> if the stream is input stream or
- * is output stream
+ * @param sendEmpty <code>true</code> if the operation should send an empty
+ * packet or not send anything if there is no data to send
+ * @param inStream <code>true</code> if the stream is input stream or is
+ * output stream
* @throws IOException if an IO error occurs
*/
public synchronized boolean continueOperation(boolean sendEmpty, boolean inStream)
@@ -717,10 +629,8 @@ public final class ClientOperation implements Operation, BaseStream {
/**
* Called when the output or input stream is closed.
- *
* @param inStream <code>true</code> if the input stream is closed;
- * <code>false</code> if the output stream is closed
- *
+ * <code>false</code> if the output stream is closed
* @throws IOException if an IO error occurs
*/
public void streamClosed(boolean inStream) throws IOException {
@@ -804,7 +714,6 @@ public final class ClientOperation implements Operation, BaseStream {
if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
mOperationDone = true;
}
-
}
}
}
diff --git a/obex/javax/obex/ClientSession.java b/obex/javax/obex/ClientSession.java
index d5549222edde..093538330f5d 100644
--- a/obex/javax/obex/ClientSession.java
+++ b/obex/javax/obex/ClientSession.java
@@ -39,7 +39,6 @@ import java.io.OutputStream;
/**
* This class in an implementation of the OBEX ClientSession.
- *
* @hide
*/
public final class ClientSession extends ObexSession {
@@ -163,7 +162,7 @@ public final class ClientSession extends ObexSession {
}
/**
- * 0xCB Connection Id an identifier used for OBEX connection multiplexing
+ * 0xCB Connection Id an identifier used for OBEX connection multiplexing
*/
public void setConnectionID(long id) {
if ((id < 0) || (id > 0xFFFFFFFFL)) {
@@ -365,7 +364,6 @@ public final class ClientSession extends ObexSession {
/**
* Verifies that the connection is open.
- *
* @throws IOException if the connection is closed
*/
public synchronized void ensureOpen() throws IOException {
@@ -375,9 +373,8 @@ public final class ClientSession extends ObexSession {
}
/**
- * Set request inactive.
- * Allows Put and get operation objects to tell this object when they are
- * done.
+ * Set request inactive. Allows Put and get operation objects to tell this
+ * object when they are done.
*/
/*package*/synchronized void setRequestInactive() {
mRequestActive = false;
@@ -395,27 +392,17 @@ public final class ClientSession extends ObexSession {
}
/**
- * Sends a standard request to the client. It will then wait for the reply
- * and update the header set object provided. If any authentication
- * headers (i.e. authentication challenge or authentication response) are
- * received, they will be processed.
- *
+ * Sends a standard request to the client. It will then wait for the reply
+ * and update the header set object provided. If any authentication headers
+ * (i.e. authentication challenge or authentication response) are received,
+ * they will be processed.
* @param opCode the type of request to send to the client
- *
- * @param head the headers to send to the server
- *
- * @param challenge the nonce that was sent in the authentication
- * challenge header located in <code>head</code>; <code>null</code>
- * if no authentication header is included in <code>head</code>
- *
+ * @param head the headers to send to the client
* @param header the header object to update with the response
- *
- * @param input the input stream used by the Operation object; null if this
- * is called on a CONNECT, SETPATH or DISCONNECT
- *
- * return <code>true</code> if the operation completed successfully;
- * <code>false</code> if an authentication response failed to pass
- *
+ * @param privateInput the input stream used by the Operation object; null
+ * if this is called on a CONNECT, SETPATH or DISCONNECT return
+ * <code>true</code> if the operation completed successfully;
+ * <code>false</code> if an authentication response failed to pass
* @throws IOException if an IO error occurs
*/
public boolean sendRequest(int opCode, byte[] head, HeaderSet header,
diff --git a/obex/javax/obex/HeaderSet.java b/obex/javax/obex/HeaderSet.java
index f777da65cd93..8b457f66ece2 100644
--- a/obex/javax/obex/HeaderSet.java
+++ b/obex/javax/obex/HeaderSet.java
@@ -40,28 +40,27 @@ import java.util.Random;
/**
* This class implements the javax.obex.HeaderSet interface for OBEX over
* RFCOMM.
- *
* @hide
*/
public final class HeaderSet {
/**
- * Represents the OBEX Count header. This allows the connection statement
- * to tell the server how many objects it plans to send or retrieve.
+ * Represents the OBEX Count header. This allows the connection statement to
+ * tell the server how many objects it plans to send or retrieve.
* <P>
* The value of <code>COUNT</code> is 0xC0 (192).
*/
public static final int COUNT = 0xC0;
/**
- * Represents the OBEX Name header. This specifies the name of the object.
+ * Represents the OBEX Name header. This specifies the name of the object.
* <P>
* The value of <code>NAME</code> is 0x01 (1).
*/
public static final int NAME = 0x01;
/**
- * Represents the OBEX Type header. This allows a request to specify the
+ * Represents the OBEX Type header. This allows a request to specify the
* type of the object (e.g. text, html, binary, etc.).
* <P>
* The value of <code>TYPE</code> is 0x42 (66).
@@ -69,7 +68,7 @@ public final class HeaderSet {
public static final int TYPE = 0x42;
/**
- * Represents the OBEX Length header. This is the length of the object in
+ * Represents the OBEX Length header. This is the length of the object in
* bytes.
* <P>
* The value of <code>LENGTH</code> is 0xC3 (195).
@@ -77,32 +76,32 @@ public final class HeaderSet {
public static final int LENGTH = 0xC3;
/**
- * Represents the OBEX Time header using the ISO 8601 standards. This is
- * the preferred time header.
+ * Represents the OBEX Time header using the ISO 8601 standards. This is the
+ * preferred time header.
* <P>
* The value of <code>TIME_ISO_8601</code> is 0x44 (68).
*/
public static final int TIME_ISO_8601 = 0x44;
/**
- * Represents the OBEX Time header using the 4 byte representation. This
- * is only included for backwards compatibility. It represents the number
- * of seconds since January 1, 1970.
+ * Represents the OBEX Time header using the 4 byte representation. This is
+ * only included for backwards compatibility. It represents the number of
+ * seconds since January 1, 1970.
* <P>
* The value of <code>TIME_4_BYTE</code> is 0xC4 (196).
*/
public static final int TIME_4_BYTE = 0xC4;
/**
- * Represents the OBEX Description header. This is a text description of
- * the object.
+ * Represents the OBEX Description header. This is a text description of the
+ * object.
* <P>
* The value of <code>DESCRIPTION</code> is 0x05 (5).
*/
public static final int DESCRIPTION = 0x05;
/**
- * Represents the OBEX Target header. This is the name of the service an
+ * Represents the OBEX Target header. This is the name of the service an
* operation is targeted to.
* <P>
* The value of <code>TARGET</code> is 0x46 (70).
@@ -110,7 +109,7 @@ public final class HeaderSet {
public static final int TARGET = 0x46;
/**
- * Represents the OBEX HTTP header. This allows an HTTP 1.X header to be
+ * Represents the OBEX HTTP header. This allows an HTTP 1.X header to be
* included in a request or reply.
* <P>
* The value of <code>HTTP</code> is 0x47 (71).
@@ -132,7 +131,7 @@ public final class HeaderSet {
public static final int END_OF_BODY = 0x49;
/**
- * Represents the OBEX Who header. Identifies the OBEX application to
+ * Represents the OBEX Who header. Identifies the OBEX application to
* determine if the two peers are talking to each other.
* <P>
* The value of <code>WHO</code> is 0x4A (74).
@@ -149,7 +148,7 @@ public final class HeaderSet {
public static final int CONNECTION_ID = 0xCB;
/**
- * Represents the OBEX Application Parameter header. This header specifies
+ * Represents the OBEX Application Parameter header. This header specifies
* additional application request and response information.
* <P>
* The value of <code>APPLICATION_PARAMETER</code> is 0x4C (76).
@@ -171,8 +170,8 @@ public final class HeaderSet {
public static final int AUTH_RESPONSE = 0x4E;
/**
- * Represents the OBEX Object Class header. This header specifies the
- * OBEX object class of the object.
+ * Represents the OBEX Object Class header. This header specifies the OBEX
+ * object class of the object.
* <P>
* The value of <code>OBJECT_CLASS</code> is 0x4F (79).
*/
@@ -200,12 +199,6 @@ public final class HeaderSet {
private byte[] mAppParam; // byte sequence of the form tag length value
- public byte[] mAuthChall; // The authentication challenge header
-
- public byte[] mAuthResp; // The authentication response header
-
- public byte[] mConnectionID; // THe connection ID
-
private byte[] mObjectClass; // byte sequence
private String[] mUnicodeUserDefined; //null terminated unicode string
@@ -216,15 +209,20 @@ public final class HeaderSet {
private Long[] mIntegerUserDefined; // 4 byte unsigned integer
- /*package*/int responseCode;
+ private final Random mRandom;
- /*package*/byte[] nonce;
+ /*package*/ byte[] nonce;
- private final Random mRandom;
+ public byte[] mAuthChall; // The authentication challenge header
+
+ public byte[] mAuthResp; // The authentication response header
+
+ public byte[] mConnectionID; // THe connection ID
+
+ public int responseCode;
/**
* Creates new <code>HeaderSet</code> object.
- *
* @param size the max packet size for this connection
*/
public HeaderSet() {
@@ -237,20 +235,17 @@ public final class HeaderSet {
}
/**
- * Sets the value of the header identifier to the value provided. The type
+ * Sets the value of the header identifier to the value provided. The type
* of object must correspond to the Java type defined in the description of
- * this interface. If <code>null</code> is passed as the
+ * this interface. If <code>null</code> is passed as the
* <code>headerValue</code> then the header will be removed from the set of
* headers to include in the next request.
- *
* @param headerID the identifier to include in the message
- *
* @param headerValue the value of the header identifier
- *
- * @throws IllegalArgumentException if the header identifier provided is
- * not one defined in this interface or a user-defined header; if the type of
- * <code>headerValue</code> is not the correct Java type as defined in the
- * description of this interface\
+ * @throws IllegalArgumentException if the header identifier provided is not
+ * one defined in this interface or a user-defined header; if the
+ * type of <code>headerValue</code> is not the correct Java type as
+ * defined in the description of this interface\
*/
public void setHeader(int headerID, Object headerValue) {
long temp = -1;
@@ -435,20 +430,16 @@ public final class HeaderSet {
}
/**
- * Retrieves the value of the header identifier provided. The type of the
+ * Retrieves the value of the header identifier provided. The type of the
* Object returned is defined in the description of this interface.
- *
* @param headerID the header identifier whose value is to be returned
- *
* @return the value of the header provided or <code>null</code> if the
- * header identifier specified is not part of this <code>HeaderSet</code>
- * object
- *
- * @throws IllegalArgumentException if the <code>headerID</code> is not
- * one defined in this interface or any of the user-defined headers
- *
+ * header identifier specified is not part of this
+ * <code>HeaderSet</code> object
+ * @throws IllegalArgumentException if the <code>headerID</code> is not one
+ * defined in this interface or any of the user-defined headers
* @throws IOException if an error occurred in the transport layer during
- * the operation or if the connection has been closed
+ * the operation or if the connection has been closed
*/
public Object getHeader(int headerID) throws IOException {
@@ -500,17 +491,14 @@ public final class HeaderSet {
/**
* Retrieves the list of headers that may be retrieved via the
- * <code>getHeader</code> method that will not return <code>null</code>.
- * In other words, this method returns all the headers that are available
- * in this object.
- *
+ * <code>getHeader</code> method that will not return <code>null</code>. In
+ * other words, this method returns all the headers that are available in
+ * this object.
* @see #getHeader
- *
* @return the array of headers that are set in this object or
- * <code>null</code> if no headers are available
- *
+ * <code>null</code> if no headers are available
* @throws IOException if an error occurred in the transport layer during
- * the operation or the connection has been closed
+ * the operation or the connection has been closed
*/
public int[] getHeaderList() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -594,51 +582,41 @@ public final class HeaderSet {
}
/**
- * Sets the authentication challenge header. The <code>realm</code> will
- * be encoded based upon the default encoding scheme used by the
- * implementation to encode strings. Therefore, the encoding scheme used
- * to encode the <code>realm</code> is application dependent.
- *
+ * Sets the authentication challenge header. The <code>realm</code> will be
+ * encoded based upon the default encoding scheme used by the implementation
+ * to encode strings. Therefore, the encoding scheme used to encode the
+ * <code>realm</code> is application dependent.
* @param realm a short description that describes what password to use; if
- * <code>null</code> no realm will be sent in the authentication challenge
- * header
- *
+ * <code>null</code> no realm will be sent in the authentication
+ * challenge header
* @param userID if <code>true</code>, a user ID is required in the reply;
- * if <code>false</code>, no user ID is required
- *
+ * if <code>false</code>, no user ID is required
* @param access if <code>true</code> then full access will be granted if
- * successful; if <code>false</code> then read-only access will be granted
- * if successful
+ * successful; if <code>false</code> then read-only access will be
+ * granted if successful
* @throws IOException
*/
public void createAuthenticationChallenge(String realm, boolean userID, boolean access)
throws IOException {
- try {
- nonce = new byte[16];
- for (int i = 0; i < 16; i++) {
- nonce[i] = (byte)mRandom.nextInt();
- }
-
- mAuthChall = ObexHelper.computeAuthenticationChallenge(nonce, realm, access, userID);
- } catch (IOException e) {
- throw e;
+ nonce = new byte[16];
+ for (int i = 0; i < 16; i++) {
+ nonce[i] = (byte)mRandom.nextInt();
}
+
+ mAuthChall = ObexHelper.computeAuthenticationChallenge(nonce, realm, access, userID);
}
/**
- * Returns the response code received from the server. Response codes
- * are defined in the <code>ResponseCodes</code> class.
- *
+ * Returns the response code received from the server. Response codes are
+ * defined in the <code>ResponseCodes</code> class.
* @see ResponseCodes
- *
* @return the response code retrieved from the server
- *
* @throws IOException if an error occurred in the transport layer during
- * the transaction; if this method is called on a <code>HeaderSet</code>
- * object created by calling <code>createHeaderSet()</code> in a
- * <code>ClientSession</code> object; if this object was created by an OBEX
- * server
+ * the transaction; if this method is called on a
+ * <code>HeaderSet</code> object created by calling
+ * <code>createHeaderSet()</code> in a <code>ClientSession</code>
+ * object; if this object was created by an OBEX server
*/
public int getResponseCode() throws IOException {
if (responseCode == -1) {
diff --git a/obex/javax/obex/ObexHelper.java b/obex/javax/obex/ObexHelper.java
index 511b7c667531..f5695954f000 100644
--- a/obex/javax/obex/ObexHelper.java
+++ b/obex/javax/obex/ObexHelper.java
@@ -43,16 +43,14 @@ import java.util.TimeZone;
/**
* This class defines a set of helper methods for the implementation of Obex.
- *
* @hide
*/
public final class ObexHelper {
/**
- * Defines the basic packet length used by OBEX. Every OBEX packet has the
+ * Defines the basic packet length used by OBEX. Every OBEX packet has the
* same basic format:<BR>
- * Byte 0: Request or Response Code
- * Byte 1&2: Length of the packet.
+ * Byte 0: Request or Response Code Byte 1&2: Length of the packet.
*/
public static final int BASE_PACKET_LENGTH = 3;
@@ -61,17 +59,14 @@ public final class ObexHelper {
}
/**
- * The maximum packet size for OBEX packets that this client can handle.
- * At present, this must be changed for each port.
- *
- * TODO: The max packet size should be the Max incoming MTU minus
- * TODO: L2CAP package headers and RFCOMM package headers.
- *
- * TODO: Retrieve the max incoming MTU from
- * TODO: LocalDevice.getProperty().
+ * The maximum packet size for OBEX packets that this client can handle. At
+ * present, this must be changed for each port. TODO: The max packet size
+ * should be the Max incoming MTU minus TODO: L2CAP package headers and
+ * RFCOMM package headers. TODO: Retrieve the max incoming MTU from TODO:
+ * LocalDevice.getProperty().
*/
- /** android note
- * set as 0xFFFE to match remote MPS
+ /*
+ * android note set as 0xFFFE to match remote MPS
*/
public static final int MAX_PACKET_SIZE_INT = 0xFFFE;
@@ -119,34 +114,46 @@ public final class ObexHelper {
/**
* Updates the HeaderSet with the headers received in the byte array
- * provided. Invalid headers are ignored.
+ * provided. Invalid headers are ignored.
* <P>
- * The first two bits of an OBEX Header specifies the type of object that
- * is being sent. The table below specifies the meaning of the high
- * bits.
+ * The first two bits of an OBEX Header specifies the type of object that is
+ * being sent. The table below specifies the meaning of the high bits.
* <TABLE>
- * <TR><TH>Bits 8 and 7</TH><TH>Value</TH><TH>Description</TH></TR>
- * <TR><TD>00</TD><TD>0x00</TD><TD>Null Terminated Unicode text, prefixed
- * with 2 byte unsigned integer</TD></TR>
- * <TR><TD>01</TD><TD>0x40</TD><TD>Byte Sequence, length prefixed with
- * 2 byte unsigned integer</TD></TR>
- * <TR><TD>10</TD><TD>0x80</TD><TD>1 byte quantity</TD></TR>
- * <TR><TD>11</TD><TD>0xC0</TD><TD>4 byte quantity - transmitted in
- * network byte order (high byte first</TD></TR>
+ * <TR>
+ * <TH>Bits 8 and 7</TH>
+ * <TH>Value</TH>
+ * <TH>Description</TH>
+ * </TR>
+ * <TR>
+ * <TD>00</TD>
+ * <TD>0x00</TD>
+ * <TD>Null Terminated Unicode text, prefixed with 2 byte unsigned integer</TD>
+ * </TR>
+ * <TR>
+ * <TD>01</TD>
+ * <TD>0x40</TD>
+ * <TD>Byte Sequence, length prefixed with 2 byte unsigned integer</TD>
+ * </TR>
+ * <TR>
+ * <TD>10</TD>
+ * <TD>0x80</TD>
+ * <TD>1 byte quantity</TD>
+ * </TR>
+ * <TR>
+ * <TD>11</TD>
+ * <TD>0xC0</TD>
+ * <TD>4 byte quantity - transmitted in network byte order (high byte first</TD>
+ * </TR>
* </TABLE>
* This method uses the information in this table to determine the type of
- * Java object to create and passes that object with the full header
- * to setHeader() to update the HeaderSet object. Invalid headers will
- * cause an exception to be thrown. When it is thrown, it is ignored.
- *
+ * Java object to create and passes that object with the full header to
+ * setHeader() to update the HeaderSet object. Invalid headers will cause an
+ * exception to be thrown. When it is thrown, it is ignored.
* @param header the HeaderSet to update
- *
* @param headerArray the byte array containing headers
- *
* @return the result of the last start body or end body header provided;
- * the first byte in the result will specify if a body or end of body is
- * received
- *
+ * the first byte in the result will specify if a body or end of
+ * body is received
* @throws IOException if an invalid header was found
*/
public static byte[] updateHeaderSet(HeaderSet header, byte[] headerArray) throws IOException {
@@ -316,18 +323,13 @@ public final class ObexHelper {
/**
* Creates the header part of OBEX packet based on the header provided.
- *
- * TODO: Could use getHeaderList() to get the array of headers to
- * TODO: include and then use the high two bits to determine the
- * TODO: the type of the object and construct the byte array from
- * TODO: that. This will make the size smaller.
- *
+ * TODO: Could use getHeaderList() to get the array of headers to include
+ * and then use the high two bits to determine the the type of the object
+ * and construct the byte array from that. This will make the size smaller.
* @param head the header used to construct the byte array
- *
* @param nullOut <code>true</code> if the header should be set to
- * <code>null</code> once it is added to the array or <code>false</code>
- * if it should not be nulled out
- *
+ * <code>null</code> once it is added to the array or
+ * <code>false</code> if it should not be nulled out
* @return the header of an OBEX packet
*/
public static byte[] createHeader(HeaderSet head, boolean nullOut) {
@@ -342,9 +344,6 @@ public final class ObexHelper {
int length;
HeaderSet headImpl = null;
ByteArrayOutputStream out = new ByteArrayOutputStream();
- if (!(head instanceof HeaderSet)) {
- throw new IllegalArgumentException("Header not created by createHeaderSet");
- }
headImpl = head;
try {
@@ -675,18 +674,14 @@ public final class ObexHelper {
}
/**
- * Determines where the maximum divide is between headers. This method is
+ * Determines where the maximum divide is between headers. This method is
* used by put and get operations to separate headers to a size that meets
* the max packet size allowed.
- *
* @param headerArray the headers to separate
- *
* @param start the starting index to search
- *
* @param maxSize the maximum size of a packet
- *
* @return the index of the end of the header block to send or -1 if the
- * header could not be divided because the header is too large
+ * header could not be divided because the header is too large
*/
public static int findHeaderEnd(byte[] headerArray, int start, int maxSize) {
@@ -757,9 +752,7 @@ public final class ObexHelper {
/**
* Converts the byte array to a long.
- *
* @param b the byte array to convert to a long
- *
* @return the byte array as a long
*/
public static long convertToLong(byte[] b) {
@@ -781,10 +774,8 @@ public final class ObexHelper {
}
/**
- * Converts the long to a 4 byte array. The long must be non negative.
- *
+ * Converts the long to a 4 byte array. The long must be non negative.
* @param l the long to convert
- *
* @return a byte array that is the same as the long
*/
public static byte[] convertToByteArray(long l) {
@@ -799,11 +790,9 @@ public final class ObexHelper {
}
/**
- * Converts the String to a UNICODE byte array. It will also add the ending
+ * Converts the String to a UNICODE byte array. It will also add the ending
* null characters to the end of the string.
- *
* @param s the string to convert
- *
* @return the unicode byte array of the string
*/
public static byte[] convertToUnicodeByteArray(String s) {
@@ -826,13 +815,10 @@ public final class ObexHelper {
}
/**
- * Retrieves the value from the byte array for the tag value specified. The
+ * Retrieves the value from the byte array for the tag value specified. The
* array should be of the form Tag - Length - Value triplet.
- *
* @param tag the tag to retrieve from the byte array
- *
* @param triplet the byte sequence containing the tag length value form
- *
* @return the value of the specified tag
*/
public static byte[] getTagValue(byte tag, byte[] triplet) {
@@ -854,11 +840,8 @@ public final class ObexHelper {
/**
* Finds the index that starts the tag value pair in the byte array provide.
- *
* @param tag the tag to look for
- *
* @param value the byte array to search
- *
* @return the starting index of the tag or -1 if the tag could not be found
*/
public static int findTag(byte tag, byte[] value) {
@@ -884,19 +867,15 @@ public final class ObexHelper {
/**
* Converts the byte array provided to a unicode string.
- *
* @param b the byte array to convert to a string
- *
* @param includesNull determine if the byte string provided contains the
- * UNICODE null character at the end or not; if it does, it will be
- * removed
- *
+ * UNICODE null character at the end or not; if it does, it will be
+ * removed
* @return a Unicode string
- *
- * @param IllegalArgumentException if the byte array has an odd length
+ * @throws IllegalArgumentException if the byte array has an odd length
*/
public static String convertToUnicode(byte[] b, boolean includesNull) {
- if (b == null) {
+ if (b == null || b.length == 0) {
return null;
}
int arrayLength = b.length;
@@ -926,9 +905,8 @@ public final class ObexHelper {
}
/**
- * Compute the MD5 hash of the byte array provided.
- * Does not accumulate input.
- *
+ * Compute the MD5 hash of the byte array provided. Does not accumulate
+ * input.
* @param in the byte array to hash
* @return the MD5 hash of the byte array
*/
@@ -939,23 +917,16 @@ public final class ObexHelper {
/**
* Computes an authentication challenge header.
- *
- *
- * @param nonce the challenge that will be provided to the peer; the
- * challenge must be 16 bytes long
- *
+ * @param nonce the challenge that will be provided to the peer; the
+ * challenge must be 16 bytes long
* @param realm a short description that describes what password to use
- *
* @param access if <code>true</code> then full access will be granted if
- * successful; if <code>false</code> then read only access will be granted
- * if successful
- *
+ * successful; if <code>false</code> then read only access will be
+ * granted if successful
* @param userID if <code>true</code>, a user ID is required in the reply;
- * if <code>false</code>, no user ID is required
- *
- * @throws IllegalArgumentException if the challenge is not 16 bytes
- * long; if the realm can not be encoded in less then 255 bytes
- *
+ * if <code>false</code>, no user ID is required
+ * @throws IllegalArgumentException if the challenge is not 16 bytes long;
+ * if the realm can not be encoded in less then 255 bytes
* @throws IOException if the encoding scheme ISO 8859-1 is not supported
*/
public static byte[] computeAuthenticationChallenge(byte[] nonce, String realm, boolean access,
diff --git a/obex/javax/obex/ObexSession.java b/obex/javax/obex/ObexSession.java
index 97d65e099d2f..a7daeb533e16 100644
--- a/obex/javax/obex/ObexSession.java
+++ b/obex/javax/obex/ObexSession.java
@@ -41,10 +41,8 @@ import java.io.IOException;
* of the same connection, which is established by server's accepting of a
* client issued "CONNECT".
* <P>
- *
* This interface serves as the common super class for
* <CODE>ClientSession</CODE> and <CODE>ServerSession</CODE>.
- *
* @hide
*/
public class ObexSession {
@@ -56,10 +54,7 @@ public class ObexSession {
/**
* Called when the server received an authentication challenge header. This
* will cause the authenticator to handle the authentication challenge.
- *
- * @param header
- * the header with the authentication challenge
- *
+ * @param header the header with the authentication challenge
* @return <code>true</code> if the last request should be resent;
* <code>false</code> if the last request should not be resent
* @throws IOException
@@ -188,10 +183,7 @@ public class ObexSession {
/**
* Called when the server received an authentication response header. This
* will cause the authenticator to handle the authentication response.
- *
- * @param authResp
- * the authentication response
- *
+ * @param authResp the authentication response
* @return <code>true</code> if the response passed; <code>false</code> if
* the response failed
*/
diff --git a/obex/javax/obex/ObexTransport.java b/obex/javax/obex/ObexTransport.java
index d0ba0c92f10f..445e2675b212 100644
--- a/obex/javax/obex/ObexTransport.java
+++ b/obex/javax/obex/ObexTransport.java
@@ -51,7 +51,6 @@ import java.io.OutputStream;
* Different kind of medium may have different construction - for example, the
* RFCOMM device file medium may be constructed from a file descriptor or simply
* a string while the TCP medium usually from a socket.
- *
* @hide
*/
public interface ObexTransport {
diff --git a/obex/javax/obex/Operation.java b/obex/javax/obex/Operation.java
index f265f5335999..20653f26df2d 100644
--- a/obex/javax/obex/Operation.java
+++ b/obex/javax/obex/Operation.java
@@ -40,44 +40,39 @@ import java.io.OutputStream;
/**
* The <code>Operation</code> interface provides ways to manipulate a single
- * OBEX PUT or GET operation. The implementation of this interface sends
- * OBEX packets as they are built. If during the operation the peer in the
- * operation ends the operation, an <code>IOException</code> is thrown on
- * the next read from the input stream, write to the output stream, or call to
+ * OBEX PUT or GET operation. The implementation of this interface sends OBEX
+ * packets as they are built. If during the operation the peer in the operation
+ * ends the operation, an <code>IOException</code> is thrown on the next read
+ * from the input stream, write to the output stream, or call to
* <code>sendHeaders()</code>.
* <P>
- * <STRONG>Definition of methods inherited from <code>ContentConnection</code></STRONG>
+ * <STRONG>Definition of methods inherited from <code>ContentConnection</code>
+ * </STRONG>
* <P>
- * <code>getEncoding()</code> will always return <code>null</code>.
- * <BR><code>getLength()</code> will return the length specified by the OBEX Length
- * header or -1 if the OBEX Length header was not included.
- * <BR><code>getType()</code> will return the value specified in the OBEX Type
+ * <code>getEncoding()</code> will always return <code>null</code>. <BR>
+ * <code>getLength()</code> will return the length specified by the OBEX Length
+ * header or -1 if the OBEX Length header was not included. <BR>
+ * <code>getType()</code> will return the value specified in the OBEX Type
* header or <code>null</code> if the OBEX Type header was not included.<BR>
* <P>
* <STRONG>How Headers are Handled</STRONG>
* <P>
* As headers are received, they may be retrieved through the
- * <code>getReceivedHeaders()</code> method. If new headers are set during the
+ * <code>getReceivedHeaders()</code> method. If new headers are set during the
* operation, the new headers will be sent during the next packet exchange.
* <P>
* <STRONG>PUT example</STRONG>
* <P>
* <PRE>
- * void putObjectViaOBEX(ClientSession conn, HeaderSet head, byte[] obj)
- * throws IOException {
- *
+ * void putObjectViaOBEX(ClientSession conn, HeaderSet head, byte[] obj) throws IOException {
* // Include the length header
* head.setHeader(head.LENGTH, new Long(obj.length));
- *
* // Initiate the PUT request
* Operation op = conn.put(head);
- *
* // Open the output stream to put the object to it
* DataOutputStream out = op.openDataOutputStream();
- *
* // Send the object to the server
* out.write(obj);
- *
* // End the transaction
* out.close();
* op.close();
@@ -88,64 +83,55 @@ import java.io.OutputStream;
* <P>
* <PRE>
* byte[] getObjectViaOBEX(ClientSession conn, HeaderSet head) throws IOException {
- *
* // Send the initial GET request to the server
* Operation op = conn.get(head);
- *
* // Retrieve the length of the object being sent back
* int length = op.getLength();
- *
- * // Create space for the object
- * byte[] obj = new byte[length];
- *
+ * // Create space for the object
+ * byte[] obj = new byte[length];
* // Get the object from the input stream
* DataInputStream in = trans.openDataInputStream();
* in.read(obj);
- *
* // End the transaction
* in.close();
* op.close();
- *
* return obj;
* }
* </PRE>
- * <H3>Client PUT Operation Flow</H3>
- * For PUT operations, a call to <code>close()</code> the <code>OutputStream</code>
- * returned from <code>openOutputStream()</code> or <code>openDataOutputStream()</code>
- * will signal that the request is done. (In OBEX terms, the End-Of-Body header should
- * be sent and the final bit in the request will be set.) At this point, the
- * reply from the server may begin to be processed. A call to
+ *
+ * <H3>Client PUT Operation Flow</H3> For PUT operations, a call to
+ * <code>close()</code> the <code>OutputStream</code> returned from
+ * <code>openOutputStream()</code> or <code>openDataOutputStream()</code> will
+ * signal that the request is done. (In OBEX terms, the End-Of-Body header
+ * should be sent and the final bit in the request will be set.) At this point,
+ * the reply from the server may begin to be processed. A call to
* <code>getResponseCode()</code> will do an implicit close on the
* <code>OutputStream</code> and therefore signal that the request is done.
- * <H3>Client GET Operation Flow</H3>
- * For GET operation, a call to <code>openInputStream()</code> or
- * <code>openDataInputStream()</code> will signal that the request is done. (In OBEX
- * terms, the final bit in the request will be set.) A call to
- * <code>getResponseCode()</code> will cause an implicit close on the
- * <code>InputStream</code>. No further data may be read at this point.
- *
+ * <H3>Client GET Operation Flow</H3> For GET operation, a call to
+ * <code>openInputStream()</code> or <code>openDataInputStream()</code> will
+ * signal that the request is done. (In OBEX terms, the final bit in the request
+ * will be set.) A call to <code>getResponseCode()</code> will cause an implicit
+ * close on the <code>InputStream</code>. No further data may be read at this
+ * point.
* @hide
*/
public interface Operation {
/**
- * Sends an ABORT message to the server. By calling this method, the
+ * Sends an ABORT message to the server. By calling this method, the
* corresponding input and output streams will be closed along with this
- * object. No headers are sent in the abort request. This will end the
+ * object. No headers are sent in the abort request. This will end the
* operation since <code>close()</code> will be called by this method.
- *
- * @throws IOException if the transaction has already ended or if an
- * OBEX server calls this method
+ * @throws IOException if the transaction has already ended or if an OBEX
+ * server calls this method
*/
void abort() throws IOException;
/**
* Returns the headers that have been received during the operation.
- * Modifying the object returned has no effect on the headers that are
- * sent or retrieved.
- *
+ * Modifying the object returned has no effect on the headers that are sent
+ * or retrieved.
* @return the headers received during this <code>Operation</code>
- *
* @throws IOException if this <code>Operation</code> has been closed
*/
HeaderSet getReceivedHeader() throws IOException;
@@ -153,30 +139,23 @@ public interface Operation {
/**
* Specifies the headers that should be sent in the next OBEX message that
* is sent.
- *
* @param headers the headers to send in the next message
- *
- * @throws IOException if this <code>Operation</code> has been closed
- * or the transaction has ended and no further messages will be exchanged
- *
+ * @throws IOException if this <code>Operation</code> has been closed or the
+ * transaction has ended and no further messages will be exchanged
* @throws IllegalArgumentException if <code>headers</code> was not created
- * by a call to <code>ServerRequestHandler.createHeaderSet()</code> or
- * <code>ClientSession.createHeaderSet()</code>
- *
+ * by a call to <code>ServerRequestHandler.createHeaderSet()</code>
+ * or <code>ClientSession.createHeaderSet()</code>
* @throws NullPointerException if <code>headers</code> if <code>null</code>
*/
void sendHeaders(HeaderSet headers) throws IOException;
/**
- * Returns the response code received from the server. Response codes
- * are defined in the <code>ResponseCodes</code> class.
- *
+ * Returns the response code received from the server. Response codes are
+ * defined in the <code>ResponseCodes</code> class.
* @see ResponseCodes
- *
* @return the response code retrieved from the server
- *
* @throws IOException if an error occurred in the transport layer during
- * the transaction; if this object was created by an OBEX server
+ * the transaction; if this object was created by an OBEX server
*/
int getResponseCode() throws IOException;
diff --git a/obex/javax/obex/PasswordAuthentication.java b/obex/javax/obex/PasswordAuthentication.java
index e81a8611391a..326b1ff56a74 100644
--- a/obex/javax/obex/PasswordAuthentication.java
+++ b/obex/javax/obex/PasswordAuthentication.java
@@ -34,7 +34,6 @@ package javax.obex;
/**
* This class holds user name and password combinations.
- *
* @hide
*/
public final class PasswordAuthentication {
@@ -44,15 +43,12 @@ public final class PasswordAuthentication {
private final byte[] mPassword;
/**
- * Creates a new <code>PasswordAuthentication</code> with the user name
- * and password provided.
- *
+ * Creates a new <code>PasswordAuthentication</code> with the user name and
+ * password provided.
* @param userName the user name to include; this may be <code>null</code>
- *
* @param password the password to include in the response
- *
* @throws NullPointerException if <code>password</code> is
- * <code>null</code>
+ * <code>null</code>
*/
public PasswordAuthentication(final byte[] userName, final byte[] password) {
if (userName != null) {
@@ -65,9 +61,8 @@ public final class PasswordAuthentication {
}
/**
- * Retrieves the user name that was specified in the constructor.
- * The user name may be <code>null</code>.
- *
+ * Retrieves the user name that was specified in the constructor. The user
+ * name may be <code>null</code>.
* @return the user name
*/
public byte[] getUserName() {
@@ -76,7 +71,6 @@ public final class PasswordAuthentication {
/**
* Retrieves the password.
- *
* @return the password
*/
public byte[] getPassword() {
diff --git a/obex/javax/obex/PrivateInputStream.java b/obex/javax/obex/PrivateInputStream.java
index 2dc02da511f1..5daee72474fc 100644
--- a/obex/javax/obex/PrivateInputStream.java
+++ b/obex/javax/obex/PrivateInputStream.java
@@ -38,7 +38,6 @@ import java.io.IOException;
/**
* This object provides an input stream to the Operation objects used in this
* package.
- *
* @hide
*/
public final class PrivateInputStream extends InputStream {
@@ -53,7 +52,6 @@ public final class PrivateInputStream extends InputStream {
/**
* Creates an input stream for the <code>Operation</code> to read from
- *
* @param p the connection this input stream is for
*/
public PrivateInputStream(BaseStream p) {
@@ -68,10 +66,8 @@ public final class PrivateInputStream extends InputStream {
* input stream without blocking by the next caller of a method for this
* input stream. The next caller might be the same thread or or another
* thread.
- *
* @return the number of bytes that can be read from this input stream
- * without blocking
- *
+ * without blocking
* @throws IOException if an I/O error occurs
*/
@Override
@@ -82,14 +78,12 @@ public final class PrivateInputStream extends InputStream {
/**
* Reads the next byte of data from the input stream. The value byte is
- * returned as an int in the range 0 to 255. If no byte is available
- * because the end of the stream has been reached, the value -1 is
- * returned. This method blocks until input data is available, the end of
- * the stream is detected, or an exception is thrown.
- *
- * @return the byte read from the input stream or -1 if it reaches the end
- * of stream
- *
+ * returned as an int in the range 0 to 255. If no byte is available because
+ * the end of the stream has been reached, the value -1 is returned. This
+ * method blocks until input data is available, the end of the stream is
+ * detected, or an exception is thrown.
+ * @return the byte read from the input stream or -1 if it reaches the end of
+ * stream
* @throws IOException if an I/O error occurs
*/
@Override
@@ -147,9 +141,7 @@ public final class PrivateInputStream extends InputStream {
/**
* Allows the <code>OperationImpl</code> thread to add body data to the
* input stream.
- *
* @param body the data to add to the stream
- *
* @param start the start of the body to array to copy
*/
public synchronized void writeBytes(byte[] body, int start) {
@@ -167,7 +159,6 @@ public final class PrivateInputStream extends InputStream {
/**
* Verifies that this stream is open
- *
* @throws IOException if the stream is not open
*/
private void ensureOpen() throws IOException {
@@ -178,9 +169,8 @@ public final class PrivateInputStream extends InputStream {
}
/**
- * Closes the input stream. If the input stream is already closed, do
+ * Closes the input stream. If the input stream is already closed, do
* nothing.
- *
* @throws IOException this will never happen
*/
@Override
diff --git a/obex/javax/obex/PrivateOutputStream.java b/obex/javax/obex/PrivateOutputStream.java
index d972f78a22ad..ca420afdda37 100644
--- a/obex/javax/obex/PrivateOutputStream.java
+++ b/obex/javax/obex/PrivateOutputStream.java
@@ -39,7 +39,6 @@ import java.io.ByteArrayOutputStream;
/**
* This object provides an output stream to the Operation objects used in this
* package.
- *
* @hide
*/
public final class PrivateOutputStream extends OutputStream {
@@ -54,18 +53,17 @@ public final class PrivateOutputStream extends OutputStream {
/**
* Creates an empty <code>PrivateOutputStream</code> to write to.
- *
* @param p the connection that this stream runs over
*/
public PrivateOutputStream(BaseStream p, int maxSize) {
mParent = p;
mArray = new ByteArrayOutputStream();
mMaxPacketSize = maxSize;
+ mOpen = true;
}
/**
* Determines how many bytes have been written to the output stream.
- *
* @return the number of bytes written to the output stream
*/
public int size() {
@@ -73,13 +71,11 @@ public final class PrivateOutputStream extends OutputStream {
}
/**
- * Writes the specified byte to this output stream. The general contract
- * for write is that one byte is written to the output stream. The byte to
- * be written is the eight low-order bits of the argument b. The 24
- * high-order bits of b are ignored.
- *
+ * Writes the specified byte to this output stream. The general contract for
+ * write is that one byte is written to the output stream. The byte to be
+ * written is the eight low-order bits of the argument b. The 24 high-order
+ * bits of b are ignored.
* @param b the byte to write
- *
* @throws IOException if an I/O error occurs
*/
@Override
@@ -128,9 +124,7 @@ public final class PrivateOutputStream extends OutputStream {
/**
* Reads the bytes that have been written to this stream.
- *
* @param size the size of the array to return
- *
* @return the byte array that is written
*/
public synchronized byte[] readBytes(int size) {
@@ -150,7 +144,6 @@ public final class PrivateOutputStream extends OutputStream {
/**
* Verifies that this stream is open
- *
* @throws IOException if the stream is not open
*/
private void ensureOpen() throws IOException {
@@ -161,9 +154,8 @@ public final class PrivateOutputStream extends OutputStream {
}
/**
- * Closes the output stream. If the input stream is already closed, do
+ * Closes the output stream. If the input stream is already closed, do
* nothing.
- *
* @throws IOException this will never happen
*/
@Override
@@ -174,9 +166,8 @@ public final class PrivateOutputStream extends OutputStream {
/**
* Determines if the connection is closed
- *
- * @return <code>true</code> if the connection is closed;
- * <code>false</code> if the connection is open
+ * @return <code>true</code> if the connection is closed; <code>false</code>
+ * if the connection is open
*/
public boolean isClosed() {
return !mOpen;
diff --git a/obex/javax/obex/ResponseCodes.java b/obex/javax/obex/ResponseCodes.java
index f6cc9c16a95f..a2b9a37b4af1 100644
--- a/obex/javax/obex/ResponseCodes.java
+++ b/obex/javax/obex/ResponseCodes.java
@@ -33,8 +33,8 @@
package javax.obex;
/**
- * The <code>ResponseCodes</code> class contains the list of valid
- * response codes a server may send to a client.
+ * The <code>ResponseCodes</code> class contains the list of valid response
+ * codes a server may send to a client.
* <P>
* <STRONG>IMPORTANT NOTE</STRONG>
* <P>
@@ -42,13 +42,12 @@ package javax.obex;
* specification, which is different with the HTTP specification.
* <P>
* <code>OBEX_DATABASE_FULL</code> and <code>OBEX_DATABASE_LOCKED</code> require
- * further description since they are not defined in HTTP. The server will send
+ * further description since they are not defined in HTTP. The server will send
* an <code>OBEX_DATABASE_FULL</code> message when the client requests that
* something be placed into a database but the database is full (cannot take
- * more data). <code>OBEX_DATABASE_LOCKED</code> will be returned when the
+ * more data). <code>OBEX_DATABASE_LOCKED</code> will be returned when the
* client wishes to access a database, database table, or database record that
* has been locked.
- *
* @hide
*/
public final class ResponseCodes {
diff --git a/obex/javax/obex/ServerOperation.java b/obex/javax/obex/ServerOperation.java
index 6c3d9ba1801e..8710c64a46d6 100644
--- a/obex/javax/obex/ServerOperation.java
+++ b/obex/javax/obex/ServerOperation.java
@@ -42,18 +42,14 @@ import java.io.ByteArrayOutputStream;
/**
* This class implements the Operation interface for server side connections.
* <P>
- * <STRONG>Request Codes</STRONG>
- * There are four different request codes that are in this class. 0x02 is a
- * PUT request that signals that the request is not complete and requires an
- * additional OBEX packet. 0x82 is a PUT request that says that request is
- * complete. In this case, the server can begin sending the response. The
- * 0x03 is a GET request that signals that the request is not finished. When
- * the server receives a 0x83, the client is signaling the server that it is
- * done with its request.
- *
- * TODO: Extend the ClientOperation and reuse the methods defined
- * TODO: in that class.
- *
+ * <STRONG>Request Codes</STRONG> There are four different request codes that
+ * are in this class. 0x02 is a PUT request that signals that the request is not
+ * complete and requires an additional OBEX packet. 0x82 is a PUT request that
+ * says that request is complete. In this case, the server can begin sending the
+ * response. The 0x03 is a GET request that signals that the request is not
+ * finished. When the server receives a 0x83, the client is signaling the server
+ * that it is done with its request. TODO: Extend the ClientOperation and reuse
+ * the methods defined TODO: in that class.
* @hide
*/
public final class ServerOperation implements Operation, BaseStream {
@@ -94,19 +90,12 @@ public final class ServerOperation implements Operation, BaseStream {
/**
* Creates new ServerOperation
- *
* @param p the parent that created this object
- *
* @param in the input stream to read from
- *
* @param out the output stream to write to
- *
* @param request the initial request that was received from the client
- *
* @param maxSize the max packet size that the client will accept
- *
* @param listen the listener that is responding to the request
- *
* @throws IOException if an IO error occurs
*/
public ServerOperation(ServerSession p, InputStream in, int request, int maxSize,
@@ -240,17 +229,16 @@ public final class ServerOperation implements Operation, BaseStream {
}
/**
- * Determines if the operation should continue or should wait. If it
- * should continue, this method will continue the operation.
- *
+ * Determines if the operation should continue or should wait. If it should
+ * continue, this method will continue the operation.
* @param sendEmpty if <code>true</code> then this will continue the
- * operation even if no headers will be sent; if <code>false</code> then
- * this method will only continue the operation if there are headers to
- * send
- * @param isStream if<code>true</code> the stream is input stream, otherwise
- * output stream
+ * operation even if no headers will be sent; if <code>false</code>
+ * then this method will only continue the operation if there are
+ * headers to send
+ * @param inStream if<code>true</code> the stream is input stream, otherwise
+ * output stream
* @return <code>true</code> if the operation was completed;
- * <code>false</code> if no operation took place
+ * <code>false</code> if no operation took place
*/
public synchronized boolean continueOperation(boolean sendEmpty, boolean inStream)
throws IOException {
@@ -277,15 +265,13 @@ public final class ServerOperation implements Operation, BaseStream {
}
/**
- * Sends a reply to the client. If the reply is a OBEX_HTTP_CONTINUE, it
+ * Sends a reply to the client. If the reply is a OBEX_HTTP_CONTINUE, it
* will wait for a response from the client before ending.
- *
* @param type the response code to send back to the client
- *
* @return <code>true</code> if the final bit was not set on the reply;
- * <code>false</code> if no reply was received because the operation ended,
- * an abort was received, or the final bit was set in the reply
- *
+ * <code>false</code> if no reply was received because the operation
+ * ended, an abort was received, or the final bit was set in the
+ * reply
* @throws IOException if an IO error occurs
*/
public synchronized boolean sendReply(int type) throws IOException {
@@ -492,12 +478,11 @@ public final class ServerOperation implements Operation, BaseStream {
}
/**
- * Sends an ABORT message to the server. By calling this method, the
+ * Sends an ABORT message to the server. By calling this method, the
* corresponding input and output streams will be closed along with this
* object.
- *
- * @throws IOException if the transaction has already ended or if an
- * OBEX server called this method
+ * @throws IOException if the transaction has already ended or if an OBEX
+ * server called this method
*/
public void abort() throws IOException {
throw new IOException("Called from a server");
@@ -505,11 +490,9 @@ public final class ServerOperation implements Operation, BaseStream {
/**
* Returns the headers that have been received during the operation.
- * Modifying the object returned has no effect on the headers that are
- * sent or retrieved.
- *
+ * Modifying the object returned has no effect on the headers that are sent
+ * or retrieved.
* @return the headers received during this <code>Operation</code>
- *
* @throws IOException if this <code>Operation</code> has been closed
*/
public HeaderSet getReceivedHeader() throws IOException {
@@ -520,14 +503,11 @@ public final class ServerOperation implements Operation, BaseStream {
/**
* Specifies the headers that should be sent in the next OBEX message that
* is sent.
- *
* @param headers the headers to send in the next message
- *
- * @throws IOException if this <code>Operation</code> has been closed
- * or the transaction has ended and no further messages will be exchanged
- *
+ * @throws IOException if this <code>Operation</code> has been closed or the
+ * transaction has ended and no further messages will be exchanged
* @throws IllegalArgumentException if <code>headers</code> was not created
- * by a call to <code>ServerRequestHandler.createHeaderSet()</code>
+ * by a call to <code>ServerRequestHandler.createHeaderSet()</code>
*/
public void sendHeaders(HeaderSet headers) throws IOException {
ensureOpen();
@@ -546,15 +526,14 @@ public final class ServerOperation implements Operation, BaseStream {
}
/**
- * Retrieves the response code retrieved from the server. Response codes
- * are defined in the <code>ResponseCodes</code> interface.
- *
+ * Retrieves the response code retrieved from the server. Response codes are
+ * defined in the <code>ResponseCodes</code> interface.
* @return the response code retrieved from the server
- *
* @throws IOException if an error occurred in the transport layer during
- * the transaction; if this method is called on a <code>HeaderSet</code>
- * object created by calling <code>createHeaderSet</code> in a
- * <code>ClientSession</code> object; if this is called from a server
+ * the transaction; if this method is called on a
+ * <code>HeaderSet</code> object created by calling
+ * <code>createHeaderSet</code> in a <code>ClientSession</code>
+ * object; if this is called from a server
*/
public int getResponseCode() throws IOException {
throw new IOException("Called from a server");
@@ -562,7 +541,6 @@ public final class ServerOperation implements Operation, BaseStream {
/**
* Always returns <code>null</code>
- *
* @return <code>null</code>
*/
public String getEncoding() {
@@ -573,9 +551,8 @@ public final class ServerOperation implements Operation, BaseStream {
* Returns the type of content that the resource connected to is providing.
* E.g. if the connection is via HTTP, then the value of the content-type
* header field is returned.
- *
* @return the content type of the resource that the URL references, or
- * <code>null</code> if not known
+ * <code>null</code> if not known
*/
public String getType() {
try {
@@ -587,11 +564,10 @@ public final class ServerOperation implements Operation, BaseStream {
/**
* Returns the length of the content which is being provided. E.g. if the
- * connection is via HTTP, then the value of the content-length header
- * field is returned.
- *
+ * connection is via HTTP, then the value of the content-length header field
+ * is returned.
* @return the content length of the resource that this connection's URL
- * references, or -1 if the content length is not known
+ * references, or -1 if the content length is not known
*/
public long getLength() {
try {
@@ -613,9 +589,7 @@ public final class ServerOperation implements Operation, BaseStream {
/**
* Open and return an input stream for a connection.
- *
* @return an input stream
- *
* @throws IOException if an I/O error occurs
*/
public InputStream openInputStream() throws IOException {
@@ -625,9 +599,7 @@ public final class ServerOperation implements Operation, BaseStream {
/**
* Open and return a data input stream for a connection.
- *
* @return an input stream
- *
* @throws IOException if an I/O error occurs
*/
public DataInputStream openDataInputStream() throws IOException {
@@ -636,9 +608,7 @@ public final class ServerOperation implements Operation, BaseStream {
/**
* Open and return an output stream for a connection.
- *
* @return an output stream
- *
* @throws IOException if an I/O error occurs
*/
public OutputStream openOutputStream() throws IOException {
@@ -661,9 +631,7 @@ public final class ServerOperation implements Operation, BaseStream {
/**
* Open and return a data output stream for a connection.
- *
* @return an output stream
- *
* @throws IOException if an I/O error occurs
*/
public DataOutputStream openDataOutputStream() throws IOException {
@@ -672,7 +640,6 @@ public final class ServerOperation implements Operation, BaseStream {
/**
* Closes the connection and ends the transaction
- *
* @throws IOException if the operation has already ended or is closed
*/
public void close() throws IOException {
@@ -682,7 +649,6 @@ public final class ServerOperation implements Operation, BaseStream {
/**
* Verifies that the connection is open and no exceptions should be thrown.
- *
* @throws IOException if an exception needs to be thrown
*/
public void ensureOpen() throws IOException {
@@ -695,26 +661,23 @@ public final class ServerOperation implements Operation, BaseStream {
}
/**
- * Verifies that additional information may be sent. In other words, the
+ * Verifies that additional information may be sent. In other words, the
* operation is not done.
* <P>
- * Included to implement the BaseStream interface only. It does not do
+ * Included to implement the BaseStream interface only. It does not do
* anything on the server side since the operation of the Operation object
* is not done until after the handler returns from its method.
- *
* @throws IOException if the operation is completed
*/
public void ensureNotDone() throws IOException {
}
/**
- * Called when the output or input stream is closed. It does not do
- * anything on the server side since the operation of the Operation object
- * is not done until after the handler returns from its method.
- *
+ * Called when the output or input stream is closed. It does not do anything
+ * on the server side since the operation of the Operation object is not
+ * done until after the handler returns from its method.
* @param inStream <code>true</code> if the input stream is closed;
- * <code>false</code> if the output stream is closed
- *
+ * <code>false</code> if the output stream is closed
* @throws IOException if an IO error occurs
*/
public void streamClosed(boolean inStream) throws IOException {
diff --git a/obex/javax/obex/ServerRequestHandler.java b/obex/javax/obex/ServerRequestHandler.java
index e468b83f2f62..d93e5b61ab9c 100644
--- a/obex/javax/obex/ServerRequestHandler.java
+++ b/obex/javax/obex/ServerRequestHandler.java
@@ -33,40 +33,38 @@
package javax.obex;
/**
- * The <code>ServerRequestHandler</code> class defines an event
- * listener that will respond to OBEX requests made to the server.
+ * The <code>ServerRequestHandler</code> class defines an event listener that
+ * will respond to OBEX requests made to the server.
* <P>
- * The <code>onConnect()</code>, <code>onSetPath()</code>, <code>onDelete()</code>,
- * <code>onGet()</code>,
- * and <code>onPut()</code> methods may return any response code defined
- * in the <code>ResponseCodes</code> class except for
- * <code>OBEX_HTTP_CONTINUE</code>. If <code>OBEX_HTTP_CONTINUE</code> or
- * a value not defined in the <code>ResponseCodes</code> class is returned,
- * the server implementation will send an <code>OBEX_HTTP_INTERNAL_ERROR</code>
- * response to the client.
+ * The <code>onConnect()</code>, <code>onSetPath()</code>,
+ * <code>onDelete()</code>, <code>onGet()</code>, and <code>onPut()</code>
+ * methods may return any response code defined in the
+ * <code>ResponseCodes</code> class except for <code>OBEX_HTTP_CONTINUE</code>.
+ * If <code>OBEX_HTTP_CONTINUE</code> or a value not defined in the
+ * <code>ResponseCodes</code> class is returned, the server implementation will
+ * send an <code>OBEX_HTTP_INTERNAL_ERROR</code> response to the client.
* <P>
* <STRONG>Connection ID and Target Headers</STRONG>
* <P>
* According to the IrOBEX specification, a packet may not contain a Connection
- * ID and Target header. Since the Connection ID header is managed by the
+ * ID and Target header. Since the Connection ID header is managed by the
* implementation, it will not send a Connection ID header, if a Connection ID
- * was specified, in a packet that has a Target header. In other words, if an
- * application adds a Target header to a <code>HeaderSet</code> object used
- * in an OBEX operation and a Connection ID was specified, no Connection ID
- * will be sent in the packet containing the Target header.
+ * was specified, in a packet that has a Target header. In other words, if an
+ * application adds a Target header to a <code>HeaderSet</code> object used in
+ * an OBEX operation and a Connection ID was specified, no Connection ID will be
+ * sent in the packet containing the Target header.
* <P>
* <STRONG>CREATE-EMPTY Requests</STRONG>
* <P>
* A CREATE-EMPTY request allows clients to create empty objects on the server.
- * When a CREATE-EMPTY request is received, the <code>onPut()</code> method
- * will be called by the implementation. To differentiate between a normal
- * PUT request and a CREATE-EMPTY request, an application must open the
- * <code>InputStream</code> from the <code>Operation</code> object passed
- * to the <code>onPut()</code> method. For a PUT request, the application
- * will be able to read Body data from this <code>InputStream</code>. For
- * a CREATE-EMPTY request, there will be no Body data to read. Therefore,
- * a call to <code>InputStream.read()</code> will return -1.
- *
+ * When a CREATE-EMPTY request is received, the <code>onPut()</code> method will
+ * be called by the implementation. To differentiate between a normal PUT
+ * request and a CREATE-EMPTY request, an application must open the
+ * <code>InputStream</code> from the <code>Operation</code> object passed to the
+ * <code>onPut()</code> method. For a PUT request, the application will be able
+ * to read Body data from this <code>InputStream</code>. For a CREATE-EMPTY
+ * request, there will be no Body data to read. Therefore, a call to
+ * <code>InputStream.read()</code> will return -1.
* @hide
*/
public class ServerRequestHandler {
@@ -74,8 +72,8 @@ public class ServerRequestHandler {
private long mConnectionId;
/**
- * Creates a <code>ServerRequestHandler</code>.
- */
+ * Creates a <code>ServerRequestHandler</code>.
+ */
protected ServerRequestHandler() {
/*
* A connection ID of -1 implies there is no conenction ID
@@ -85,12 +83,10 @@ public class ServerRequestHandler {
/**
* Sets the connection ID header to include in the reply packets.
- *
- * @param connectionId the connection ID to use; -1 if no connection ID should be
- * sent
- *
- * @throws IllegalArgumentException if <code>id</code> is not in the
- * range -1 to 2<sup>32</sup>-1
+ * @param connectionId the connection ID to use; -1 if no connection ID
+ * should be sent
+ * @throws IllegalArgumentException if <code>id</code> is not in the range
+ * -1 to 2<sup>32</sup>-1
*/
public void setConnectionId(final long connectionId) {
if ((connectionId < -1) || (connectionId > 0xFFFFFFFFL)) {
@@ -102,9 +98,8 @@ public class ServerRequestHandler {
/**
* Retrieves the connection ID that is being used in the present connection.
* This method will return -1 if no connection ID is being used.
- *
* @return the connection id being used or -1 if no connection ID is being
- * used
+ * used
*/
public long getConnectionId() {
return mConnectionId;
@@ -113,23 +108,21 @@ public class ServerRequestHandler {
/**
* Called when a CONNECT request is received.
* <P>
- * If this method is not implemented by the class that extends this
- * class, <code>onConnect()</code> will always return an
- * <code>OBEX_HTTP_OK</code> response code.
+ * If this method is not implemented by the class that extends this class,
+ * <code>onConnect()</code> will always return an <code>OBEX_HTTP_OK</code>
+ * response code.
* <P>
* The headers received in the request can be retrieved from the
- * <code>request</code> argument. The headers that should be sent
- * in the reply must be specified in the <code>reply</code> argument.
- *
+ * <code>request</code> argument. The headers that should be sent in the
+ * reply must be specified in the <code>reply</code> argument.
* @param request contains the headers sent by the client;
- * <code>request</code> will never be <code>null</code>
- *
+ * <code>request</code> will never be <code>null</code>
* @param reply the headers that should be sent in the reply;
- * <code>reply</code> will never be <code>null</code>
- *
- * @return a response code defined in <code>ResponseCodes</code> that will be
- * returned to the client; if an invalid response code is provided, the
- * <code>OBEX_HTTP_INTERNAL_ERROR</code> response code will be used
+ * <code>reply</code> will never be <code>null</code>
+ * @return a response code defined in <code>ResponseCodes</code> that will
+ * be returned to the client; if an invalid response code is
+ * provided, the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code
+ * will be used
*/
public int onConnect(HeaderSet request, HeaderSet reply) {
return ResponseCodes.OBEX_HTTP_OK;
@@ -139,14 +132,12 @@ public class ServerRequestHandler {
* Called when a DISCONNECT request is received.
* <P>
* The headers received in the request can be retrieved from the
- * <code>request</code> argument. The headers that should be sent
- * in the reply must be specified in the <code>reply</code> argument.
- *
+ * <code>request</code> argument. The headers that should be sent in the
+ * reply must be specified in the <code>reply</code> argument.
* @param request contains the headers sent by the client;
- * <code>request</code> will never be <code>null</code>
- *
+ * <code>request</code> will never be <code>null</code>
* @param reply the headers that should be sent in the reply;
- * <code>reply</code> will never be <code>null</code>
+ * <code>reply</code> will never be <code>null</code>
*/
public void onDisconnect(HeaderSet request, HeaderSet reply) {
}
@@ -154,32 +145,28 @@ public class ServerRequestHandler {
/**
* Called when a SETPATH request is received.
* <P>
- * If this method is not implemented by the class that extends this
- * class, <code>onSetPath()</code> will always return an
+ * If this method is not implemented by the class that extends this class,
+ * <code>onSetPath()</code> will always return an
* <code>OBEX_HTTP_NOT_IMPLEMENTED</code> response code.
* <P>
* The headers received in the request can be retrieved from the
- * <code>request</code> argument. The headers that should be sent
- * in the reply must be specified in the <code>reply</code> argument.
- *
+ * <code>request</code> argument. The headers that should be sent in the
+ * reply must be specified in the <code>reply</code> argument.
* @param request contains the headers sent by the client;
- * <code>request</code> will never be <code>null</code>
- *
+ * <code>request</code> will never be <code>null</code>
* @param reply the headers that should be sent in the reply;
- * <code>reply</code> will never be <code>null</code>
- *
+ * <code>reply</code> will never be <code>null</code>
* @param backup <code>true</code> if the client requests that the server
- * back up one directory before changing to the path described by
- * <code>name</code>; <code>false</code> to apply the request to the present
- * path
- *
+ * back up one directory before changing to the path described by
+ * <code>name</code>; <code>false</code> to apply the request to the
+ * present path
* @param create <code>true</code> if the path should be created if it does
- * not already exist; <code>false</code> if the path should not be created
- * if it does not exist and an error code should be returned
- *
- * @return a response code defined in <code>ResponseCodes</code> that will be
- * returned to the client; if an invalid response code is provided, the
- * <code>OBEX_HTTP_INTERNAL_ERROR</code> response code will be used
+ * not already exist; <code>false</code> if the path should not be
+ * created if it does not exist and an error code should be returned
+ * @return a response code defined in <code>ResponseCodes</code> that will
+ * be returned to the client; if an invalid response code is
+ * provided, the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code
+ * will be used
*/
public int onSetPath(HeaderSet request, HeaderSet reply, boolean backup, boolean create) {
@@ -189,23 +176,21 @@ public class ServerRequestHandler {
/**
* Called when a DELETE request is received.
* <P>
- * If this method is not implemented by the class that extends this
- * class, <code>onDelete()</code> will always return an
+ * If this method is not implemented by the class that extends this class,
+ * <code>onDelete()</code> will always return an
* <code>OBEX_HTTP_NOT_IMPLEMENTED</code> response code.
* <P>
* The headers received in the request can be retrieved from the
- * <code>request</code> argument. The headers that should be sent
- * in the reply must be specified in the <code>reply</code> argument.
- *
+ * <code>request</code> argument. The headers that should be sent in the
+ * reply must be specified in the <code>reply</code> argument.
* @param request contains the headers sent by the client;
- * <code>request</code> will never be <code>null</code>
- *
+ * <code>request</code> will never be <code>null</code>
* @param reply the headers that should be sent in the reply;
- * <code>reply</code> will never be <code>null</code>
- *
- * @return a response code defined in <code>ResponseCodes</code> that will be
- * returned to the client; if an invalid response code is provided, the
- * <code>OBEX_HTTP_INTERNAL_ERROR</code> response code will be used
+ * <code>reply</code> will never be <code>null</code>
+ * @return a response code defined in <code>ResponseCodes</code> that will
+ * be returned to the client; if an invalid response code is
+ * provided, the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code
+ * will be used
*/
public int onDelete(HeaderSet request, HeaderSet reply) {
return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
@@ -214,20 +199,19 @@ public class ServerRequestHandler {
/**
* Called when a PUT request is received.
* <P>
- * If this method is not implemented by the class that extends this
- * class, <code>onPut()</code> will always return an
+ * If this method is not implemented by the class that extends this class,
+ * <code>onPut()</code> will always return an
* <code>OBEX_HTTP_NOT_IMPLEMENTED</code> response code.
* <P>
* If an ABORT request is received during the processing of a PUT request,
* <code>op</code> will be closed by the implementation.
- *
* @param operation contains the headers sent by the client and allows new
- * headers to be sent in the reply; <code>op</code> will never be
- * <code>null</code>
- *
- * @return a response code defined in <code>ResponseCodes</code> that will be
- * returned to the client; if an invalid response code is provided, the
- * <code>OBEX_HTTP_INTERNAL_ERROR</code> response code will be used
+ * headers to be sent in the reply; <code>op</code> will never be
+ * <code>null</code>
+ * @return a response code defined in <code>ResponseCodes</code> that will
+ * be returned to the client; if an invalid response code is
+ * provided, the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code
+ * will be used
*/
public int onPut(Operation operation) {
return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
@@ -236,20 +220,19 @@ public class ServerRequestHandler {
/**
* Called when a GET request is received.
* <P>
- * If this method is not implemented by the class that extends this
- * class, <code>onGet()</code> will always return an
+ * If this method is not implemented by the class that extends this class,
+ * <code>onGet()</code> will always return an
* <code>OBEX_HTTP_NOT_IMPLEMENTED</code> response code.
* <P>
* If an ABORT request is received during the processing of a GET request,
* <code>op</code> will be closed by the implementation.
- *
* @param operation contains the headers sent by the client and allows new
- * headers to be sent in the reply; <code>op</code> will never be
- * <code>null</code>
- *
- * @return a response code defined in <code>ResponseCodes</code> that will be
- * returned to the client; if an invalid response code is provided, the
- * <code>OBEX_HTTP_INTERNAL_ERROR</code> response code will be used
+ * headers to be sent in the reply; <code>op</code> will never be
+ * <code>null</code>
+ * @return a response code defined in <code>ResponseCodes</code> that will
+ * be returned to the client; if an invalid response code is
+ * provided, the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code
+ * will be used
*/
public int onGet(Operation operation) {
return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
@@ -262,9 +245,8 @@ public class ServerRequestHandler {
* <P>
* If this method is not implemented by the class that extends this class,
* this method will do nothing.
- *
* @param userName the user name returned in the authentication response;
- * <code>null</code> if no user name was provided in the response
+ * <code>null</code> if no user name was provided in the response
*/
public void onAuthenticationFailure(byte[] userName) {
}
@@ -274,7 +256,6 @@ public class ServerRequestHandler {
* <P>
* If this method is not implemented by the class that extends this class,
* this method will do nothing.
- *
*/
public void updateStatus(String message) {
}
@@ -284,7 +265,6 @@ public class ServerRequestHandler {
* <P>
* If this method is not implemented by the class that extends this class,
* this method will do nothing.
- *
*/
public void onClose() {
}
diff --git a/obex/javax/obex/ServerSession.java b/obex/javax/obex/ServerSession.java
index 3a0e81501acf..423d5a757b89 100644
--- a/obex/javax/obex/ServerSession.java
+++ b/obex/javax/obex/ServerSession.java
@@ -40,7 +40,6 @@ import java.io.OutputStream;
/**
* This class in an implementation of the OBEX ServerSession.
- *
* @hide
*/
public final class ServerSession extends ObexSession implements Runnable {
@@ -63,19 +62,11 @@ public final class ServerSession extends ObexSession implements Runnable {
/**
* Creates new ServerSession.
- *
- * @param trans
- * the connection to the client
- *
- * @param handler
- * the event listener that will process requests
- *
- * @param auth
- * the authenticator to use with this connection
- *
- * @throws IOException
- * if an error occurred while opening the input and output
- * streams
+ * @param trans the connection to the client
+ * @param handler the event listener that will process requests
+ * @param auth the authenticator to use with this connection
+ * @throws IOException if an error occurred while opening the input and
+ * output streams
*/
public ServerSession(ObexTransport trans, ServerRequestHandler handler, Authenticator auth)
throws IOException {
@@ -163,12 +154,8 @@ public final class ServerSession extends ObexSession implements Runnable {
* <code>ServerOperation</code> object will always reply with a
* OBEX_HTTP_CONTINUE reply. It will only reply if further information is
* needed.
- *
- * @param type
- * the type of request received; either 0x02 or 0x82
- *
- * @throws IOException
- * if an error occurred at the transport layer
+ * @param type the type of request received; either 0x02 or 0x82
+ * @throws IOException if an error occurred at the transport layer
*/
private void handlePutRequest(int type) throws IOException {
ServerOperation op = new ServerOperation(this, mInput, type, mMaxPacketLength, mListener);
@@ -191,7 +178,14 @@ public final class ServerSession extends ObexSession implements Runnable {
op.sendReply(response);
}
} catch (Exception e) {
- sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
+ /*To fix bugs in aborted cases,
+ *(client abort file transfer prior to the last packet which has the end of body header,
+ *internal error should not be sent because server has already replied with
+ *OK response in "sendReply")
+ */
+ if (!op.isAborted) {
+ sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
+ }
}
}
@@ -205,12 +199,8 @@ public final class ServerSession extends ObexSession implements Runnable {
* <code>ServerOperation</code> object will always reply with a
* OBEX_HTTP_CONTINUE reply. It will only reply if further information is
* needed.
- *
- * @param type
- * the type of request received; either 0x03 or 0x83
- *
- * @throws IOException
- * if an error occurred at the transport layer
+ * @param type the type of request received; either 0x03 or 0x83
+ * @throws IOException if an error occurred at the transport layer
*/
private void handleGetRequest(int type) throws IOException {
ServerOperation op = new ServerOperation(this, mInput, type, mMaxPacketLength, mListener);
@@ -227,15 +217,9 @@ public final class ServerSession extends ObexSession implements Runnable {
/**
* Send standard response.
- *
- * @param code
- * the response code to send
- *
- * @param header
- * the headers to include in the response
- *
- * @throws IOException
- * if an IO error occurs
+ * @param code the response code to send
+ * @param header the headers to include in the response
+ * @throws IOException if an IO error occurs
*/
public void sendResponse(int code, byte[] header) throws IOException {
int totalLength = 3;
@@ -265,9 +249,7 @@ public final class ServerSession extends ObexSession implements Runnable {
* <code>ServerRequestHandler</code> object. After the handler processes the
* request, this method will create a reply message to send to the server
* with the response code provided.
- *
- * @throws IOException
- * if an error occurred at the transport layer
+ * @throws IOException if an error occurred at the transport layer
*/
private void handleSetPathRequest() throws IOException {
int length;
@@ -393,9 +375,7 @@ public final class ServerSession extends ObexSession implements Runnable {
* will create a <code>HeaderSet</code> object to pass to the
* <code>ServerRequestHandler</code> object. After the handler processes the
* request, this method will create a reply message to send to the server.
- *
- * @throws IOException
- * if an error occurred at the transport layer
+ * @throws IOException if an error occurred at the transport layer
*/
private void handleDisconnectRequest() throws IOException {
int length;
@@ -500,9 +480,7 @@ public final class ServerSession extends ObexSession implements Runnable {
* <code>ServerRequestHandler</code> object. After the handler processes the
* request, this method will create a reply message to send to the server
* with the response code provided.
- *
- * @throws IOException
- * if an error occurred at the transport layer
+ * @throws IOException if an error occurred at the transport layer
*/
private void handleConnectRequest() throws IOException {
int packetLength;
@@ -660,10 +638,7 @@ public final class ServerSession extends ObexSession implements Runnable {
/**
* Verifies that the response code is valid. If it is not valid, it will
* return the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code.
- *
- * @param code
- * the response code to check
- *
+ * @param code the response code to check
* @return the valid response code or <code>OBEX_HTTP_INTERNAL_ERROR</code>
* if <code>code</code> is not valid
*/
diff --git a/obex/javax/obex/SessionNotifier.java b/obex/javax/obex/SessionNotifier.java
index 36e0ebf14226..9836dd60f2c6 100644
--- a/obex/javax/obex/SessionNotifier.java
+++ b/obex/javax/obex/SessionNotifier.java
@@ -36,73 +36,53 @@ import java.io.IOException;
/**
* The <code>SessionNotifier</code> interface defines a connection notifier for
- * server-side OBEX connections. When a <code>SessionNotifier</code> is
- * created and calls <code>acceptAndOpen()</code>, it will begin listening for
- * clients to create a connection at the transport layer. When the transport
- * layer connection is received, the <code>acceptAndOpen()</code> method will
- * return a <code>javax.microedition.io.Connection</code> that is the
- * connection to the client. The <code>acceptAndOpen()</code> method also takes a
+ * server-side OBEX connections. When a <code>SessionNotifier</code> is created
+ * and calls <code>acceptAndOpen()</code>, it will begin listening for clients
+ * to create a connection at the transport layer. When the transport layer
+ * connection is received, the <code>acceptAndOpen()</code> method will return a
+ * <code>javax.microedition.io.Connection</code> that is the connection to the
+ * client. The <code>acceptAndOpen()</code> method also takes a
* <code>ServerRequestHandler</code> argument that will process the requests
* from the client that connects to the server.
- *
* @hide
*/
public interface SessionNotifier {
/**
* Waits for a transport layer connection to be established and specifies
- * the handler to handle the requests from the client. No authenticator
- * is associated with this connection, therefore, it is implementation
+ * the handler to handle the requests from the client. No authenticator is
+ * associated with this connection, therefore, it is implementation
* dependent as to how an authentication challenge and authentication
* response header will be received and processed.
* <P>
- * <H4>Additional Note for OBEX over Bluetooth</H4>
- * If this method is called on a <code>SessionNotifier</code> object that
- * does not have a <code>ServiceRecord</code> in the SDDB, the
- * <code>ServiceRecord</code> for this object will be added to the SDDB.
- * This method requests the BCC to put the
- * local device in connectible mode so that it will respond to
+ * <H4>Additional Note for OBEX over Bluetooth</H4> If this method is called
+ * on a <code>SessionNotifier</code> object that does not have a
+ * <code>ServiceRecord</code> in the SDDB, the <code>ServiceRecord</code>
+ * for this object will be added to the SDDB. This method requests the BCC
+ * to put the local device in connectable mode so that it will respond to
* connection attempts by clients.
* <P>
- * The following checks are done to verify that the service record
- * provided is valid. If any of these checks fail, then a
+ * The following checks are done to verify that the service record provided
+ * is valid. If any of these checks fail, then a
* <code>ServiceRegistrationException</code> is thrown.
* <UL>
- * <LI>ServiceClassIDList and ProtocolDescriptorList, the mandatory
- * service attributes for a <code>btgoep</code> service record, must be
- * present in the <code>ServiceRecord</code> associated with this notifier.
+ * <LI>ServiceClassIDList and ProtocolDescriptorList, the mandatory service
+ * attributes for a <code>btgoep</code> service record, must be present in
+ * the <code>ServiceRecord</code> associated with this notifier.
* <LI>L2CAP, RFCOMM and OBEX must all be in the ProtocolDescriptorList
- * <LI>The <code>ServiceRecord</code> associated with this notifier must
- * not have changed the RFCOMM server channel number
+ * <LI>The <code>ServiceRecord</code> associated with this notifier must not
+ * have changed the RFCOMM server channel number
* </UL>
* <P>
* This method will not ensure that <code>ServiceRecord</code> associated
- * with this notifier is a completely
- * valid service record. It is the responsibility of the application to
- * ensure that the service record follows all of the applicable
- * syntactic and semantic rules for service record correctness.
- *
+ * with this notifier is a completely valid service record. It is the
+ * responsibility of the application to ensure that the service record
+ * follows all of the applicable syntactic and semantic rules for service
+ * record correctness.
* @param handler the request handler that will respond to OBEX requests
- *
* @return the connection to the client
- *
* @throws IOException if an error occurs in the transport layer
- *
- * @throws NullPointerException if <code>handler</code> is
- * <code>null</code>
- *
- * @throws ServiceRegistrationException if the structure of the
- * associated service record is invalid or if the service record
- * could not be added successfully to the local SDDB. The
- * structure of service record is invalid if the service
- * record is missing any mandatory service attributes, or has
- * changed any of the values described above which are fixed and
- * cannot be changed. Failures to add the record to the SDDB could
- * be due to insufficient disk space, database locks, etc.
- *
- * @throws BluetoothStateException if the server device could
- * not be placed in connectible mode because the device user has
- * configured the device to be non-connectible
+ * @throws NullPointerException if <code>handler</code> is <code>null</code>
*/
ObexSession acceptAndOpen(ServerRequestHandler handler) throws IOException;
@@ -112,56 +92,37 @@ public interface SessionNotifier {
* <code>Authenticator</code> to use to respond to authentication challenge
* and authentication response headers.
* <P>
- * <H4>Additional Note for OBEX over Bluetooth</H4>
- * If this method is called on a <code>SessionNotifier</code> object that
- * does not have a <code>ServiceRecord</code> in the SDDB, the
- * <code>ServiceRecord</code> for this object will be added to the SDDB.
- * This method requests the BCC to put the
- * local device in connectible mode so that it will respond to
+ * <H4>Additional Note for OBEX over Bluetooth</H4> If this method is called
+ * on a <code>SessionNotifier</code> object that does not have a
+ * <code>ServiceRecord</code> in the SDDB, the <code>ServiceRecord</code>
+ * for this object will be added to the SDDB. This method requests the BCC
+ * to put the local device in connectable mode so that it will respond to
* connection attempts by clients.
* <P>
- * The following checks are done to verify that the service record
- * provided is valid. If any of these checks fail, then a
+ * The following checks are done to verify that the service record provided
+ * is valid. If any of these checks fail, then a
* <code>ServiceRegistrationException</code> is thrown.
* <UL>
- * <LI>ServiceClassIDList and ProtocolDescriptorList, the mandatory
- * service attributes for a <code>btgoep</code> service record, must be
- * present in the <code>ServiceRecord</code> associated with this notifier.
+ * <LI>ServiceClassIDList and ProtocolDescriptorList, the mandatory service
+ * attributes for a <code>btgoep</code> service record, must be present in
+ * the <code>ServiceRecord</code> associated with this notifier.
* <LI>L2CAP, RFCOMM and OBEX must all be in the ProtocolDescriptorList
- * <LI>The <code>ServiceRecord</code> associated with this notifier must
- * not have changed the RFCOMM server channel number
+ * <LI>The <code>ServiceRecord</code> associated with this notifier must not
+ * have changed the RFCOMM server channel number
* </UL>
* <P>
* This method will not ensure that <code>ServiceRecord</code> associated
- * with this notifier is a completely
- * valid service record. It is the responsibility of the application to
- * ensure that the service record follows all of the applicable
- * syntactic and semantic rules for service record correctness.
- *
+ * with this notifier is a completely valid service record. It is the
+ * responsibility of the application to ensure that the service record
+ * follows all of the applicable syntactic and semantic rules for service
+ * record correctness.
* @param handler the request handler that will respond to OBEX requests
- *
* @param auth the <code>Authenticator</code> to use with this connection;
- * if <code>null</code> then no <code>Authenticator</code> will be used
- *
+ * if <code>null</code> then no <code>Authenticator</code> will be
+ * used
* @return the connection to the client
- *
* @throws IOException if an error occurs in the transport layer
- *
- * @throws NullPointerException if <code>handler</code> is
- * <code>null</code>
- *
- * @throws ServiceRegistrationException if the structure of the
- * associated service record is invalid or if the service record
- * could not be added successfully to the local SDDB. The
- * structure of service record is invalid if the service
- * record is missing any mandatory service attributes, or has
- * changed any of the values described above which are fixed and
- * cannot be changed. Failures to add the record to the SDDB could
- * be due to insufficient disk space, database locks, etc.
- *
- * @throws BluetoothStateException if the server device could
- * not be placed in connectible mode because the device user has
- * configured the device to be non-connectible
+ * @throws NullPointerException if <code>handler</code> is <code>null</code>
*/
ObexSession acceptAndOpen(ServerRequestHandler handler, Authenticator auth) throws IOException;
}
diff --git a/opengl/libagl/Android.mk b/opengl/libagl/Android.mk
index d1ed82e5eb00..2522656f2213 100644
--- a/opengl/libagl/Android.mk
+++ b/opengl/libagl/Android.mk
@@ -38,7 +38,6 @@ endif
ifeq ($(LIBAGL_USE_GRALLOC_COPYBITS),1)
LOCAL_CFLAGS += -DLIBAGL_USE_GRALLOC_COPYBITS
LOCAL_SRC_FILES += copybit.cpp
- LOCAL_C_INCLUDES += hardware/libhardware/modules/gralloc
endif
LOCAL_CFLAGS += -DLOG_TAG=\"libagl\"
diff --git a/opengl/libagl/copybit.cpp b/opengl/libagl/copybit.cpp
index 3331026f66b9..3c5bcdf39f66 100644
--- a/opengl/libagl/copybit.cpp
+++ b/opengl/libagl/copybit.cpp
@@ -295,6 +295,9 @@ static bool copybit(GLint x, GLint y,
clipRectRegion it(c);
status_t err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it);
+ if (err != NO_ERROR) {
+ c->textures.tmu[0].texture->try_copybit = false;
+ }
return err == NO_ERROR ? true : false;
}
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index 6f6656a4d29b..7afcae7504f8 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -48,10 +48,6 @@
#include "texture.h"
#include "matrix.h"
-#ifdef LIBAGL_USE_GRALLOC_COPYBITS
-#include "gralloc_priv.h"
-#endif // LIBAGL_USE_GRALLOC_COPYBITS
-
#undef NELEM
#define NELEM(x) (sizeof(x)/sizeof(*(x)))
@@ -622,11 +618,10 @@ EGLBoolean egl_window_surface_v2_t::bindDrawSurface(ogles_context_t* gl)
#ifdef LIBAGL_USE_GRALLOC_COPYBITS
gl->copybits.drawSurfaceBuffer = 0;
- if (supportedCopybitsDestinationFormat(buffer.format)) {
- buffer_handle_t handle = this->buffer->handle;
- if (handle != NULL) {
- private_handle_t* hand = private_handle_t::dynamicCast(handle);
- if (hand != NULL && hand->usesPhysicallyContiguousMemory()) {
+ if (gl->copybits.blitEngine != NULL) {
+ if (supportedCopybitsDestinationFormat(buffer.format)) {
+ buffer_handle_t handle = this->buffer->handle;
+ if (handle != NULL) {
gl->copybits.drawSurfaceBuffer = handle;
}
}
diff --git a/opengl/libagl/primitives.cpp b/opengl/libagl/primitives.cpp
index f164c02eea34..769ec404a8a4 100644
--- a/opengl/libagl/primitives.cpp
+++ b/opengl/libagl/primitives.cpp
@@ -369,7 +369,7 @@ void compute_iterators_t::iterators0032(int32_t* it,
int32_t c0, int32_t c1, int32_t c2) const
{
int64_t it64[3];
- iterators0032(it, c0, c1, c2);
+ iterators0032(it64, c0, c1, c2);
it[0] = it64[0];
it[1] = it64[1];
it[2] = it64[2];
diff --git a/opengl/libagl/texture.cpp b/opengl/libagl/texture.cpp
index d767c31ac88d..4d3c2f4381dd 100644
--- a/opengl/libagl/texture.cpp
+++ b/opengl/libagl/texture.cpp
@@ -27,7 +27,6 @@
#ifdef LIBAGL_USE_GRALLOC_COPYBITS
#include "copybit.h"
-#include "gralloc_priv.h"
#endif // LIBAGL_USE_GRALLOC_COPYBITS
namespace android {
@@ -1540,20 +1539,9 @@ void glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image)
sp<EGLTextureObject> tex = getAndBindActiveTextureObject(c);
tex->setImage(native_buffer);
- /*
- * Here an implementation can retrieve the buffer_handle_t of this buffer
- * which gives it access to an arbitrary-defined kernel resource
- * (or anything else for that matter).
- * There needs to be an intimate knowledge between GLES and buffer_handle_t,
- * so make sure to validate the handle before using it.
- * Typically, buffer_handle_t comes from the gralloc HAL which is provided
- * by the implementor of GLES.
- *
- */
#ifdef LIBAGL_USE_GRALLOC_COPYBITS
tex->try_copybit = false;
- private_handle_t* hnd = private_handle_t::dynamicCast(native_buffer->handle);
- if (hnd && hnd->usesPhysicallyContiguousMemory()) {
+ if (c->copybits.blitEngine != NULL) {
tex->try_copybit = true;
}
#endif // LIBAGL_USE_GRALLOC_COPYBITS
diff --git a/packages/SettingsProvider/etc/bookmarks.xml b/packages/SettingsProvider/etc/bookmarks.xml
index 33b4c977a363..734e0cd90b00 100644
--- a/packages/SettingsProvider/etc/bookmarks.xml
+++ b/packages/SettingsProvider/etc/bookmarks.xml
@@ -55,6 +55,6 @@
shortcut="s" />
<bookmark
package="com.google.android.youtube"
- class="com.google.android.youtube.HomePage"
+ class="com.google.android.youtube.HomeActivity"
shortcut="y" />
</bookmarks>
diff --git a/packages/TtsService/jni/android_tts_SynthProxy.cpp b/packages/TtsService/jni/android_tts_SynthProxy.cpp
index 424748364fda..82067be1d8f1 100644
--- a/packages/TtsService/jni/android_tts_SynthProxy.cpp
+++ b/packages/TtsService/jni/android_tts_SynthProxy.cpp
@@ -59,6 +59,9 @@ struct afterSynthData_t {
// ----------------------------------------------------------------------------
static fields_t javaTTSFields;
+// TODO move to synth member once we have multiple simultaneous engines running
+static Mutex engineMutex;
+
// ----------------------------------------------------------------------------
class SynthProxyJniStorage {
public :
@@ -84,6 +87,7 @@ class SynthProxyJniStorage {
mNbChannels = DEFAULT_TTS_NB_CHANNELS;
mBufferSize = DEFAULT_TTS_BUFFERSIZE;
mBuffer = new int8_t[mBufferSize];
+ memset(mBuffer, 0, mBufferSize);
}
~SynthProxyJniStorage() {
@@ -194,6 +198,7 @@ static tts_callback_status ttsSynthDoneCB(void *& userdata, uint32_t rate,
prepAudioTrack(pJniData, pForAfter->streamType, rate, format, channel);
if (pJniData->mAudioOut) {
pJniData->mAudioOut->write(wav, bufferSize);
+ memset(wav, 0, bufferSize);
//LOGV("AudioTrack wrote: %d bytes", bufferSize);
} else {
LOGE("Can't play, null audiotrack");
@@ -208,6 +213,7 @@ static tts_callback_status ttsSynthDoneCB(void *& userdata, uint32_t rate,
}
if (bufferSize > 0){
fwrite(wav, 1, bufferSize, pForAfter->outputFile);
+ memset(wav, 0, bufferSize);
}
}
// Future update:
@@ -283,6 +289,7 @@ android_tts_SynthProxy_native_finalize(JNIEnv *env, jobject thiz, jint jniData)
{
if (jniData) {
SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+ env->DeleteGlobalRef(pSynthData->tts_ref);
delete pSynthData;
}
}
@@ -326,6 +333,8 @@ android_tts_SynthProxy_setLanguage(JNIEnv *env, jobject thiz, jint jniData,
return result;
}
+ Mutex::Autolock l(engineMutex);
+
SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
const char *langNativeString = env->GetStringUTFChars(language, 0);
const char *countryNativeString = env->GetStringUTFChars(country, 0);
@@ -385,6 +394,8 @@ android_tts_SynthProxy_setSpeechRate(JNIEnv *env, jobject thiz, jint jniData,
char buffer [bufSize];
sprintf(buffer, "%d", speechRate);
+ Mutex::Autolock l(engineMutex);
+
SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
LOGI("setting speech rate to %d", speechRate);
@@ -407,6 +418,8 @@ android_tts_SynthProxy_setPitch(JNIEnv *env, jobject thiz, jint jniData,
return result;
}
+ Mutex::Autolock l(engineMutex);
+
int bufSize = 10;
char buffer [bufSize];
sprintf(buffer, "%d", pitch);
@@ -439,6 +452,8 @@ android_tts_SynthProxy_synthesizeToFile(JNIEnv *env, jobject thiz, jint jniData,
return result;
}
+ Mutex::Autolock l(engineMutex);
+
// Retrieve audio parameters before writing the file header
AudioSystem::audio_format encoding = DEFAULT_TTS_FORMAT;
uint32_t rate = DEFAULT_TTS_RATE;
@@ -473,6 +488,7 @@ android_tts_SynthProxy_synthesizeToFile(JNIEnv *env, jobject thiz, jint jniData,
unsigned int unique_identifier;
+ memset(pSynthData->mBuffer, 0, pSynthData->mBufferSize);
result = pSynthData->mNativeSynthInterface->synthesizeText(textNativeString,
pSynthData->mBuffer, pSynthData->mBufferSize, (void *)pForAfter);
@@ -540,10 +556,11 @@ android_tts_SynthProxy_speak(JNIEnv *env, jobject thiz, jint jniData,
return result;
}
+ Mutex::Autolock l(engineMutex);
+
SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
if (pSynthData->mAudioOut) {
- pSynthData->mAudioOut->stop();
pSynthData->mAudioOut->start();
}
@@ -554,6 +571,7 @@ android_tts_SynthProxy_speak(JNIEnv *env, jobject thiz, jint jniData,
if (pSynthData->mNativeSynthInterface) {
const char *textNativeString = env->GetStringUTFChars(textJavaString, 0);
+ memset(pSynthData->mBuffer, 0, pSynthData->mBufferSize);
result = pSynthData->mNativeSynthInterface->synthesizeText(textNativeString,
pSynthData->mBuffer, pSynthData->mBufferSize, (void *)pForAfter);
env->ReleaseStringUTFChars(textJavaString, textNativeString);
@@ -575,12 +593,12 @@ android_tts_SynthProxy_stop(JNIEnv *env, jobject thiz, jint jniData)
SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
- if (pSynthData->mNativeSynthInterface) {
- result = pSynthData->mNativeSynthInterface->stop();
- }
if (pSynthData->mAudioOut) {
pSynthData->mAudioOut->stop();
}
+ if (pSynthData->mNativeSynthInterface) {
+ result = pSynthData->mNativeSynthInterface->stop();
+ }
return result;
}
@@ -594,6 +612,8 @@ android_tts_SynthProxy_shutdown(JNIEnv *env, jobject thiz, jint jniData)
return;
}
+ Mutex::Autolock l(engineMutex);
+
SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
if (pSynthData->mNativeSynthInterface) {
pSynthData->mNativeSynthInterface->shutdown();
@@ -602,24 +622,6 @@ android_tts_SynthProxy_shutdown(JNIEnv *env, jobject thiz, jint jniData)
}
-// TODO add buffer format
-static void
-android_tts_SynthProxy_playAudioBuffer(JNIEnv *env, jobject thiz, jint jniData,
- int bufferPointer, int bufferSize)
-{
-LOGI("android_tts_SynthProxy_playAudioBuffer");
- if (jniData == 0) {
- LOGE("android_tts_SynthProxy_playAudioBuffer(): invalid JNI data");
- return;
- }
-
- SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
- short* wav = (short*) bufferPointer;
- pSynthData->mAudioOut->write(wav, bufferSize);
- //LOGI("AudioTrack wrote: %d bytes", bufferSize);
-}
-
-
static jobjectArray
android_tts_SynthProxy_getLanguage(JNIEnv *env, jobject thiz, jint jniData)
{
@@ -705,10 +707,6 @@ static JNINativeMethod gMethods[] = {
"(II)I",
(void*)android_tts_SynthProxy_setPitch
},
- { "native_playAudioBuffer",
- "(III)V",
- (void*)android_tts_SynthProxy_playAudioBuffer
- },
{ "native_getLanguage",
"(I)[Ljava/lang/String;",
(void*)android_tts_SynthProxy_getLanguage
diff --git a/packages/TtsService/src/android/tts/SynthProxy.java b/packages/TtsService/src/android/tts/SynthProxy.java
index 41ff92a1b9bd..a0814aa912a2 100755
--- a/packages/TtsService/src/android/tts/SynthProxy.java
+++ b/packages/TtsService/src/android/tts/SynthProxy.java
@@ -109,13 +109,6 @@ public class SynthProxy {
}
/**
- * Plays the given audio buffer.
- */
- public void playAudioBuffer(int bufferPointer, int bufferSize) {
- native_playAudioBuffer(mJniData, bufferPointer, bufferSize);
- }
-
- /**
* Returns the currently set language, country and variant information.
*/
public String[] getLanguage() {
@@ -180,9 +173,6 @@ public class SynthProxy {
private native final int native_setPitch(int jniData, int speechRate);
- // TODO add buffer format
- private native final void native_playAudioBuffer(int jniData, int bufferPointer, int bufferSize);
-
private native final String[] native_getLanguage(int jniData);
private native final int native_getRate(int jniData);
diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java
index 4d2518327d2d..cfefcb79c492 100755
--- a/packages/TtsService/src/android/tts/TtsService.java
+++ b/packages/TtsService/src/android/tts/TtsService.java
@@ -130,6 +130,8 @@ public class TtsService extends Service implements OnCompletionListener {
private HashMap<String, SoundResource> mUtterances;
private MediaPlayer mPlayer;
private SpeechItem mCurrentSpeechItem;
+ private HashMap<SpeechItem, Boolean> mKillList; // Used to ensure that in-flight synth calls
+ // are killed when stop is used.
private TtsService mSelf;
private ContentResolver mResolver;
@@ -137,16 +139,18 @@ public class TtsService extends Service implements OnCompletionListener {
private final ReentrantLock speechQueueLock = new ReentrantLock();
private final ReentrantLock synthesizerLock = new ReentrantLock();
- private SynthProxy nativeSynth;
+ private static SynthProxy sNativeSynth = null;
@Override
public void onCreate() {
super.onCreate();
- Log.i("TTS", "TTS starting");
+ Log.i("TtsService", "TtsService.onCreate()");
mResolver = getContentResolver();
String soLibPath = "/system/lib/libttspico.so";
- nativeSynth = new SynthProxy(soLibPath);
+ if (sNativeSynth == null) {
+ sNativeSynth = new SynthProxy(soLibPath);
+ }
mSelf = this;
mIsSpeaking = false;
@@ -158,6 +162,7 @@ public class TtsService extends Service implements OnCompletionListener {
mSpeechQueue = new ArrayList<SpeechItem>();
mPlayer = null;
mCurrentSpeechItem = null;
+ mKillList = new HashMap<SpeechItem, Boolean>();
setDefaultSettings();
}
@@ -168,7 +173,7 @@ public class TtsService extends Service implements OnCompletionListener {
// Don't hog the media player
cleanUpPlayer();
- nativeSynth.shutdown();
+ sNativeSynth.shutdown();
// Unregister all callbacks.
mCallbacks.kill();
@@ -236,36 +241,36 @@ public class TtsService extends Service implements OnCompletionListener {
private int setSpeechRate(String callingApp, int rate) {
if (isDefaultEnforced()) {
- return nativeSynth.setSpeechRate(getDefaultRate());
+ return sNativeSynth.setSpeechRate(getDefaultRate());
} else {
- return nativeSynth.setSpeechRate(rate);
+ return sNativeSynth.setSpeechRate(rate);
}
}
private int setPitch(String callingApp, int pitch) {
- return nativeSynth.setPitch(pitch);
+ return sNativeSynth.setPitch(pitch);
}
private int isLanguageAvailable(String lang, String country, String variant) {
- //Log.v("TTS", "TtsService.isLanguageAvailable(" + lang + ", " + country + ", " +variant+")");
- return nativeSynth.isLanguageAvailable(lang, country, variant);
+ //Log.v("TtsService", "TtsService.isLanguageAvailable(" + lang + ", " + country + ", " +variant+")");
+ return sNativeSynth.isLanguageAvailable(lang, country, variant);
}
private String[] getLanguage() {
- return nativeSynth.getLanguage();
+ return sNativeSynth.getLanguage();
}
private int setLanguage(String callingApp, String lang, String country, String variant) {
- //Log.v("TTS", "TtsService.setLanguage(" + lang + ", " + country + ", " + variant + ")");
+ Log.v("TtsService", "TtsService.setLanguage(" + lang + ", " + country + ", " + variant + ")");
if (isDefaultEnforced()) {
- return nativeSynth.setLanguage(getDefaultLanguage(), getDefaultCountry(),
+ return sNativeSynth.setLanguage(getDefaultLanguage(), getDefaultCountry(),
getDefaultLocVariant());
} else {
- return nativeSynth.setLanguage(lang, country, variant);
+ return sNativeSynth.setLanguage(lang, country, variant);
}
}
@@ -337,9 +342,11 @@ public class TtsService extends Service implements OnCompletionListener {
* engines.
*/
private int speak(String callingApp, String text, int queueMode, ArrayList<String> params) {
- Log.i("TTS service received", text);
+ Log.v("TtsService", "TTS service received " + text);
if (queueMode == TextToSpeech.TTS_QUEUE_FLUSH) {
stop(callingApp);
+ } else if (queueMode == 2) {
+ stopAll(callingApp);
}
mSpeechQueue.add(new SpeechItem(callingApp, text, params, SpeechItem.TEXT));
if (!mIsSpeaking) {
@@ -364,6 +371,8 @@ public class TtsService extends Service implements OnCompletionListener {
ArrayList<String> params) {
if (queueMode == TextToSpeech.TTS_QUEUE_FLUSH) {
stop(callingApp);
+ } else if (queueMode == 2) {
+ stopAll(callingApp);
}
mSpeechQueue.add(new SpeechItem(callingApp, earcon, params, SpeechItem.EARCON));
if (!mIsSpeaking) {
@@ -373,7 +382,7 @@ public class TtsService extends Service implements OnCompletionListener {
}
/**
- * Stops all speech output and removes any utterances still in the queue.
+ * Stops all speech output and removes any utterances still in the queue for the calling app.
*/
private int stop(String callingApp) {
int result = TextToSpeech.TTS_ERROR;
@@ -383,31 +392,86 @@ public class TtsService extends Service implements OnCompletionListener {
// something has gone very wrong with processSpeechQueue.
speechQueueAvailable = speechQueueLock.tryLock(1000, TimeUnit.MILLISECONDS);
if (speechQueueAvailable) {
- Log.i("TTS", "Stopping");
+ Log.i("TtsService", "Stopping");
for (int i = mSpeechQueue.size() - 1; i > -1; i--){
if (mSpeechQueue.get(i).mCallingApp.equals(callingApp)){
mSpeechQueue.remove(i);
}
}
+ if ((mCurrentSpeechItem != null) &&
+ mCurrentSpeechItem.mCallingApp.equals(callingApp)) {
+ result = sNativeSynth.stop();
+ mKillList.put(mCurrentSpeechItem, true);
+ if (mPlayer != null) {
+ try {
+ mPlayer.stop();
+ } catch (IllegalStateException e) {
+ // Do nothing, the player is already stopped.
+ }
+ }
+ mIsSpeaking = false;
+ mCurrentSpeechItem = null;
+ } else {
+ result = TextToSpeech.TTS_SUCCESS;
+ }
+ Log.i("TtsService", "Stopped");
+ }
+ } catch (InterruptedException e) {
+ Log.e("TtsService", "TTS stop: tryLock interrupted");
+ e.printStackTrace();
+ } finally {
+ // This check is needed because finally will always run; even if the
+ // method returns somewhere in the try block.
+ if (speechQueueAvailable) {
+ speechQueueLock.unlock();
+ }
+ return result;
+ }
+ }
- result = nativeSynth.stop();
- mIsSpeaking = false;
- if (mPlayer != null) {
- try {
- mPlayer.stop();
- } catch (IllegalStateException e) {
- // Do nothing, the player is already stopped.
+
+
+ /**
+ * Stops all speech output and removes any utterances still in the queue globally.
+ */
+ private int stopAll(String callingApp) {
+ int result = TextToSpeech.TTS_ERROR;
+ boolean speechQueueAvailable = false;
+ try{
+ // If the queue is locked for more than 1 second,
+ // something has gone very wrong with processSpeechQueue.
+ speechQueueAvailable = speechQueueLock.tryLock(1000, TimeUnit.MILLISECONDS);
+ if (speechQueueAvailable) {
+ for (int i = mSpeechQueue.size() - 1; i > -1; i--){
+ if (mSpeechQueue.get(i).mType != SpeechItem.TEXT_TO_FILE){
+ mSpeechQueue.remove(i);
+ }
+ }
+ if ((mCurrentSpeechItem != null) &&
+ ((mCurrentSpeechItem.mType != SpeechItem.TEXT_TO_FILE) ||
+ mCurrentSpeechItem.mCallingApp.equals(callingApp))) {
+ result = sNativeSynth.stop();
+ mKillList.put(mCurrentSpeechItem, true);
+ if (mPlayer != null) {
+ try {
+ mPlayer.stop();
+ } catch (IllegalStateException e) {
+ // Do nothing, the player is already stopped.
+ }
}
+ mIsSpeaking = false;
+ mCurrentSpeechItem = null;
+ } else {
+ result = TextToSpeech.TTS_SUCCESS;
}
- Log.i("TTS", "Stopped");
+ Log.i("TtsService", "Stopped all");
}
} catch (InterruptedException e) {
- Log.e("TTS stop", "tryLock interrupted");
+ Log.e("TtsService", "TTS stopAll: tryLock interrupted");
e.printStackTrace();
} finally {
// This check is needed because finally will always run; even if the
// method returns somewhere in the try block.
- mCurrentSpeechItem = null;
if (speechQueueAvailable) {
speechQueueLock.unlock();
}
@@ -430,7 +494,6 @@ public class TtsService extends Service implements OnCompletionListener {
if (utteranceId.length() > 0){
dispatchUtteranceCompletedCallback(utteranceId, callingApp);
}
- mCurrentSpeechItem = null;
processSpeechQueue();
}
@@ -466,7 +529,6 @@ public class TtsService extends Service implements OnCompletionListener {
if (utteranceId.length() > 0){
dispatchUtteranceCompletedCallback(utteranceId, speechItem.mCallingApp);
}
- mCurrentSpeechItem = null;
processSpeechQueue();
}
}
@@ -486,21 +548,21 @@ public class TtsService extends Service implements OnCompletionListener {
if (!synthAvailable) {
Thread.sleep(100);
Thread synth = (new Thread(new SynthThread()));
- synth.setPriority(Thread.MIN_PRIORITY);
+ //synth.setPriority(Thread.MIN_PRIORITY);
synth.start();
return;
}
int streamType = DEFAULT_STREAM_TYPE;
+ String language = "";
+ String country = "";
+ String variant = "";
+ String speechRate = "";
if (speechItem.mParams != null){
- String language = "";
- String country = "";
- String variant = "";
for (int i = 0; i < speechItem.mParams.size() - 1; i = i + 2){
String param = speechItem.mParams.get(i);
if (param != null) {
if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_RATE)) {
- setSpeechRate("",
- Integer.parseInt(speechItem.mParams.get(i+1)));
+ speechRate = speechItem.mParams.get(i+1);
} else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_LANGUAGE)){
language = speechItem.mParams.get(i+1);
} else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_COUNTRY)){
@@ -519,31 +581,36 @@ public class TtsService extends Service implements OnCompletionListener {
}
}
}
+ }
+ // Only do the synthesis if it has not been killed by a subsequent utterance.
+ if (mKillList.get(speechItem) == null) {
if (language.length() > 0){
setLanguage("", language, country, variant);
}
+ if (speechRate.length() > 0){
+ setSpeechRate("", Integer.parseInt(speechRate));
+ }
+ sNativeSynth.speak(speechItem.mText, streamType);
}
- nativeSynth.speak(speechItem.mText, streamType);
} catch (InterruptedException e) {
- Log.e("TTS speakInternalOnly", "tryLock interrupted");
+ Log.e("TtsService", "TTS speakInternalOnly(): tryLock interrupted");
e.printStackTrace();
} finally {
// This check is needed because finally will always run;
// even if the
// method returns somewhere in the try block.
- if (synthAvailable) {
- synthesizerLock.unlock();
- }
if (utteranceId.length() > 0){
dispatchUtteranceCompletedCallback(utteranceId, speechItem.mCallingApp);
}
- mCurrentSpeechItem = null;
+ if (synthAvailable) {
+ synthesizerLock.unlock();
+ }
processSpeechQueue();
}
}
}
Thread synth = (new Thread(new SynthThread()));
- synth.setPriority(Thread.MIN_PRIORITY);
+ //synth.setPriority(Thread.MIN_PRIORITY);
synth.start();
}
@@ -552,26 +619,26 @@ public class TtsService extends Service implements OnCompletionListener {
public void run() {
boolean synthAvailable = false;
String utteranceId = "";
- Log.i("TTS", "Synthesizing to " + speechItem.mFilename);
+ Log.i("TtsService", "Synthesizing to " + speechItem.mFilename);
try {
synthAvailable = synthesizerLock.tryLock();
if (!synthAvailable) {
Thread.sleep(100);
Thread synth = (new Thread(new SynthThread()));
- synth.setPriority(Thread.MIN_PRIORITY);
+ //synth.setPriority(Thread.MIN_PRIORITY);
synth.start();
return;
}
+ String language = "";
+ String country = "";
+ String variant = "";
+ String speechRate = "";
if (speechItem.mParams != null){
- String language = "";
- String country = "";
- String variant = "";
for (int i = 0; i < speechItem.mParams.size() - 1; i = i + 2){
String param = speechItem.mParams.get(i);
- if (param != null){
- if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_RATE)){
- setSpeechRate("",
- Integer.parseInt(speechItem.mParams.get(i+1)));
+ if (param != null) {
+ if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_RATE)) {
+ speechRate = speechItem.mParams.get(i+1);
} else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_LANGUAGE)){
language = speechItem.mParams.get(i+1);
} else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_COUNTRY)){
@@ -583,31 +650,36 @@ public class TtsService extends Service implements OnCompletionListener {
}
}
}
+ }
+ // Only do the synthesis if it has not been killed by a subsequent utterance.
+ if (mKillList.get(speechItem) == null){
if (language.length() > 0){
setLanguage("", language, country, variant);
}
+ if (speechRate.length() > 0){
+ setSpeechRate("", Integer.parseInt(speechRate));
+ }
+ sNativeSynth.synthesizeToFile(speechItem.mText, speechItem.mFilename);
}
- nativeSynth.synthesizeToFile(speechItem.mText, speechItem.mFilename);
} catch (InterruptedException e) {
- Log.e("TTS synthToFileInternalOnly", "tryLock interrupted");
+ Log.e("TtsService", "TTS synthToFileInternalOnly(): tryLock interrupted");
e.printStackTrace();
} finally {
// This check is needed because finally will always run;
// even if the
// method returns somewhere in the try block.
- if (synthAvailable) {
- synthesizerLock.unlock();
- }
if (utteranceId.length() > 0){
dispatchUtteranceCompletedCallback(utteranceId, speechItem.mCallingApp);
}
- mCurrentSpeechItem = null;
+ if (synthAvailable) {
+ synthesizerLock.unlock();
+ }
processSpeechQueue();
}
}
}
Thread synth = (new Thread(new SynthThread()));
- synth.setPriority(Thread.MIN_PRIORITY);
+ //synth.setPriority(Thread.MIN_PRIORITY);
synth.start();
}
@@ -635,7 +707,7 @@ public class TtsService extends Service implements OnCompletionListener {
if (cb == null){
return;
}
- Log.i("TTS callback", "dispatch started");
+ Log.i("TtsService", "TTS callback: dispatch started");
// Broadcast to all clients the new value.
final int N = mCallbacks.beginBroadcast();
try {
@@ -645,7 +717,7 @@ public class TtsService extends Service implements OnCompletionListener {
// the dead object for us.
}
mCallbacks.finishBroadcast();
- Log.i("TTS callback", "dispatch completed to " + N);
+ Log.i("TtsService", "TTS callback: dispatch completed to " + N);
}
private SpeechItem splitCurrentTextIfNeeded(SpeechItem currentSpeechItem){
@@ -694,7 +766,7 @@ public class TtsService extends Service implements OnCompletionListener {
SoundResource sr = getSoundResource(mCurrentSpeechItem);
// Synth speech as needed - synthesizer should call
// processSpeechQueue to continue running the queue
- Log.i("TTS processing: ", mCurrentSpeechItem.mText);
+ Log.i("TtsService", "TTS processing: " + mCurrentSpeechItem.mText);
if (sr == null) {
if (mCurrentSpeechItem.mType == SpeechItem.TEXT) {
mCurrentSpeechItem = splitCurrentTextIfNeeded(mCurrentSpeechItem);
diff --git a/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java b/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java
index c6c94521faf8..e4c070ff8b29 100644
--- a/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java
+++ b/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java
@@ -106,6 +106,13 @@ public class AndroidServiceProxy extends ProcessProxy {
@Override
protected void performTask() throws IOException {
String svc = mServiceName;
+ Log.d(mTag, "----- Stop the daemon just in case: " + mServiceName);
+ SystemProperties.set(SVC_STOP_CMD, mServiceName);
+ if (!blockUntil(SVC_STATE_STOPPED, 5)) {
+ throw new IOException("cannot start service anew: " + svc
+ + ", it is still running");
+ }
+
Log.d(mTag, "+++++ Start: " + svc);
SystemProperties.set(SVC_START_CMD, svc);
diff --git a/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java b/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java
index cf153e3572e8..32b8e51f278d 100644
--- a/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java
+++ b/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java
@@ -57,6 +57,7 @@ public class VpnServiceBinder extends Service {
public void onStart (Intent intent, int startId) {
super.onStart(intent, startId);
setForeground(true);
+ android.util.Log.d("VpnServiceBinder", "becomes a foreground service");
}
public IBinder onBind(Intent intent) {
@@ -71,9 +72,8 @@ public class VpnServiceBinder extends Service {
}
private synchronized void checkStatus(VpnProfile p) {
- if (mService == null) broadcastConnectivity(p.getName(), VpnState.IDLE);
-
- if (!p.getName().equals(mService.mProfile.getName())) {
+ if ((mService == null)
+ || (!p.getName().equals(mService.mProfile.getName()))) {
broadcastConnectivity(p.getName(), VpnState.IDLE);
} else {
broadcastConnectivity(p.getName(), mService.getState());
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 131e156748d0..78db6f93727e 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -68,6 +68,7 @@ class AppWidgetService extends IAppWidgetService.Stub
private static final String SETTINGS_FILENAME = "appwidgets.xml";
private static final String SETTINGS_TMP_FILENAME = SETTINGS_FILENAME + ".tmp";
+ private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
/*
* When identifying a Host or Provider based on the calling process, use the uid field.
@@ -629,9 +630,12 @@ class AppWidgetService extends IAppWidgetService.Stub
Binder.restoreCallingIdentity(token);
}
if (!alreadyRegistered) {
+ long period = p.info.updatePeriodMillis;
+ if (period < MIN_UPDATE_PERIOD) {
+ period = MIN_UPDATE_PERIOD;
+ }
mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + p.info.updatePeriodMillis,
- p.info.updatePeriodMillis, p.broadcast);
+ SystemClock.elapsedRealtime() + period, period, p.broadcast);
}
}
}
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 20f0750bc8f8..fc1ec033d89d 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -100,18 +100,18 @@ class BackupManagerService extends IBackupManager.Stub {
private PowerManager mPowerManager;
private AlarmManager mAlarmManager;
- private boolean mEnabled; // access to this is synchronized on 'this'
- private boolean mProvisioned;
- private PowerManager.WakeLock mWakelock;
- private final BackupHandler mBackupHandler = new BackupHandler();
- private PendingIntent mRunBackupIntent;
- private BroadcastReceiver mRunBackupReceiver;
- private IntentFilter mRunBackupFilter;
+ boolean mEnabled; // access to this is synchronized on 'this'
+ boolean mProvisioned;
+ PowerManager.WakeLock mWakelock;
+ final BackupHandler mBackupHandler = new BackupHandler();
+ PendingIntent mRunBackupIntent;
+ BroadcastReceiver mRunBackupReceiver;
+ IntentFilter mRunBackupFilter;
// map UIDs to the set of backup client services within that UID's app set
- private final SparseArray<HashSet<ApplicationInfo>> mBackupParticipants
+ final SparseArray<HashSet<ApplicationInfo>> mBackupParticipants
= new SparseArray<HashSet<ApplicationInfo>>();
// set of backup services that have pending changes
- private class BackupRequest {
+ class BackupRequest {
public ApplicationInfo appInfo;
public boolean fullBackup;
@@ -125,35 +125,35 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
// Backups that we haven't started yet.
- private HashMap<ApplicationInfo,BackupRequest> mPendingBackups
+ HashMap<ApplicationInfo,BackupRequest> mPendingBackups
= new HashMap<ApplicationInfo,BackupRequest>();
// Pseudoname that we use for the Package Manager metadata "package"
- private static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
+ static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
// locking around the pending-backup management
- private final Object mQueueLock = new Object();
+ final Object mQueueLock = new Object();
// The thread performing the sequence of queued backups binds to each app's agent
// in succession. Bind notifications are asynchronously delivered through the
// Activity Manager; use this lock object to signal when a requested binding has
// completed.
- private final Object mAgentConnectLock = new Object();
- private IBackupAgent mConnectedAgent;
- private volatile boolean mConnecting;
+ final Object mAgentConnectLock = new Object();
+ IBackupAgent mConnectedAgent;
+ volatile boolean mConnecting;
// A similar synchronicity mechanism around clearing apps' data for restore
- private final Object mClearDataLock = new Object();
- private volatile boolean mClearingData;
+ final Object mClearDataLock = new Object();
+ volatile boolean mClearingData;
// Transport bookkeeping
- private final HashMap<String,IBackupTransport> mTransports
+ final HashMap<String,IBackupTransport> mTransports
= new HashMap<String,IBackupTransport>();
- private String mCurrentTransport;
- private IBackupTransport mLocalTransport, mGoogleTransport;
- private RestoreSession mActiveRestoreSession;
+ String mCurrentTransport;
+ IBackupTransport mLocalTransport, mGoogleTransport;
+ RestoreSession mActiveRestoreSession;
- private class RestoreParams {
+ class RestoreParams {
public IBackupTransport transport;
public IRestoreObserver observer;
public long token;
@@ -165,7 +165,7 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
- private class ClearParams {
+ class ClearParams {
public IBackupTransport transport;
public PackageInfo packageInfo;
@@ -176,11 +176,17 @@ class BackupManagerService extends IBackupManager.Stub {
}
// Where we keep our journal files and other bookkeeping
- private File mBaseStateDir;
- private File mDataDir;
- private File mJournalDir;
- private File mJournal;
- private RandomAccessFile mJournalStream;
+ File mBaseStateDir;
+ File mDataDir;
+ File mJournalDir;
+ File mJournal;
+ RandomAccessFile mJournalStream;
+
+ // Keep a log of all the apps we've ever backed up
+ private File mEverStored;
+ private RandomAccessFile mEverStoredStream;
+ HashSet<String> mEverStoredApps = new HashSet<String>();
+
public BackupManagerService(Context context) {
mContext = context;
@@ -215,6 +221,9 @@ class BackupManagerService extends IBackupManager.Stub {
mJournalDir.mkdirs(); // creates mBaseStateDir along the way
makeJournalLocked(); // okay because no other threads are running yet
+ // Set up the various sorts of package tracking we do
+ initPackageTracking();
+
// Build our mapping of uid to backup client services. This implicitly
// schedules a backup pass on the Package Manager metadata the first
// time anything needs to be backed up.
@@ -249,14 +258,6 @@ class BackupManagerService extends IBackupManager.Stub {
// leftover journal files into the pending backup set
parseLeftoverJournals();
- // Register for broadcasts about package install, etc., so we can
- // update the provider list.
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_PACKAGE_ADDED);
- filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- filter.addDataScheme("package");
- mContext.registerReceiver(mBroadcastReceiver, filter);
-
// Power management
mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "backup");
@@ -281,6 +282,67 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
+ private void initPackageTracking() {
+ if (DEBUG) Log.v(TAG, "Initializing package tracking");
+
+ // Keep a log of what apps we've ever backed up. Because we might have
+ // rebooted in the middle of an operation that was removing something from
+ // this log, we sanity-check its contents here and reconstruct it.
+ mEverStored = new File(mBaseStateDir, "processed");
+ File tempProcessedFile = new File(mBaseStateDir, "processed.new");
+ try {
+ RandomAccessFile temp = new RandomAccessFile(tempProcessedFile, "rw");
+ mEverStoredStream = new RandomAccessFile(mEverStored, "r");
+
+ // parse its existing contents
+ mEverStoredStream.seek(0);
+ temp.seek(0);
+ try {
+ while (true) {
+ PackageInfo info;
+ String pkg = mEverStoredStream.readUTF();
+ try {
+ info = mPackageManager.getPackageInfo(pkg, 0);
+ mEverStoredApps.add(pkg);
+ temp.writeUTF(pkg);
+ if (DEBUG) Log.v(TAG, " + " + pkg);
+ } catch (NameNotFoundException e) {
+ // nope, this package was uninstalled; don't include it
+ if (DEBUG) Log.v(TAG, " - " + pkg);
+ }
+ }
+ } catch (EOFException e) {
+ // now we're at EOF
+ }
+
+ // Once we've rewritten the backup history log, atomically replace the
+ // old one with the new one then reopen the file for continuing use.
+ temp.close();
+ mEverStoredStream.close();
+ tempProcessedFile.renameTo(mEverStored);
+ mEverStoredStream = new RandomAccessFile(mEverStored, "rwd");
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to open known-stored file!");
+ mEverStoredStream = null;
+ }
+
+ // If we were in the middle of removing something from the ever-backed-up
+ // file, there might be a transient "processed.new" file still present.
+ // We've reconstructed a coherent state at this point though, so we can
+ // safely discard that file now.
+ if (tempProcessedFile.exists()) {
+ tempProcessedFile.delete();
+ }
+
+ // Register for broadcasts about package install, etc., so we can
+ // update the provider list.
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addDataScheme("package");
+ mContext.registerReceiver(mBroadcastReceiver, filter);
+ }
+
private void makeJournalLocked() {
try {
mJournal = File.createTempFile("journal", null, mJournalDir);
@@ -485,6 +547,17 @@ class BackupManagerService extends IBackupManager.Stub {
mBackupParticipants.put(uid, set);
}
set.add(pkg.applicationInfo);
+
+ // If we've never seen this app before, schedule a backup for it
+ if (!mEverStoredApps.contains(pkg.packageName)) {
+ if (DEBUG) Log.i(TAG, "New app " + pkg.packageName
+ + " never backed up; scheduling");
+ try {
+ dataChanged(pkg.packageName);
+ } catch (RemoteException e) {
+ // can't happen; it's a local method call
+ }
+ }
}
}
}
@@ -528,6 +601,7 @@ class BackupManagerService extends IBackupManager.Stub {
for (ApplicationInfo entry: set) {
if (entry.packageName.equals(pkg.packageName)) {
set.remove(entry);
+ removeEverBackedUp(pkg.packageName);
break;
}
}
@@ -540,7 +614,7 @@ class BackupManagerService extends IBackupManager.Stub {
}
// Returns the set of all applications that define an android:backupAgent attribute
- private List<PackageInfo> allAgentPackages() {
+ List<PackageInfo> allAgentPackages() {
// !!! TODO: cache this and regenerate only when necessary
int flags = PackageManager.GET_SIGNATURES;
List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
@@ -570,6 +644,65 @@ class BackupManagerService extends IBackupManager.Stub {
addPackageParticipantsLockedInner(packageName, allApps);
}
+ // Called from the backup thread: record that the given app has been successfully
+ // backed up at least once
+ void logBackupComplete(String packageName) {
+ if (mEverStoredStream != null && !packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
+ synchronized (mEverStoredApps) {
+ if (mEverStoredApps.add(packageName)) {
+ try {
+ mEverStoredStream.writeUTF(packageName);
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to log backup of " + packageName + ", ceasing log");
+ try {
+ mEverStoredStream.close();
+ } catch (IOException ioe) {
+ // we're dropping it; no need to handle an exception on close here
+ }
+ mEverStoredStream = null;
+ }
+ }
+ }
+ }
+ }
+
+ // Remove our awareness of having ever backed up the given package
+ void removeEverBackedUp(String packageName) {
+ if (DEBUG) Log.v(TAG, "Removing backed-up knowledge of " + packageName
+ + ", new set:");
+
+ if (mEverStoredStream != null) {
+ synchronized (mEverStoredApps) {
+ // Rewrite the file and rename to overwrite. If we reboot in the middle,
+ // we'll recognize on initialization time that the package no longer
+ // exists and fix it up then.
+ File tempKnownFile = new File(mBaseStateDir, "processed.new");
+ try {
+ mEverStoredStream.close();
+ RandomAccessFile known = new RandomAccessFile(tempKnownFile, "rw");
+ mEverStoredApps.remove(packageName);
+ for (String s : mEverStoredApps) {
+ known.writeUTF(s);
+ if (DEBUG) Log.v(TAG, " " + s);
+ }
+ known.close();
+ tempKnownFile.renameTo(mEverStored);
+ mEverStoredStream = new RandomAccessFile(mEverStored, "rwd");
+ } catch (IOException e) {
+ // Bad: we couldn't create the new copy. For safety's sake we
+ // abandon the whole process and remove all what's-backed-up
+ // state entirely, meaning we'll force a backup pass for every
+ // participant on the next boot or [re]install.
+ Log.w(TAG, "Error rewriting backed-up set; halting log");
+ mEverStoredStream = null;
+ mEverStoredApps.clear();
+ tempKnownFile.delete();
+ mEverStored.delete();
+ }
+ }
+ }
+ }
+
// Return the given transport
private IBackupTransport getTransport(String transportName) {
synchronized (mTransports) {
@@ -799,6 +932,7 @@ class BackupManagerService extends IBackupManager.Stub {
boolean success = false;
try {
agent.doBackup(savedState, backupData, newState);
+ logBackupComplete(packageName);
success = true;
} finally {
if (savedState != null) {
@@ -1142,7 +1276,11 @@ class BackupManagerService extends IBackupManager.Stub {
File savedStateName = new File(mStateDir, packageName);
newStateName.renameTo(savedStateName);
} catch (Exception e) {
+ // If the agent fails restore, it might have put the app's data
+ // into an incoherent state. For consistency we wipe its data
+ // again in this case before propagating the exception
Log.e(TAG, "Error restoring data for " + packageName, e);
+ clearApplicationDataSynchronous(packageName);
}
}
}
@@ -1235,7 +1373,8 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
} else {
- Log.w(TAG, "dataChanged but no participant pkg='" + packageName + "'");
+ Log.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
+ + " uid=" + Binder.getCallingUid());
}
}
@@ -1566,6 +1705,10 @@ class BackupManagerService extends IBackupManager.Stub {
pw.println(" " + app.toString());
}
}
+ pw.println("Ever backed up: " + mEverStoredApps.size());
+ for (String pkg : mEverStoredApps) {
+ pw.println(" " + pkg);
+ }
pw.println("Pending: " + mPendingBackups.size());
for (BackupRequest req : mPendingBackups.values()) {
pw.println(" " + req);
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index 5cdce5b3b63f..a682fcb444ba 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -254,15 +254,15 @@ class BatteryService extends Binder {
// Separate broadcast is sent for power connected / not connected
// since the standard intent will not wake any applications and some
// applications may want to have smart behavior based on this.
+ Intent statusIntent = new Intent();
+ statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
if (mPlugType != 0 && mLastPlugType == 0) {
- Intent intent = new Intent(Intent.ACTION_POWER_CONNECTED);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendBroadcast(intent);
+ statusIntent.setAction(Intent.ACTION_POWER_CONNECTED);
+ mContext.sendBroadcast(statusIntent);
}
else if (mPlugType == 0 && mLastPlugType != 0) {
- Intent intent = new Intent(Intent.ACTION_POWER_DISCONNECTED);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendBroadcast(intent);
+ statusIntent.setAction(Intent.ACTION_POWER_DISCONNECTED);
+ mContext.sendBroadcast(statusIntent);
}
final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE;
@@ -289,10 +289,12 @@ class BatteryService extends Binder {
sendIntent();
if (sendBatteryLow) {
mSentLowBatteryBroadcast = true;
- mContext.sendBroadcast(new Intent(Intent.ACTION_BATTERY_LOW));
+ statusIntent.setAction(Intent.ACTION_BATTERY_LOW);
+ mContext.sendBroadcast(statusIntent);
} else if (mSentLowBatteryBroadcast && mLastBatteryLevel >= BATTERY_LEVEL_CLOSE_WARNING) {
mSentLowBatteryBroadcast = false;
- mContext.sendBroadcast(new Intent(Intent.ACTION_BATTERY_OKAY));
+ statusIntent.setAction(Intent.ACTION_BATTERY_OKAY);
+ mContext.sendBroadcast(statusIntent);
}
// This needs to be done after sendIntent() so that we get the lastest battery stats.
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 493bd099065f..62b839dc1861 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -74,7 +74,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private static class ConnectivityThread extends Thread {
private Context mContext;
-
+
private ConnectivityThread(Context context) {
super("ConnectivityThread");
mContext = context;
@@ -89,11 +89,11 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
Looper.loop();
}
-
+
public static ConnectivityService getServiceInstance(Context context) {
ConnectivityThread thread = new ConnectivityThread(context);
thread.start();
-
+
synchronized (thread) {
while (sServiceInstance == null) {
try {
@@ -101,27 +101,28 @@ public class ConnectivityService extends IConnectivityManager.Stub {
thread.wait();
} catch (InterruptedException ignore) {
Log.e(TAG,
- "Unexpected InterruptedException while waiting for ConnectivityService thread");
+ "Unexpected InterruptedException while waiting"+
+ " for ConnectivityService thread");
}
}
}
-
+
return sServiceInstance;
}
}
-
+
public static ConnectivityService getInstance(Context context) {
return ConnectivityThread.getServiceInstance(context);
}
-
+
private ConnectivityService(Context context) {
if (DBG) Log.v(TAG, "ConnectivityService starting up");
mContext = context;
mNetTrackers = new NetworkStateTracker[2];
Handler handler = new MyHandler();
-
+
mNetworkPreference = getPersistedNetworkPreference();
-
+
/*
* Create the network state trackers for Wi-Fi and mobile
* data. Maybe this could be done with a factory class,
@@ -137,7 +138,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mMobileDataStateTracker = new MobileDataStateTracker(context, handler);
mNetTrackers[ConnectivityManager.TYPE_MOBILE] = mMobileDataStateTracker;
-
+
mActiveNetwork = null;
mNumDnsEntries = 0;
@@ -148,11 +149,12 @@ public class ConnectivityService extends IConnectivityManager.Stub {
t.startMonitoring();
// Constructing this starts it too
- mWifiWatchdogService = new WifiWatchdogService(context, mWifiStateTracker);
+ mWifiWatchdogService = new WifiWatchdogService(context,
+ mWifiStateTracker);
}
/**
- * Sets the preferred network.
+ * Sets the preferred network.
* @param preference the new preference
*/
public synchronized void setNetworkPreference(int preference) {
@@ -173,9 +175,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private void persistNetworkPreference(int networkPreference) {
final ContentResolver cr = mContext.getContentResolver();
- Settings.Secure.putInt(cr, Settings.Secure.NETWORK_PREFERENCE, networkPreference);
+ Settings.Secure.putInt(cr, Settings.Secure.NETWORK_PREFERENCE,
+ networkPreference);
}
-
+
private int getPersistedNetworkPreference() {
final ContentResolver cr = mContext.getContentResolver();
@@ -187,9 +190,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
return ConnectivityManager.DEFAULT_NETWORK_PREFERENCE;
}
-
+
/**
- * Make the state of network connectivity conform to the preference settings.
+ * Make the state of network connectivity conform to the preference settings
* In this method, we only tear down a non-preferred network. Establishing
* a connection to the preferred network is taken care of when we handle
* the disconnect event from the non-preferred network
@@ -207,7 +210,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
ConnectivityManager.TYPE_WIFI);
if (t.getNetworkInfo().getType() != mNetworkPreference) {
- NetworkStateTracker otherTracker = mNetTrackers[otherNetType];
+ NetworkStateTracker otherTracker =
+ mNetTrackers[otherNetType];
if (otherTracker.isAvailable()) {
teardown(t);
}
@@ -229,7 +233,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
* Return NetworkInfo for the active (i.e., connected) network interface.
* It is assumed that at most one network is active at a time. If more
* than one is active, it is indeterminate which will be returned.
- * @return the info for the active network, or {@code null} if none is active
+ * @return the info for the active network, or {@code null} if none is
+ * active
*/
public NetworkInfo getActiveNetworkInfo() {
enforceAccessPermission();
@@ -287,7 +292,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
NetworkStateTracker tracker = mNetTrackers[networkType];
if (tracker != null) {
- return tracker.startUsingNetworkFeature(feature, getCallingPid(), getCallingUid());
+ return tracker.startUsingNetworkFeature(feature, getCallingPid(),
+ getCallingUid());
}
return -1;
}
@@ -299,7 +305,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
NetworkStateTracker tracker = mNetTrackers[networkType];
if (tracker != null) {
- return tracker.stopUsingNetworkFeature(feature, getCallingPid(), getCallingUid());
+ return tracker.stopUsingNetworkFeature(feature, getCallingPid(),
+ getCallingUid());
}
return -1;
}
@@ -307,9 +314,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
/**
* Ensure that a network route exists to deliver traffic to the specified
* host via the specified network interface.
- * @param networkType the type of the network over which traffic to the specified
- * host is to be routed
- * @param hostAddress the IP address of the host to which the route is desired
+ * @param networkType the type of the network over which traffic to the
+ * specified host is to be routed
+ * @param hostAddress the IP address of the host to which the route is
+ * desired
* @return {@code true} on success, {@code false} on failure
*/
public boolean requestRouteToHost(int networkType, int hostAddress) {
@@ -340,7 +348,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
return Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.BACKGROUND_DATA, 1) == 1;
}
-
+
/**
* @see ConnectivityManager#setBackgroundDataSetting(boolean)
*/
@@ -348,22 +356,24 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_BACKGROUND_DATA_SETTING,
"ConnectivityService");
-
+
if (getBackgroundDataSetting() == allowBackgroundDataUsage) return;
Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.BACKGROUND_DATA, allowBackgroundDataUsage ? 1 : 0);
-
+ Settings.Secure.BACKGROUND_DATA,
+ allowBackgroundDataUsage ? 1 : 0);
+
Intent broadcast = new Intent(
ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
mContext.sendBroadcast(broadcast);
- }
-
+ }
+
private int getNumConnectedNetworks() {
int numConnectedNets = 0;
for (NetworkStateTracker nt : mNetTrackers) {
- if (nt.getNetworkInfo().isConnected() && !nt.isTeardownRequested()) {
+ if (nt.getNetworkInfo().isConnected() &&
+ !nt.isTeardownRequested()) {
++numConnectedNets;
}
}
@@ -371,21 +381,22 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
private void enforceAccessPermission() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NETWORK_STATE,
- "ConnectivityService");
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_NETWORK_STATE,
+ "ConnectivityService");
}
private void enforceChangePermission() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_NETWORK_STATE,
- "ConnectivityService");
-
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_NETWORK_STATE,
+ "ConnectivityService");
}
/**
- * Handle a {@code DISCONNECTED} event. If this pertains to the non-active network,
- * we ignore it. If it is for the active network, we send out a broadcast.
- * But first, we check whether it might be possible to connect to a different
- * network.
+ * Handle a {@code DISCONNECTED} event. If this pertains to the non-active
+ * network, we ignore it. If it is for the active network, we send out a
+ * broadcast. But first, we check whether it might be possible to connect
+ * to a different network.
* @param info the {@code NetworkInfo} for the network
*/
private void handleDisconnect(NetworkInfo info) {
@@ -399,7 +410,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
* getting the disconnect for a network that we explicitly disabled
* in accordance with network preference policies.
*/
- if (mActiveNetwork == null || info.getType() != mActiveNetwork.getNetworkInfo().getType())
+ if (mActiveNetwork == null ||
+ info.getType() != mActiveNetwork.getNetworkInfo().getType())
return;
NetworkStateTracker newNet;
@@ -439,28 +451,33 @@ public class ConnectivityService extends IConnectivityManager.Stub {
intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason());
}
if (info.getExtraInfo() != null) {
- intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo());
+ intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO,
+ info.getExtraInfo());
}
if (switchTo != null) {
otherNetworkConnected = switchTo.isConnected();
if (DBG) {
if (otherNetworkConnected) {
- Log.v(TAG, "Switching to already connected " + switchTo.getTypeName());
+ Log.v(TAG, "Switching to already connected " +
+ switchTo.getTypeName());
} else {
- Log.v(TAG, "Attempting to switch to " + switchTo.getTypeName());
+ Log.v(TAG, "Attempting to switch to " +
+ switchTo.getTypeName());
}
}
- intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, switchTo);
+ intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO,
+ switchTo);
} else {
intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
}
- if (DBG) Log.v(TAG, "Sending DISCONNECT bcast for " + info.getTypeName() +
+ if (DBG) Log.v(TAG, "Sending DISCONNECT bcast for " +
+ info.getTypeName() +
(switchTo == null ? "" : " other=" + switchTo.getTypeName()));
mContext.sendStickyBroadcast(intent);
/*
- * If the failover network is already connected, then immediately send out
- * a followup broadcast indicating successful failover
+ * If the failover network is already connected, then immediately send
+ * out a followup broadcast indicating successful failover
*/
if (switchTo != null && otherNetworkConnected)
sendConnectedBroadcast(switchTo);
@@ -477,7 +494,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason());
}
if (info.getExtraInfo() != null) {
- intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo());
+ intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO,
+ info.getExtraInfo());
}
mContext.sendStickyBroadcast(intent);
}
@@ -499,9 +517,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
} else {
reasonText = " (" + reason + ").";
}
- Log.v(TAG, "Attempt to connect to " + info.getTypeName() + " failed" + reasonText);
+ Log.v(TAG, "Attempt to connect to " + info.getTypeName() +
+ " failed" + reasonText);
}
-
+
Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
@@ -509,7 +528,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
intent.putExtra(ConnectivityManager.EXTRA_REASON, reason);
}
if (extraInfo != null) {
- intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, extraInfo);
+ intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO,
+ extraInfo);
}
if (info.isFailover()) {
intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
@@ -562,32 +582,34 @@ public class ConnectivityService extends IConnectivityManager.Stub {
*/
if (!toredown || deadnet.getNetworkInfo().getType() != info.getType()) {
mActiveNetwork = thisNet;
- if (DBG) Log.v(TAG, "Sending CONNECT bcast for " + info.getTypeName());
+ if (DBG) Log.v(TAG, "Sending CONNECT bcast for " +
+ info.getTypeName());
thisNet.updateNetworkSettings();
sendConnectedBroadcast(info);
if (isFailover) {
otherNet.releaseWakeLock();
}
} else {
- if (DBG) Log.v(TAG, "Not broadcasting CONNECT_ACTION to torn down network " +
- info.getTypeName());
+ if (DBG) Log.v(TAG, "Not broadcasting CONNECT_ACTION to torn " +
+ "down network " + info.getTypeName());
}
}
private void handleScanResultsAvailable(NetworkInfo info) {
int networkType = info.getType();
if (networkType != ConnectivityManager.TYPE_WIFI) {
- if (DBG) Log.v(TAG, "Got ScanResultsAvailable for " + info.getTypeName() + " network."
- + " Don't know how to handle.");
+ if (DBG) Log.v(TAG, "Got ScanResultsAvailable for " +
+ info.getTypeName() + " network. Don't know how to handle.");
}
-
+
mNetTrackers[networkType].interpretScanResultsAvailable();
}
- private void handleNotificationChange(boolean visible, int id, Notification notification) {
+ private void handleNotificationChange(boolean visible, int id,
+ Notification notification) {
NotificationManager notificationManager = (NotificationManager) mContext
.getSystemService(Context.NOTIFICATION_SERVICE);
-
+
if (visible) {
notificationManager.notify(id, notification);
} else {
@@ -629,12 +651,15 @@ public class ConnectivityService extends IConnectivityManager.Stub {
int index = 1;
String lastDns = "";
int numConnectedNets = 0;
- int incrValue = ConnectivityManager.TYPE_MOBILE - ConnectivityManager.TYPE_WIFI;
+ int incrValue = ConnectivityManager.TYPE_MOBILE -
+ ConnectivityManager.TYPE_WIFI;
int stopValue = ConnectivityManager.TYPE_MOBILE + incrValue;
- for (int netType = ConnectivityManager.TYPE_WIFI; netType != stopValue; netType += incrValue) {
+ for (int netType = ConnectivityManager.TYPE_WIFI; netType != stopValue;
+ netType += incrValue) {
NetworkStateTracker nt = mNetTrackers[netType];
- if (nt.getNetworkInfo().isConnected() && !nt.isTeardownRequested()) {
+ if (nt.getNetworkInfo().isConnected() &&
+ !nt.isTeardownRequested()) {
++numConnectedNets;
String[] dnsList = nt.getNameServers();
for (int i = 0; i < dnsList.length && dnsList[i] != null; i++) {
@@ -652,23 +677,26 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
mNumDnsEntries = index - 1;
// Notify the name resolver library of the change
- SystemProperties.set("net.dnschange", String.valueOf(sDnsChangeCounter++));
+ SystemProperties.set("net.dnschange",
+ String.valueOf(sDnsChangeCounter++));
return numConnectedNets;
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump ConnectivityService from from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid());
+ pw.println("Permission Denial: can't dump ConnectivityService " +
+ "from from pid=" + Binder.getCallingPid() + ", uid=" +
+ Binder.getCallingUid());
return;
}
if (mActiveNetwork == null) {
pw.println("No active network");
} else {
- pw.println("Active network: " + mActiveNetwork.getNetworkInfo().getTypeName());
+ pw.println("Active network: " +
+ mActiveNetwork.getNetworkInfo().getTypeName());
}
pw.println();
for (NetworkStateTracker nst : mNetTrackers) {
@@ -685,29 +713,37 @@ public class ConnectivityService extends IConnectivityManager.Stub {
switch (msg.what) {
case NetworkStateTracker.EVENT_STATE_CHANGED:
info = (NetworkInfo) msg.obj;
- if (DBG) Log.v(TAG, "ConnectivityChange for " + info.getTypeName() + ": " +
+ if (DBG) Log.v(TAG, "ConnectivityChange for " +
+ info.getTypeName() + ": " +
info.getState() + "/" + info.getDetailedState());
// Connectivity state changed:
// [31-13] Reserved for future use
- // [12-9] Network subtype (for mobile network, as defined by TelephonyManager)
- // [8-3] Detailed state ordinal (as defined by NetworkInfo.DetailedState)
+ // [12-9] Network subtype (for mobile network, as defined
+ // by TelephonyManager)
+ // [8-3] Detailed state ordinal (as defined by
+ // NetworkInfo.DetailedState)
// [2-0] Network type (as defined by ConnectivityManager)
int eventLogParam = (info.getType() & 0x7) |
((info.getDetailedState().ordinal() & 0x3f) << 3) |
(info.getSubtype() << 9);
- EventLog.writeEvent(EVENTLOG_CONNECTIVITY_STATE_CHANGED, eventLogParam);
-
- if (info.getDetailedState() == NetworkInfo.DetailedState.FAILED) {
+ EventLog.writeEvent(EVENTLOG_CONNECTIVITY_STATE_CHANGED,
+ eventLogParam);
+
+ if (info.getDetailedState() ==
+ NetworkInfo.DetailedState.FAILED) {
handleConnectionFailure(info);
- } else if (info.getState() == NetworkInfo.State.DISCONNECTED) {
+ } else if (info.getState() ==
+ NetworkInfo.State.DISCONNECTED) {
handleDisconnect(info);
} else if (info.getState() == NetworkInfo.State.SUSPENDED) {
// TODO: need to think this over.
- // the logic here is, handle SUSPENDED the same as DISCONNECTED. The
- // only difference being we are broadcasting an intent with NetworkInfo
- // that's suspended. This allows the applications an opportunity to
- // handle DISCONNECTED and SUSPENDED differently, or not.
+ // the logic here is, handle SUSPENDED the same as
+ // DISCONNECTED. The only difference being we are
+ // broadcasting an intent with NetworkInfo that's
+ // suspended. This allows the applications an
+ // opportunity to handle DISCONNECTED and SUSPENDED
+ // differently, or not.
handleDisconnect(info);
} else if (info.getState() == NetworkInfo.State.CONNECTED) {
handleConnect(info);
@@ -719,9 +755,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
info = (NetworkInfo) msg.obj;
handleScanResultsAvailable(info);
break;
-
+
case NetworkStateTracker.EVENT_NOTIFICATION_CHANGED:
- handleNotificationChange(msg.arg1 == 1, msg.arg2, (Notification) msg.obj);
+ handleNotificationChange(msg.arg1 == 1, msg.arg2,
+ (Notification) msg.obj);
case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED:
handleConfigurationChange();
diff --git a/services/java/com/android/server/InputDevice.java b/services/java/com/android/server/InputDevice.java
index 9c1f9421a818..a71c39a0b3b5 100644
--- a/services/java/com/android/server/InputDevice.java
+++ b/services/java/com/android/server/InputDevice.java
@@ -66,19 +66,56 @@ public class InputDevice {
MotionEvent generateMotion(InputDevice device, long curTime, long curTimeNano,
boolean isAbs, Display display, int orientation,
int metaState) {
- if (!changed) {
- return null;
- }
-
float scaledX = x;
float scaledY = y;
float temp;
float scaledPressure = 1.0f;
float scaledSize = 0;
int edgeFlags = 0;
+
+ int action;
+ if (down != lastDown) {
+ if (isAbs) {
+ final AbsoluteInfo absX = device.absX;
+ final AbsoluteInfo absY = device.absY;
+ if (down && absX != null && absY != null) {
+ // We don't let downs start unless we are
+ // inside of the screen. There are two reasons for
+ // this: to avoid spurious touches when holding
+ // the edges of the device near the touchscreen,
+ // and to avoid reporting events if there are virtual
+ // keys on the touchscreen outside of the display
+ // area.
+ if (scaledX < absX.minValue || scaledX > absX.maxValue
+ || scaledY < absY.minValue || scaledY > absY.maxValue) {
+ if (false) Log.v("InputDevice", "Rejecting (" + scaledX + ","
+ + scaledY + "): outside of ("
+ + absX.minValue + "," + absY.minValue
+ + ")-(" + absX.maxValue + ","
+ + absY.maxValue + ")");
+ return null;
+ }
+ }
+ } else {
+ x = y = 0;
+ }
+ lastDown = down;
+ if (down) {
+ action = MotionEvent.ACTION_DOWN;
+ downTime = curTime;
+ } else {
+ action = MotionEvent.ACTION_UP;
+ }
+ currentMove = null;
+ } else {
+ action = MotionEvent.ACTION_MOVE;
+ }
+
if (isAbs) {
- int w = display.getWidth()-1;
- int h = display.getHeight()-1;
+ final int dispW = display.getWidth()-1;
+ final int dispH = display.getHeight()-1;
+ int w = dispW;
+ int h = dispH;
if (orientation == Surface.ROTATION_90
|| orientation == Surface.ROTATION_270) {
int tmp = w;
@@ -120,16 +157,17 @@ public class InputDevice {
break;
}
- if (scaledX == 0) {
- edgeFlags += MotionEvent.EDGE_LEFT;
- } else if (scaledX == display.getWidth() - 1.0f) {
- edgeFlags += MotionEvent.EDGE_RIGHT;
- }
-
- if (scaledY == 0) {
- edgeFlags += MotionEvent.EDGE_TOP;
- } else if (scaledY == display.getHeight() - 1.0f) {
- edgeFlags += MotionEvent.EDGE_BOTTOM;
+ if (action != MotionEvent.ACTION_DOWN) {
+ if (scaledX <= 0) {
+ edgeFlags += MotionEvent.EDGE_LEFT;
+ } else if (scaledX >= dispW) {
+ edgeFlags += MotionEvent.EDGE_RIGHT;
+ }
+ if (scaledY <= 0) {
+ edgeFlags += MotionEvent.EDGE_TOP;
+ } else if (scaledY >= dispH) {
+ edgeFlags += MotionEvent.EDGE_BOTTOM;
+ }
}
} else {
@@ -153,41 +191,25 @@ public class InputDevice {
}
}
- changed = false;
- if (down != lastDown) {
- int action;
- lastDown = down;
- if (down) {
- action = MotionEvent.ACTION_DOWN;
- downTime = curTime;
- } else {
- action = MotionEvent.ACTION_UP;
- }
- currentMove = null;
- if (!isAbs) {
- x = y = 0;
- }
- return MotionEvent.obtainNano(downTime, curTime, curTimeNano, action,
- scaledX, scaledY, scaledPressure, scaledSize, metaState,
- xPrecision, yPrecision, device.id, edgeFlags);
- } else {
- if (currentMove != null) {
- if (false) Log.i("InputDevice", "Adding batch x=" + scaledX
- + " y=" + scaledY + " to " + currentMove);
- currentMove.addBatch(curTime, scaledX, scaledY,
- scaledPressure, scaledSize, metaState);
- if (WindowManagerPolicy.WATCH_POINTER) {
- Log.i("KeyInputQueue", "Updating: " + currentMove);
- }
- return null;
+ if (currentMove != null) {
+ if (false) Log.i("InputDevice", "Adding batch x=" + scaledX
+ + " y=" + scaledY + " to " + currentMove);
+ currentMove.addBatch(curTime, scaledX, scaledY,
+ scaledPressure, scaledSize, metaState);
+ if (WindowManagerPolicy.WATCH_POINTER) {
+ Log.i("KeyInputQueue", "Updating: " + currentMove);
}
- MotionEvent me = MotionEvent.obtainNano(downTime, curTime, curTimeNano,
- MotionEvent.ACTION_MOVE, scaledX, scaledY,
- scaledPressure, scaledSize, metaState,
- xPrecision, yPrecision, device.id, edgeFlags);
+ return null;
+ }
+
+ MotionEvent me = MotionEvent.obtainNano(downTime, curTime,
+ curTimeNano, action, scaledX, scaledY,
+ scaledPressure, scaledSize, metaState,
+ xPrecision, yPrecision, device.id, edgeFlags);
+ if (action == MotionEvent.ACTION_MOVE) {
currentMove = me;
- return me;
}
+ return me;
}
}
diff --git a/services/java/com/android/server/KeyInputQueue.java b/services/java/com/android/server/KeyInputQueue.java
index 78cdf8b97eef..77051bd0023c 100644
--- a/services/java/com/android/server/KeyInputQueue.java
+++ b/services/java/com/android/server/KeyInputQueue.java
@@ -18,11 +18,13 @@ package com.android.server;
import android.content.Context;
import android.content.res.Configuration;
+import android.os.Environment;
import android.os.LatencyTimer;
import android.os.PowerManager;
import android.os.SystemClock;
import android.util.Log;
import android.util.SparseArray;
+import android.util.Xml;
import android.view.Display;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -30,10 +32,29 @@ import android.view.RawInputEvent;
import android.view.Surface;
import android.view.WindowManagerPolicy;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+
public abstract class KeyInputQueue {
static final String TAG = "KeyInputQueue";
- SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>();
+ static final boolean DEBUG_VIRTUAL_KEYS = false;
+
+ private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
+
+ final SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>();
+ final ArrayList<VirtualKey> mVirtualKeys = new ArrayList<VirtualKey>();
int mGlobalMetaState = 0;
boolean mHaveGlobalMetaState = false;
@@ -44,10 +65,14 @@ public abstract class KeyInputQueue {
int mCacheCount;
Display mDisplay = null;
+ int mDisplayWidth;
+ int mDisplayHeight;
int mOrientation = Surface.ROTATION_0;
int[] mKeyRotationMap = null;
+ VirtualKey mPressedVirtualKey = null;
+
PowerManager.WakeLock mWakeLock;
static final int[] KEY_90_MAP = new int[] {
@@ -110,11 +135,143 @@ public abstract class KeyInputQueue {
QueuedEvent next;
}
+ /**
+ * A key that exists as a part of the touch-screen, outside of the normal
+ * display area of the screen.
+ */
+ static class VirtualKey {
+ int scancode;
+ int centerx;
+ int centery;
+ int width;
+ int height;
+
+ int hitLeft;
+ int hitTop;
+ int hitRight;
+ int hitBottom;
+
+ InputDevice lastDevice;
+ int lastKeycode;
+
+ boolean checkHit(int x, int y) {
+ return (x >= hitLeft && x <= hitRight
+ && y >= hitTop && y <= hitBottom);
+ }
+
+ void computeHitRect(InputDevice dev, int dw, int dh) {
+ if (dev == lastDevice) {
+ return;
+ }
+
+ if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "computeHitRect for " + scancode
+ + ": dev=" + dev + " absX=" + dev.absX + " absY=" + dev.absY);
+
+ lastDevice = dev;
+
+ int minx = dev.absX.minValue;
+ int maxx = dev.absX.maxValue;
+
+ int halfw = width/2;
+ int left = centerx - halfw;
+ int right = centerx + halfw;
+ hitLeft = minx + ((left*maxx-minx)/dw);
+ hitRight = minx + ((right*maxx-minx)/dw);
+
+ int miny = dev.absY.minValue;
+ int maxy = dev.absY.maxValue;
+
+ int halfh = height/2;
+ int top = centery - halfh;
+ int bottom = centery + halfh;
+ hitTop = miny + ((top*maxy-miny)/dh);
+ hitBottom = miny + ((bottom*maxy-miny)/dh);
+ }
+ }
+
+ private void readVirtualKeys() {
+ try {
+ FileInputStream fis = new FileInputStream(
+ "/sys/board_properties/virtualkeys.synaptics-rmi-touchscreen");
+ InputStreamReader isr = new InputStreamReader(fis);
+ BufferedReader br = new BufferedReader(isr);
+ String str = br.readLine();
+ if (str != null) {
+ String[] it = str.split(":");
+ if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "***** VIRTUAL KEYS: " + it);
+ final int N = it.length-6;
+ for (int i=0; i<=N; i+=6) {
+ if (!"0x01".equals(it[i])) {
+ Log.w(TAG, "Unknown virtual key type at elem #" + i
+ + ": " + it[i]);
+ continue;
+ }
+ try {
+ VirtualKey sb = new VirtualKey();
+ sb.scancode = Integer.parseInt(it[i+1]);
+ sb.centerx = Integer.parseInt(it[i+2]);
+ sb.centery = Integer.parseInt(it[i+3]);
+ sb.width = Integer.parseInt(it[i+4]);
+ sb.height = Integer.parseInt(it[i+5]);
+ if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Virtual key "
+ + sb.scancode + ": center=" + sb.centerx + ","
+ + sb.centery + " size=" + sb.width + "x"
+ + sb.height);
+ mVirtualKeys.add(sb);
+ } catch (NumberFormatException e) {
+ Log.w(TAG, "Bad number at region " + i + " in: "
+ + str, e);
+ }
+ }
+ }
+ br.close();
+ } catch (FileNotFoundException e) {
+ Log.i(TAG, "No virtual keys found");
+ } catch (IOException e) {
+ Log.w(TAG, "Error reading virtual keys", e);
+ }
+ }
+
+ private void readExcludedDevices() {
+ // Read partner-provided list of excluded input devices
+ XmlPullParser parser = null;
+ // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
+ File confFile = new File(Environment.getRootDirectory(), EXCLUDED_DEVICES_PATH);
+ FileReader confreader = null;
+ try {
+ confreader = new FileReader(confFile);
+ parser = Xml.newPullParser();
+ parser.setInput(confreader);
+ XmlUtils.beginDocument(parser, "devices");
+
+ while (true) {
+ XmlUtils.nextElement(parser);
+ if (!"device".equals(parser.getName())) {
+ break;
+ }
+ String name = parser.getAttributeValue(null, "name");
+ if (name != null) {
+ Log.d(TAG, "addExcludedDevice " + name);
+ addExcludedDevice(name);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ // It's ok if the file does not exist.
+ } catch (Exception e) {
+ Log.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
+ } finally {
+ try { if (confreader != null) confreader.close(); } catch (IOException e) { }
+ }
+ }
+
KeyInputQueue(Context context) {
if (MEASURE_LATENCY) {
lt = new LatencyTimer(100, 1000);
}
+ readVirtualKeys();
+ readExcludedDevices();
+
PowerManager pm = (PowerManager)context.getSystemService(
Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
@@ -131,6 +288,12 @@ public abstract class KeyInputQueue {
public void setDisplay(Display display) {
mDisplay = display;
+
+ // We assume at this point that the display dimensions reflect the
+ // natural, unrotated display. We will perform hit tests for soft
+ // buttons based on that display.
+ mDisplayWidth = display.getWidth();
+ mDisplayHeight = display.getHeight();
}
public void getInputConfiguration(Configuration config) {
@@ -165,6 +328,7 @@ public abstract class KeyInputQueue {
public static native String getDeviceName(int deviceId);
public static native int getDeviceClasses(int deviceId);
+ public static native void addExcludedDevice(String deviceName);
public static native boolean getAbsoluteInfo(int deviceId, int axis,
InputDevice.AbsoluteInfo outInfo);
public static native int getSwitchState(int sw);
@@ -173,6 +337,7 @@ public abstract class KeyInputQueue {
public static native int getScancodeState(int deviceId, int sw);
public static native int getKeycodeState(int sw);
public static native int getKeycodeState(int deviceId, int sw);
+ public static native int scancodeToKeycode(int deviceId, int scancode);
public static native boolean hasKeys(int[] keycodes, boolean[] keyExists);
public static KeyEvent newKeyEvent(InputDevice device, long downTime,
@@ -189,6 +354,7 @@ public abstract class KeyInputQueue {
Thread mThread = new Thread("InputDeviceReader") {
public void run() {
+ Log.d(TAG, "InputDeviceReader.run()");
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
@@ -339,36 +505,127 @@ public abstract class KeyInputQueue {
}
MotionEvent me;
- me = di.mAbs.generateMotion(di, curTime, curTimeNano, true,
- mDisplay, mOrientation, mGlobalMetaState);
- if (false) Log.v(TAG, "Absolute: x=" + di.mAbs.x
- + " y=" + di.mAbs.y + " ev=" + me);
- if (me != null) {
- if (WindowManagerPolicy.WATCH_POINTER) {
- Log.i(TAG, "Enqueueing: " + me);
+
+ InputDevice.MotionState ms = di.mAbs;
+ if (ms.changed) {
+ ms.changed = false;
+
+ boolean doMotion = true;
+
+ // Look for virtual buttons.
+ VirtualKey vk = mPressedVirtualKey;
+ if (vk != null) {
+ doMotion = false;
+ if (!ms.down) {
+ mPressedVirtualKey = null;
+ ms.lastDown = ms.down;
+ if (DEBUG_VIRTUAL_KEYS) Log.v(TAG,
+ "Generate key up for: " + vk.scancode);
+ addLocked(di, curTimeNano, ev.flags,
+ RawInputEvent.CLASS_KEYBOARD,
+ newKeyEvent(di, di.mDownTime,
+ curTime, false,
+ vk.lastKeycode,
+ 0, vk.scancode, 0));
+ }
+ } else if (ms.down && !ms.lastDown) {
+ vk = findSoftButton(di);
+ if (vk != null) {
+ doMotion = false;
+ mPressedVirtualKey = vk;
+ vk.lastKeycode = scancodeToKeycode(
+ di.id, vk.scancode);
+ ms.lastDown = ms.down;
+ di.mDownTime = curTime;
+ if (DEBUG_VIRTUAL_KEYS) Log.v(TAG,
+ "Generate key down for: " + vk.scancode
+ + " (keycode=" + vk.lastKeycode + ")");
+ addLocked(di, curTimeNano, ev.flags,
+ RawInputEvent.CLASS_KEYBOARD,
+ newKeyEvent(di, di.mDownTime,
+ curTime, true,
+ vk.lastKeycode, 0,
+ vk.scancode, 0));
+ }
+ }
+
+ if (doMotion) {
+ me = ms.generateMotion(di, curTime,
+ curTimeNano, true, mDisplay,
+ mOrientation, mGlobalMetaState);
+ if (false) Log.v(TAG, "Absolute: x=" + di.mAbs.x
+ + " y=" + di.mAbs.y + " ev=" + me);
+ if (me != null) {
+ if (WindowManagerPolicy.WATCH_POINTER) {
+ Log.i(TAG, "Enqueueing: " + me);
+ }
+ addLocked(di, curTimeNano, ev.flags,
+ RawInputEvent.CLASS_TOUCHSCREEN, me);
+ }
}
- addLocked(di, curTimeNano, ev.flags,
- RawInputEvent.CLASS_TOUCHSCREEN, me);
}
- me = di.mRel.generateMotion(di, curTime, curTimeNano, false,
- mDisplay, mOrientation, mGlobalMetaState);
- if (false) Log.v(TAG, "Relative: x=" + di.mRel.x
- + " y=" + di.mRel.y + " ev=" + me);
- if (me != null) {
- addLocked(di, curTimeNano, ev.flags,
- RawInputEvent.CLASS_TRACKBALL, me);
+
+ ms = di.mRel;
+ if (ms.changed) {
+ ms.changed = false;
+
+ me = ms.generateMotion(di, curTime,
+ curTimeNano, false, mDisplay,
+ mOrientation, mGlobalMetaState);
+ if (false) Log.v(TAG, "Relative: x=" + di.mRel.x
+ + " y=" + di.mRel.y + " ev=" + me);
+ if (me != null) {
+ addLocked(di, curTimeNano, ev.flags,
+ RawInputEvent.CLASS_TRACKBALL, me);
+ }
}
}
}
}
}
- }
- catch (RuntimeException exc) {
+
+ } catch (RuntimeException exc) {
Log.e(TAG, "InputReaderThread uncaught exception", exc);
}
}
};
+ private VirtualKey findSoftButton(InputDevice dev) {
+ final int N = mVirtualKeys.size();
+ if (N <= 0) {
+ return null;
+ }
+
+ final InputDevice.AbsoluteInfo absx = dev.absX;
+ final InputDevice.AbsoluteInfo absy = dev.absY;
+ final InputDevice.MotionState absm = dev.mAbs;
+ if (absx == null || absy == null || absm == null) {
+ return null;
+ }
+
+ if (absm.x >= absx.minValue && absm.x <= absx.maxValue
+ && absm.y >= absy.minValue && absm.y <= absy.maxValue) {
+ if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Input (" + absm.x
+ + "," + absm.y + ") inside of display");
+ return null;
+ }
+
+ for (int i=0; i<N; i++) {
+ VirtualKey sb = mVirtualKeys.get(i);
+ sb.computeHitRect(dev, mDisplayWidth, mDisplayHeight);
+ if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Hit test (" + absm.x + ","
+ + absm.y + ") in code " + sb.scancode + " - (" + sb.hitLeft
+ + "," + sb.hitTop + ")-(" + sb.hitRight + ","
+ + sb.hitBottom + ")");
+ if (sb.checkHit(absm.x, absm.y)) {
+ if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Hit!");
+ return sb;
+ }
+ }
+
+ return null;
+ }
+
/**
* Returns a new meta state for the given keys and old state.
*/
diff --git a/services/java/com/android/server/MountListener.java b/services/java/com/android/server/MountListener.java
index 2e430c849d97..3e535851b9e9 100644
--- a/services/java/com/android/server/MountListener.java
+++ b/services/java/com/android/server/MountListener.java
@@ -202,6 +202,7 @@ final class MountListener implements Runnable {
byte[] buffer = new byte[100];
writeCommand(VOLD_CMD_SEND_UMS_STATUS);
+ mountMedia(Environment.getExternalStorageDirectory().getAbsolutePath());
while (true) {
int count = inputStream.read(buffer);
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 682eaa7c89bb..7025e79776cd 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -4908,6 +4908,22 @@ class PackageManagerService extends IPackageManager.Stub {
pw.print(" resourcePath="); pw.println(ps.resourcePathString);
if (ps.pkg != null) {
pw.print(" dataDir="); pw.println(ps.pkg.applicationInfo.dataDir);
+ pw.print(" targetSdk="); pw.println(ps.pkg.applicationInfo.targetSdkVersion);
+ pw.print(" densities="); pw.println(ps.pkg.supportsDensityList);
+ ArrayList<String> screens = new ArrayList<String>();
+ if ((ps.pkg.applicationInfo.flags &
+ ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS) != 0) {
+ screens.add("medium");
+ }
+ if ((ps.pkg.applicationInfo.flags &
+ ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) {
+ screens.add("large");
+ }
+ if ((ps.pkg.applicationInfo.flags &
+ ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS) != 0) {
+ screens.add("small,");
+ }
+ pw.print(" supportsScreens="); pw.println(screens);
}
pw.print(" timeStamp="); pw.println(ps.getTimeStampStr());
pw.print(" signatures="); pw.println(ps.signatures);
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index a561d119cd79..67e8cf3a2683 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -171,6 +171,7 @@ public class WifiService extends IWifiManager.Stub {
WifiService(Context context, WifiStateTracker tracker) {
mContext = context;
mWifiStateTracker = tracker;
+ mWifiStateTracker.enableRssiPolling(true);
mBatteryStats = BatteryStatsService.getService();
mScanResultCache = new LinkedHashMap<String, ScanResult>(
@@ -1367,9 +1368,11 @@ public class WifiService extends IWifiManager.Stub {
mAlarmManager.cancel(mIdleIntent);
mDeviceIdle = false;
mScreenOff = false;
+ mWifiStateTracker.enableRssiPolling(true);
} else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
Log.d(TAG, "ACTION_SCREEN_OFF");
mScreenOff = true;
+ mWifiStateTracker.enableRssiPolling(false);
/*
* Set a timer to put Wi-Fi to sleep, but only if the screen is off
* AND the "stay on while plugged in" setting doesn't match the
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 25aff5c60603..4bc7c682a02c 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -24,8 +24,10 @@ import static android.os.LocalPowerManager.TOUCH_UP_EVENT;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_BLUR_BEHIND;
+import static android.view.WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
import static android.view.WindowManager.LayoutParams.FLAG_SYSTEM_ERROR;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
@@ -102,6 +104,7 @@ import android.view.WindowManager;
import android.view.WindowManagerImpl;
import android.view.WindowManagerPolicy;
import android.view.WindowManager.LayoutParams;
+import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.Transformation;
@@ -175,6 +178,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
*/
static final int DEFAULT_DIM_DURATION = 200;
+ /** Amount of time (in milliseconds) to animate the fade-in-out transition for
+ * compatible windows.
+ */
+ static final int DEFAULT_FADE_IN_OUT_DURATION = 400;
+
/** Adjustment to time to perform a dim, to make it more dramatic.
*/
static final int DIM_DURATION_MULTIPLIER = 6;
@@ -328,12 +336,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
IInputMethodManager mInputMethodManager;
SurfaceSession mFxSession;
- Surface mDimSurface;
- boolean mDimShown;
- float mDimCurrentAlpha;
- float mDimTargetAlpha;
- float mDimDeltaPerMs;
- long mLastDimAnimTime;
+ private DimAnimator mDimAnimator = null;
Surface mBlurSurface;
boolean mBlurShown;
@@ -1861,44 +1864,51 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
// artifacts when we unfreeze the display if some different animation
// is running.
if (!mDisplayFrozen) {
- int animAttr = 0;
- switch (transit) {
- case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN:
- animAttr = enter
- ? com.android.internal.R.styleable.WindowAnimation_activityOpenEnterAnimation
- : com.android.internal.R.styleable.WindowAnimation_activityOpenExitAnimation;
- break;
- case WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE:
- animAttr = enter
- ? com.android.internal.R.styleable.WindowAnimation_activityCloseEnterAnimation
- : com.android.internal.R.styleable.WindowAnimation_activityCloseExitAnimation;
- break;
- case WindowManagerPolicy.TRANSIT_TASK_OPEN:
- animAttr = enter
- ? com.android.internal.R.styleable.WindowAnimation_taskOpenEnterAnimation
- : com.android.internal.R.styleable.WindowAnimation_taskOpenExitAnimation;
- break;
- case WindowManagerPolicy.TRANSIT_TASK_CLOSE:
- animAttr = enter
- ? com.android.internal.R.styleable.WindowAnimation_taskCloseEnterAnimation
- : com.android.internal.R.styleable.WindowAnimation_taskCloseExitAnimation;
- break;
- case WindowManagerPolicy.TRANSIT_TASK_TO_FRONT:
- animAttr = enter
- ? com.android.internal.R.styleable.WindowAnimation_taskToFrontEnterAnimation
- : com.android.internal.R.styleable.WindowAnimation_taskToFrontExitAnimation;
- break;
- case WindowManagerPolicy.TRANSIT_TASK_TO_BACK:
- animAttr = enter
- ? com.android.internal.R.styleable.WindowAnimation_taskToBackEnterAnimation
- : com.android.internal.R.styleable.WindowAnimation_taskToBackExitAnimation;
- break;
+ Animation a;
+ if (lp != null && (lp.flags & FLAG_COMPATIBLE_WINDOW) != 0) {
+ a = new FadeInOutAnimation(enter);
+ if (DEBUG_ANIM) Log.v(TAG,
+ "applying FadeInOutAnimation for a window in compatibility mode");
+ } else {
+ int animAttr = 0;
+ switch (transit) {
+ case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN:
+ animAttr = enter
+ ? com.android.internal.R.styleable.WindowAnimation_activityOpenEnterAnimation
+ : com.android.internal.R.styleable.WindowAnimation_activityOpenExitAnimation;
+ break;
+ case WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE:
+ animAttr = enter
+ ? com.android.internal.R.styleable.WindowAnimation_activityCloseEnterAnimation
+ : com.android.internal.R.styleable.WindowAnimation_activityCloseExitAnimation;
+ break;
+ case WindowManagerPolicy.TRANSIT_TASK_OPEN:
+ animAttr = enter
+ ? com.android.internal.R.styleable.WindowAnimation_taskOpenEnterAnimation
+ : com.android.internal.R.styleable.WindowAnimation_taskOpenExitAnimation;
+ break;
+ case WindowManagerPolicy.TRANSIT_TASK_CLOSE:
+ animAttr = enter
+ ? com.android.internal.R.styleable.WindowAnimation_taskCloseEnterAnimation
+ : com.android.internal.R.styleable.WindowAnimation_taskCloseExitAnimation;
+ break;
+ case WindowManagerPolicy.TRANSIT_TASK_TO_FRONT:
+ animAttr = enter
+ ? com.android.internal.R.styleable.WindowAnimation_taskToFrontEnterAnimation
+ : com.android.internal.R.styleable.WindowAnimation_taskToFrontExitAnimation;
+ break;
+ case WindowManagerPolicy.TRANSIT_TASK_TO_BACK:
+ animAttr = enter
+ ? com.android.internal.R.styleable.WindowAnimation_taskToBackEnterAnimation
+ : com.android.internal.R.styleable.WindowAnimation_taskToBackExitAnimation;
+ break;
+ }
+ a = loadAnimation(lp, animAttr);
+ if (DEBUG_ANIM) Log.v(TAG, "applyAnimation: wtoken=" + wtoken
+ + " anim=" + a
+ + " animAttr=0x" + Integer.toHexString(animAttr)
+ + " transit=" + transit);
}
- Animation a = loadAnimation(lp, animAttr);
- if (DEBUG_ANIM) Log.v(TAG, "applyAnimation: wtoken=" + wtoken
- + " anim=" + a
- + " animAttr=0x" + Integer.toHexString(animAttr)
- + " transit=" + transit);
if (a != null) {
if (DEBUG_ANIM) {
RuntimeException e = new RuntimeException();
@@ -5909,9 +5919,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
final Rect display = mDisplayFrame;
display.set(df);
- if ((mAttrs.flags & WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW) != 0) {
+ if ((mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0) {
container.intersect(mCompatibleScreenFrame);
- display.intersect(mCompatibleScreenFrame);
+ if ((mAttrs.flags & FLAG_LAYOUT_NO_LIMITS) == 0) {
+ display.intersect(mCompatibleScreenFrame);
+ }
}
final int pw = container.right - container.left;
@@ -6628,12 +6640,17 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
return false;
}
final Rect frame = shownFrame ? mShownFrame : mFrame;
- if (frame.left <= 0 && frame.top <= 0
- && frame.right >= screenWidth
- && frame.bottom >= screenHeight) {
- return true;
+
+ if ((mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0) {
+ return frame.left <= mCompatibleScreenFrame.left &&
+ frame.top <= mCompatibleScreenFrame.top &&
+ frame.right >= mCompatibleScreenFrame.right &&
+ frame.bottom >= mCompatibleScreenFrame.bottom;
+ } else {
+ return frame.left <= 0 && frame.top <= 0
+ && frame.right >= screenWidth
+ && frame.bottom >= screenHeight;
}
- return false;
}
/**
@@ -6647,13 +6664,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
boolean needsBackgroundFiller(int screenWidth, int screenHeight) {
return
// only if the application is requesting compatible window
- (mAttrs.flags & mAttrs.FLAG_COMPATIBLE_WINDOW) != 0 &&
- // and only if the application wanted to fill the screen
- mAttrs.width == mAttrs.FILL_PARENT &&
- mAttrs.height == mAttrs.FILL_PARENT &&
- // and only if the screen is bigger
- ((mFrame.right - mFrame.right) < screenWidth ||
- (mFrame.bottom - mFrame.top) < screenHeight);
+ (mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0 &&
+ // only if it's visible
+ mHasDrawn && mViewVisibility == View.VISIBLE &&
+ // and only if the application fills the compatible screen
+ mFrame.left <= mCompatibleScreenFrame.left &&
+ mFrame.top <= mCompatibleScreenFrame.top &&
+ mFrame.right >= mCompatibleScreenFrame.right &&
+ mFrame.bottom >= mCompatibleScreenFrame.bottom &&
+ // and starting window do not need background filler
+ mAttrs.type != mAttrs.TYPE_APPLICATION_STARTING;
}
boolean isFullscreen(int screenWidth, int screenHeight) {
@@ -7266,17 +7286,27 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
public static WindowManager.LayoutParams findAnimations(
ArrayList<AppWindowToken> order,
- ArrayList<AppWindowToken> tokenList1,
- ArrayList<AppWindowToken> tokenList2) {
+ ArrayList<AppWindowToken> openingTokenList1,
+ ArrayList<AppWindowToken> closingTokenList2) {
// We need to figure out which animation to use...
+
+ // First, check if there is a compatible window in opening/closing
+ // apps, and use it if exists.
WindowManager.LayoutParams animParams = null;
int animSrc = 0;
-
+ animParams = findCompatibleWindowParams(openingTokenList1);
+ if (animParams == null) {
+ animParams = findCompatibleWindowParams(closingTokenList2);
+ }
+ if (animParams != null) {
+ return animParams;
+ }
+
//Log.i(TAG, "Looking for animations...");
for (int i=order.size()-1; i>=0; i--) {
AppWindowToken wtoken = order.get(i);
//Log.i(TAG, "Token " + wtoken + " with " + wtoken.windows.size() + " windows");
- if (tokenList1.contains(wtoken) || tokenList2.contains(wtoken)) {
+ if (openingTokenList1.contains(wtoken) || closingTokenList2.contains(wtoken)) {
int j = wtoken.windows.size();
while (j > 0) {
j--;
@@ -7304,6 +7334,21 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
return animParams;
}
+ private static LayoutParams findCompatibleWindowParams(ArrayList<AppWindowToken> tokenList) {
+ for (int appCount = tokenList.size() - 1; appCount >= 0; appCount--) {
+ AppWindowToken wtoken = tokenList.get(appCount);
+ // Just checking one window is sufficient as all windows have the compatible flag
+ // if the application is in compatibility mode.
+ if (wtoken.windows.size() > 0) {
+ WindowManager.LayoutParams params = wtoken.windows.get(0).mAttrs;
+ if ((params.flags & FLAG_COMPATIBLE_WINDOW) != 0) {
+ return params;
+ }
+ }
+ }
+ return null;
+ }
+
// -------------------------------------------------------------
// DummyAnimation
// -------------------------------------------------------------
@@ -8457,7 +8502,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
obscured = true;
} else if (opaqueDrawn && w.needsBackgroundFiller(dw, dh)) {
if (SHOW_TRANSACTIONS) Log.d(TAG, "showing background filler");
- // This window is in compatibility mode, and needs background filler.
+ // This window is in compatibility mode, and needs background filler.
obscured = true;
if (mBackgroundFillerSurface == null) {
try {
@@ -8491,56 +8536,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (!dimming) {
//Log.i(TAG, "DIM BEHIND: " + w);
dimming = true;
- mDimShown = true;
- if (mDimSurface == null) {
- if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM "
- + mDimSurface + ": CREATE");
- try {
- mDimSurface = new Surface(mFxSession, 0,
- -1, 16, 16,
- PixelFormat.OPAQUE,
- Surface.FX_SURFACE_DIM);
- } catch (Exception e) {
- Log.e(TAG, "Exception creating Dim surface", e);
- }
- }
- if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM "
- + mDimSurface + ": SHOW pos=(0,0) (" +
- dw + "x" + dh + "), layer=" + (w.mAnimLayer-1));
- if (mDimSurface != null) {
- try {
- mDimSurface.setPosition(0, 0);
- mDimSurface.setSize(dw, dh);
- mDimSurface.show();
- } catch (RuntimeException e) {
- Log.w(TAG, "Failure showing dim surface", e);
- }
- }
- }
- mDimSurface.setLayer(w.mAnimLayer-1);
- final float target = w.mExiting ? 0 : attrs.dimAmount;
- if (mDimTargetAlpha != target) {
- // 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()
- : DEFAULT_DIM_DURATION;
- if (target > mDimTargetAlpha) {
- // This is happening behind the activity UI,
- // so we can make it run a little longer to
- // give a stronger impression without disrupting
- // the user.
- duration *= DIM_DURATION_MULTIPLIER;
+ if (mDimAnimator == null) {
+ mDimAnimator = new DimAnimator(mFxSession);
}
- if (duration < 1) {
- // Don't divide by zero
- duration = 1;
- }
- mDimTargetAlpha = target;
- mDimDeltaPerMs = (mDimTargetAlpha-mDimCurrentAlpha)
- / duration;
+ mDimAnimator.show(dw, dh);
}
+ mDimAnimator.updateParameters(w, currentTime);
}
if ((attrFlags&FLAG_BLUR_BEHIND) != 0) {
if (!blurring) {
@@ -8588,58 +8589,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
}
- if (!dimming && mDimShown) {
- // Time to hide the dim surface... start fading.
- if (mDimTargetAlpha != 0) {
- mLastDimAnimTime = currentTime;
- mDimTargetAlpha = 0;
- mDimDeltaPerMs = (-mDimCurrentAlpha) / DEFAULT_DIM_DURATION;
- }
- }
-
- if (mDimShown && mLastDimAnimTime != 0) {
- mDimCurrentAlpha += mDimDeltaPerMs
- * (currentTime-mLastDimAnimTime);
- boolean more = true;
- if (mDisplayFrozen) {
- // If the display is frozen, there is no reason to animate.
- more = false;
- } else if (mDimDeltaPerMs > 0) {
- if (mDimCurrentAlpha > mDimTargetAlpha) {
- more = false;
- }
- } else if (mDimDeltaPerMs < 0) {
- if (mDimCurrentAlpha < mDimTargetAlpha) {
- more = false;
- }
- } else {
- more = false;
- }
-
- // Do we need to continue animating?
- if (more) {
- if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM "
- + mDimSurface + ": alpha=" + mDimCurrentAlpha);
- mLastDimAnimTime = currentTime;
- mDimSurface.setAlpha(mDimCurrentAlpha);
- animating = true;
- } else {
- mDimCurrentAlpha = mDimTargetAlpha;
- mLastDimAnimTime = 0;
- if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM "
- + mDimSurface + ": final alpha=" + mDimCurrentAlpha);
- mDimSurface.setAlpha(mDimCurrentAlpha);
- if (!dimming) {
- if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM " + mDimSurface
- + ": HIDE");
- try {
- mDimSurface.hide();
- } catch (RuntimeException e) {
- Log.w(TAG, "Illegal argument exception hiding dim surface");
- }
- mDimShown = false;
- }
- }
+ if (mDimAnimator != null && mDimAnimator.mDimShown) {
+ animating |= mDimAnimator.updateSurface(dimming, currentTime, mDisplayFrozen);
}
if (!blurring && mBlurShown) {
@@ -9182,11 +9133,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
pw.print(" mDisplayEnabled="); pw.println(mDisplayEnabled);
pw.print(" mLayoutNeeded="); pw.print(mLayoutNeeded);
pw.print(" mBlurShown="); pw.println(mBlurShown);
- pw.print(" mDimShown="); pw.print(mDimShown);
- pw.print(" current="); pw.print(mDimCurrentAlpha);
- pw.print(" target="); pw.print(mDimTargetAlpha);
- pw.print(" delta="); pw.print(mDimDeltaPerMs);
- pw.print(" lastAnimTime="); pw.println(mLastDimAnimTime);
+ if (mDimAnimator != null) {
+ mDimAnimator.printTo(pw);
+ } else {
+ pw.print( " no DimAnimator ");
+ }
pw.print(" mInputMethodAnimLayerAdjustment=");
pw.println(mInputMethodAnimLayerAdjustment);
pw.print(" mDisplayFrozen="); pw.print(mDisplayFrozen);
@@ -9227,4 +9178,188 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
synchronized (mKeyguardDisabled) { }
synchronized (mKeyWaiter) { }
}
+
+ /**
+ * DimAnimator class that controls the dim animation. This holds the surface and
+ * all state used for dim animation.
+ */
+ private static class DimAnimator {
+ Surface mDimSurface;
+ boolean mDimShown = false;
+ float mDimCurrentAlpha;
+ float mDimTargetAlpha;
+ float mDimDeltaPerMs;
+ long mLastDimAnimTime;
+
+ DimAnimator (SurfaceSession session) {
+ if (mDimSurface == null) {
+ if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM "
+ + mDimSurface + ": CREATE");
+ try {
+ mDimSurface = new Surface(session, 0, -1, 16, 16, PixelFormat.OPAQUE,
+ Surface.FX_SURFACE_DIM);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception creating Dim surface", e);
+ }
+ }
+ }
+
+ /**
+ * Show the dim surface.
+ */
+ void show(int dw, int dh) {
+ if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM " + mDimSurface + ": SHOW pos=(0,0) (" +
+ dw + "x" + dh + ")");
+ mDimShown = true;
+ try {
+ mDimSurface.setPosition(0, 0);
+ mDimSurface.setSize(dw, dh);
+ mDimSurface.show();
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Failure showing dim surface", e);
+ }
+ }
+
+ /**
+ * Set's the dim surface's layer and update dim parameters that will be used in
+ * {@link updateSurface} after all windows are examined.
+ */
+ void updateParameters(WindowState w, long currentTime) {
+ mDimSurface.setLayer(w.mAnimLayer-1);
+
+ final float target = w.mExiting ? 0 : w.mAttrs.dimAmount;
+ if (SHOW_TRANSACTIONS) Log.i(TAG, "layer=" + (w.mAnimLayer-1) + ", target=" + target);
+ if (mDimTargetAlpha != target) {
+ // 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()
+ : DEFAULT_DIM_DURATION;
+ if (target > mDimTargetAlpha) {
+ // This is happening behind the activity UI,
+ // so we can make it run a little longer to
+ // give a stronger impression without disrupting
+ // the user.
+ duration *= DIM_DURATION_MULTIPLIER;
+ }
+ if (duration < 1) {
+ // Don't divide by zero
+ duration = 1;
+ }
+ mDimTargetAlpha = target;
+ mDimDeltaPerMs = (mDimTargetAlpha-mDimCurrentAlpha) / duration;
+ }
+ }
+
+ /**
+ * Updating the surface's alpha. Returns true if the animation continues, or returns
+ * false when the animation is finished and the dim surface is hidden.
+ */
+ boolean updateSurface(boolean dimming, long currentTime, boolean displayFrozen) {
+ if (!dimming) {
+ if (mDimTargetAlpha != 0) {
+ mLastDimAnimTime = currentTime;
+ mDimTargetAlpha = 0;
+ mDimDeltaPerMs = (-mDimCurrentAlpha) / DEFAULT_DIM_DURATION;
+ }
+ }
+
+ boolean animating = false;
+ if (mLastDimAnimTime != 0) {
+ mDimCurrentAlpha += mDimDeltaPerMs
+ * (currentTime-mLastDimAnimTime);
+ boolean more = true;
+ if (displayFrozen) {
+ // If the display is frozen, there is no reason to animate.
+ more = false;
+ } else if (mDimDeltaPerMs > 0) {
+ if (mDimCurrentAlpha > mDimTargetAlpha) {
+ more = false;
+ }
+ } else if (mDimDeltaPerMs < 0) {
+ if (mDimCurrentAlpha < mDimTargetAlpha) {
+ more = false;
+ }
+ } else {
+ more = false;
+ }
+
+ // Do we need to continue animating?
+ if (more) {
+ if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM "
+ + mDimSurface + ": alpha=" + mDimCurrentAlpha);
+ mLastDimAnimTime = currentTime;
+ mDimSurface.setAlpha(mDimCurrentAlpha);
+ animating = true;
+ } else {
+ mDimCurrentAlpha = mDimTargetAlpha;
+ mLastDimAnimTime = 0;
+ if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM "
+ + mDimSurface + ": final alpha=" + mDimCurrentAlpha);
+ mDimSurface.setAlpha(mDimCurrentAlpha);
+ if (!dimming) {
+ if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM " + mDimSurface
+ + ": HIDE");
+ try {
+ mDimSurface.hide();
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Illegal argument exception hiding dim surface");
+ }
+ mDimShown = false;
+ }
+ }
+ }
+ return animating;
+ }
+
+ public void printTo(PrintWriter pw) {
+ pw.print(" mDimShown="); pw.print(mDimShown);
+ pw.print(" current="); pw.print(mDimCurrentAlpha);
+ pw.print(" target="); pw.print(mDimTargetAlpha);
+ pw.print(" delta="); pw.print(mDimDeltaPerMs);
+ pw.print(" lastAnimTime="); pw.println(mLastDimAnimTime);
+ }
+ }
+
+ /**
+ * Animation that fade in after 0.5 interpolate time, or fade out in reverse order.
+ * This is used for opening/closing transition for apps in compatible mode.
+ */
+ private static class FadeInOutAnimation extends Animation {
+ int mWidth;
+ boolean mFadeIn;
+
+ public FadeInOutAnimation(boolean fadeIn) {
+ setInterpolator(new AccelerateInterpolator());
+ setDuration(DEFAULT_FADE_IN_OUT_DURATION);
+ mFadeIn = fadeIn;
+ }
+
+ @Override
+ protected void applyTransformation(float interpolatedTime, Transformation t) {
+ float x = interpolatedTime;
+ if (!mFadeIn) {
+ x = 1.0f - x; // reverse the interpolation for fade out
+ }
+ if (x < 0.5) {
+ // move the window out of the screen.
+ t.getMatrix().setTranslate(mWidth, 0);
+ } else {
+ t.getMatrix().setTranslate(0, 0);// show
+ t.setAlpha((x - 0.5f) * 2);
+ }
+ }
+
+ @Override
+ public void initialize(int width, int height, int parentWidth, int parentHeight) {
+ // width is the screen width {@see AppWindowToken#stepAnimatinoLocked}
+ mWidth = width;
+ }
+
+ @Override
+ public int getZAdjustment() {
+ return Animation.ZORDER_TOP;
+ }
+ }
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 25991f28ce05..e1ca2015fac7 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -3528,8 +3528,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
intent = new Intent(intent);
// Collect information about the target of the Intent.
- // Must do this before locking, because resolving the intent
- // may require launching a process to run its content provider.
ActivityInfo aInfo;
try {
ResolveInfo rInfo =
@@ -3663,17 +3661,24 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- final int startActivityInPackage(int uid,
+ public final int startActivityInPackage(int uid,
Intent intent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, boolean onlyIfNeeded) {
+
+ // This is so super not safe, that only the system (or okay root)
+ // can do it.
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != 0 && callingUid != Process.myUid()) {
+ throw new SecurityException(
+ "startActivityInPackage only available to the system");
+ }
+
final boolean componentSpecified = intent.getComponent() != null;
// Don't modify the client's object!
intent = new Intent(intent);
// Collect information about the target of the Intent.
- // Must do this before locking, because resolving the intent
- // may require launching a process to run its content provider.
ActivityInfo aInfo;
try {
ResolveInfo rInfo =
@@ -11876,10 +11881,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
config.reqTouchScreen = mConfiguration.touchscreen;
config.reqKeyboardType = mConfiguration.keyboard;
config.reqNavigation = mConfiguration.navigation;
- if (mConfiguration.navigation != Configuration.NAVIGATION_NONAV) {
+ if (mConfiguration.navigation == Configuration.NAVIGATION_DPAD
+ || mConfiguration.navigation == Configuration.NAVIGATION_TRACKBALL) {
config.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;
}
- if (mConfiguration.keyboard != Configuration.KEYBOARD_UNDEFINED) {
+ if (mConfiguration.keyboard != Configuration.KEYBOARD_UNDEFINED
+ && mConfiguration.keyboard != Configuration.KEYBOARD_NOKEYS) {
config.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;
}
}
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index 39a1ee050226..c834b34e3568 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -41,7 +41,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
final BatteryStatsImpl mStats;
Context mContext;
-
+
BatteryStatsService(String filename) {
mStats = new BatteryStatsImpl(filename);
}
diff --git a/services/jni/com_android_server_KeyInputQueue.cpp b/services/jni/com_android_server_KeyInputQueue.cpp
index 63830d59c24a..f27596c5c581 100644
--- a/services/jni/com_android_server_KeyInputQueue.cpp
+++ b/services/jni/com_android_server_KeyInputQueue.cpp
@@ -110,6 +110,23 @@ android_server_KeyInputQueue_getDeviceName(JNIEnv* env, jobject clazz,
return NULL;
}
+static void
+android_server_KeyInputQueue_addExcludedDevice(JNIEnv* env, jobject clazz,
+ jstring deviceName)
+{
+ gLock.lock();
+ sp<EventHub> hub = gHub;
+ if (hub == NULL) {
+ hub = new EventHub;
+ gHub = hub;
+ }
+ gLock.unlock();
+
+ const char* nameStr = env->GetStringUTFChars(deviceName, NULL);
+ gHub->addExcludedDevice(nameStr);
+ env->ReleaseStringUTFChars(deviceName, nameStr);
+}
+
static jboolean
android_server_KeyInputQueue_getAbsoluteInfo(JNIEnv* env, jobject clazz,
jint deviceId, jint axis,
@@ -205,6 +222,23 @@ android_server_KeyInputQueue_getKeycodeStateDevice(JNIEnv* env, jobject clazz,
return st;
}
+static jint
+android_server_KeyInputQueue_scancodeToKeycode(JNIEnv* env, jobject clazz,
+ jint deviceId, jint scancode)
+{
+ jint res = 0;
+ gLock.lock();
+ if (gHub != NULL) {
+ int32_t keycode;
+ uint32_t flags;
+ gHub->scancodeToKeycode(deviceId, scancode, &keycode, &flags);
+ res = keycode;
+ }
+ gLock.unlock();
+
+ return res;
+}
+
static jboolean
android_server_KeyInputQueue_hasKeys(JNIEnv* env, jobject clazz,
jintArray keyCodes, jbooleanArray outFlags)
@@ -238,6 +272,8 @@ static JNINativeMethod gInputMethods[] = {
(void*) android_server_KeyInputQueue_getDeviceClasses },
{ "getDeviceName", "(I)Ljava/lang/String;",
(void*) android_server_KeyInputQueue_getDeviceName },
+ { "addExcludedDevice", "(Ljava/lang/String;)V",
+ (void*) android_server_KeyInputQueue_addExcludedDevice },
{ "getAbsoluteInfo", "(IILcom/android/server/InputDevice$AbsoluteInfo;)Z",
(void*) android_server_KeyInputQueue_getAbsoluteInfo },
{ "getSwitchState", "(I)I",
@@ -254,6 +290,8 @@ static JNINativeMethod gInputMethods[] = {
(void*) android_server_KeyInputQueue_getKeycodeStateDevice },
{ "hasKeys", "([I[Z)Z",
(void*) android_server_KeyInputQueue_hasKeys },
+ { "scancodeToKeycode", "(II)I",
+ (void*) android_server_KeyInputQueue_scancodeToKeycode },
};
int register_android_server_KeyInputQueue(JNIEnv* env)
diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java
index 7f2b8496f592..0bb2df156c75 100644
--- a/telephony/java/com/android/internal/telephony/Phone.java
+++ b/telephony/java/com/android/internal/telephony/Phone.java
@@ -22,6 +22,7 @@ import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.telephony.CellLocation;
+import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
@@ -260,8 +261,8 @@ public interface Phone {
/**
* Get current coarse-grained voice call state.
- * Use {@link #registerForPhoneStateChanged(Handler, int, Object)
- * registerForPhoneStateChanged()} for change notification. <p>
+ * Use {@link #registerForPreciseCallStateChanged(Handler, int, Object)
+ * registerForPreciseCallStateChanged()} for change notification. <p>
* If the phone has an active call and call waiting occurs,
* then the phone state is RINGING not OFFHOOK
* <strong>Note:</strong>
@@ -315,18 +316,21 @@ public interface Phone {
void unregisterForUnknownConnection(Handler h);
/**
- * Notifies when any aspect of the voice call state changes.
+ * Register for getting notifications for change in the Call State {@link Call.State}
+ * This is called PreciseCallState because the call state is more precise than the
+ * {@link Phone.State} which can be obtained using the {@link PhoneStateListener}
+ *
* Resulting events will have an AsyncResult in <code>Message.obj</code>.
* AsyncResult.userData will be set to the obj argument here.
* The <em>h</em> parameter is held only by a weak reference.
*/
- void registerForPhoneStateChanged(Handler h, int what, Object obj);
+ void registerForPreciseCallStateChanged(Handler h, int what, Object obj);
/**
* Unregisters for voice call state change notifications.
* Extraneous calls are tolerated silently.
*/
- void unregisterForPhoneStateChanged(Handler h);
+ void unregisterForPreciseCallStateChanged(Handler h);
/**
@@ -556,8 +560,8 @@ public interface Phone {
/**
* Answers a ringing or waiting call. Active calls, if any, go on hold.
* Answering occurs asynchronously, and final notification occurs via
- * {@link #registerForPhoneStateChanged(android.os.Handler, int,
- * java.lang.Object) registerForPhoneStateChanged()}.
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
*
* @exception CallStateException when no call is ringing or waiting
*/
@@ -567,8 +571,8 @@ public interface Phone {
* Reject (ignore) a ringing call. In GSM, this means UDUB
* (User Determined User Busy). Reject occurs asynchronously,
* and final notification occurs via
- * {@link #registerForPhoneStateChanged(android.os.Handler, int,
- * java.lang.Object) registerForPhoneStateChanged()}.
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
*
* @exception CallStateException when no call is ringing or waiting
*/
@@ -578,8 +582,8 @@ public interface Phone {
* Places any active calls on hold, and makes any held calls
* active. Switch occurs asynchronously and may fail.
* Final notification occurs via
- * {@link #registerForPhoneStateChanged(android.os.Handler, int,
- * java.lang.Object) registerForPhoneStateChanged()}.
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
*
* @exception CallStateException if a call is ringing, waiting, or
* dialing/alerting. In these cases, this operation may not be performed.
@@ -596,8 +600,8 @@ public interface Phone {
/**
* Conferences holding and active. Conference occurs asynchronously
* and may fail. Final notification occurs via
- * {@link #registerForPhoneStateChanged(android.os.Handler, int,
- * java.lang.Object) registerForPhoneStateChanged()}.
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
*
* @exception CallStateException if canConference() would return false.
* In these cases, this operation may not be performed.
@@ -631,8 +635,8 @@ public interface Phone {
* Connects the two calls and disconnects the subscriber from both calls
* Explicit Call Transfer occurs asynchronously
* and may fail. Final notification occurs via
- * {@link #registerForPhoneStateChanged(android.os.Handler, int,
- * java.lang.Object) registerForPhoneStateChanged()}.
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
*
* @exception CallStateException if canTransfer() would return false.
* In these cases, this operation may not be performed.
@@ -659,8 +663,8 @@ public interface Phone {
* IDLE, ACTIVE, DIALING, ALERTING, or DISCONNECTED.
*
* State change notification is available via
- * {@link #registerForPhoneStateChanged(android.os.Handler, int,
- * java.lang.Object) registerForPhoneStateChanged()}.
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
*/
Call getForegroundCall();
@@ -676,8 +680,8 @@ public interface Phone {
* IDLE, HOLDING or DISCONNECTED.
*
* State change notification is available via
- * {@link #registerForPhoneStateChanged(android.os.Handler, int,
- * java.lang.Object) registerForPhoneStateChanged()}.
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
*/
Call getBackgroundCall();
@@ -693,8 +697,8 @@ public interface Phone {
* IDLE, INCOMING, WAITING or DISCONNECTED.
*
* State change notification is available via
- * {@link #registerForPhoneStateChanged(android.os.Handler, int,
- * java.lang.Object) registerForPhoneStateChanged()}.
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
*/
Call getRingingCall();
@@ -1067,8 +1071,8 @@ public interface Phone {
/**
* Gets current mute status. Use
- * {@link #registerForPhoneStateChanged(android.os.Handler, int,
- * java.lang.Object) registerForPhoneStateChanged()}
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}
* as a change notifcation, although presently phone state changed is not
* fired when setMute() is called.
*
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index 1509a6bcb1e3..fbda221ddbbb 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -119,7 +119,7 @@ public abstract class PhoneBase implements Phone {
}
- protected final RegistrantList mPhoneStateRegistrants
+ protected final RegistrantList mPreciseCallStateRegistrants
= new RegistrantList();
protected final RegistrantList mNewRingingConnectionRegistrants
@@ -221,25 +221,24 @@ public abstract class PhoneBase implements Phone {
}
// Inherited documentation suffices.
- public void registerForPhoneStateChanged(Handler h, int what, Object obj) {
+ public void registerForPreciseCallStateChanged(Handler h, int what, Object obj) {
checkCorrectThread(h);
- mPhoneStateRegistrants.addUnique(h, what, obj);
+ mPreciseCallStateRegistrants.addUnique(h, what, obj);
}
// Inherited documentation suffices.
- public void unregisterForPhoneStateChanged(Handler h) {
- mPhoneStateRegistrants.remove(h);
+ public void unregisterForPreciseCallStateChanged(Handler h) {
+ mPreciseCallStateRegistrants.remove(h);
}
/**
- * Notify registrants of a PhoneStateChanged.
* Subclasses of Phone probably want to replace this with a
* version scoped to their packages
*/
- protected void notifyCallStateChangedP() {
+ protected void notifyPreciseCallStateChangedP() {
AsyncResult ar = new AsyncResult(null, this, null);
- mPhoneStateRegistrants.notifyRegistrants(ar);
+ mPreciseCallStateRegistrants.notifyRegistrants(ar);
}
// Inherited documentation suffices.
diff --git a/telephony/java/com/android/internal/telephony/PhoneProxy.java b/telephony/java/com/android/internal/telephony/PhoneProxy.java
index da002683a895..979f0cdca574 100644
--- a/telephony/java/com/android/internal/telephony/PhoneProxy.java
+++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java
@@ -25,6 +25,7 @@ import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.telephony.CellLocation;
+import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.util.Log;
@@ -210,12 +211,12 @@ public class PhoneProxy extends Handler implements Phone {
mActivePhone.unregisterForUnknownConnection(h);
}
- public void registerForPhoneStateChanged(Handler h, int what, Object obj) {
- mActivePhone.registerForPhoneStateChanged(h, what, obj);
+ public void registerForPreciseCallStateChanged(Handler h, int what, Object obj) {
+ mActivePhone.registerForPreciseCallStateChanged(h, what, obj);
}
- public void unregisterForPhoneStateChanged(Handler h) {
- mActivePhone.unregisterForPhoneStateChanged(h);
+ public void unregisterForPreciseCallStateChanged(Handler h) {
+ mActivePhone.unregisterForPreciseCallStateChanged(h);
}
public void registerForNewRingingConnection(Handler h, int what, Object obj) {
diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
index 5ec4020d6fdc..8a1e928ad136 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
@@ -109,4 +109,10 @@ public interface TelephonyProperties
/** The international dialing prefix conversion string */
static final String PROPERTY_IDP_STRING = "ro.cdma.idpstring";
+
+ /**
+ * Defines the schema for the carrier specified OTASP number
+ */
+ static final String PROPERTY_OTASP_NUM_SCHEMA = "ro.cdma.otaspnumschema";
+
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
index 0918a8c1437a..bdcea92089ce 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -18,17 +18,23 @@ package com.android.internal.telephony.cdma;
import android.app.ActivityManagerNative;
import android.content.Context;
+import android.content.ContentValues;
import android.content.Intent;
+import android.content.res.Configuration;
import android.content.SharedPreferences;
+import android.database.SQLException;
+import android.net.Uri;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Registrant;
import android.os.RegistrantList;
+import android.os.RemoteException;
import android.os.SystemProperties;
import android.preference.PreferenceManager;
import android.provider.Settings;
+import android.provider.Telephony;
import android.telephony.CellLocation;
import android.telephony.PhoneNumberUtils;
import android.telephony.ServiceState;
@@ -36,11 +42,16 @@ import android.telephony.SignalStrength;
import android.text.TextUtils;
import android.util.Log;
+import com.android.internal.telephony.Call;
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.DataConnection;
+// TODO(Moto): need to move MccTable from telephony.gsm to telephony
+// since there is no difference between CDMA and GSM for MccTable and
+// CDMA uses gsm's MccTable is not good.
+import com.android.internal.telephony.gsm.MccTable;
import com.android.internal.telephony.IccCard;
import com.android.internal.telephony.IccException;
import com.android.internal.telephony.IccFileHandler;
@@ -56,15 +67,23 @@ import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.TelephonyProperties;
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA;
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC;
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY;
+
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
/**
* {@hide}
*/
public class CDMAPhone extends PhoneBase {
static final String LOG_TAG = "CDMA";
- private static final boolean LOCAL_DEBUG = true;
+ private static final boolean DBG = true;
// Default Emergency Callback Mode exit timer
private static final int DEFAULT_ECM_EXIT_TIMER_VALUE = 300000;
@@ -99,6 +118,8 @@ public class CDMAPhone extends PhoneBase {
private Registrant mECMExitRespRegistrant;
private String mEsn;
private String mMeid;
+ // string to define how the carrier specifies its own ota sp number
+ private String mCarrierOtaSpNumSchema;
// A runnable which is used to automatically exit from ECM after a period of time.
private Runnable mExitEcmRunnable = new Runnable() {
@@ -154,6 +175,27 @@ public class CDMAPhone extends PhoneBase {
String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false");
mIsPhoneInECMState = inEcm.equals("true");
+ // get the string that specifies the carrier OTA Sp number
+ mCarrierOtaSpNumSchema = SystemProperties.get(
+ TelephonyProperties.PROPERTY_OTASP_NUM_SCHEMA,"");
+
+ // Sets operator alpha property by retrieving from build-time system property
+ String operatorAlpha = SystemProperties.get("ro.cdma.home.operator.alpha");
+ setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, operatorAlpha);
+
+ // Sets operator numeric property by retrieving from build-time system property
+ String operatorNumeric = SystemProperties.get("ro.cdma.home.operator.numeric");
+ setSystemProperty(PROPERTY_ICC_OPERATOR_NUMERIC, operatorNumeric);
+
+ // Sets iso country property by retrieving from build-time system property
+ setIsoCountryProperty(operatorNumeric);
+
+ // Sets current entry in the telephony carrier table
+ updateCurrentCarrierInProvider(operatorNumeric);
+
+ // Updates MCC MNC device configuration information
+ updateMccMncConfiguration(operatorNumeric);
+
// Notify voicemails.
notifier.notifyMessageWaitingChanged(this);
}
@@ -202,7 +244,7 @@ public class CDMAPhone extends PhoneBase {
}
protected void finalize() {
- if(LOCAL_DEBUG) Log.d(LOG_TAG, "CDMAPhone finalized");
+ if(DBG) Log.d(LOG_TAG, "CDMAPhone finalized");
}
@@ -427,13 +469,7 @@ public class CDMAPhone extends PhoneBase {
}
public String getSubscriberId() {
- // Subscriber ID is the combination of MCC+MNC+MIN as CDMA IMSI
- // TODO(Moto): Replace with call to mRuimRecords.getIMSI_M() when implemented.
- if ((getServiceState().getOperatorNumeric() != null) && (getCdmaMin() != null)) {
- return (getServiceState().getOperatorNumeric() + getCdmaMin());
- } else {
- return null;
- }
+ return mSST.getImsi();
}
public boolean canConference() {
@@ -783,19 +819,19 @@ public class CDMAPhone extends PhoneBase {
}
/**
- * Notify any interested party of a Phone state change.
+ * Notify any interested party of a Phone state change {@link Phone.State}
*/
/*package*/ void notifyPhoneStateChanged() {
mNotifier.notifyPhoneState(this);
}
/**
- * Notifies registrants (ie, activities in the Phone app) about
- * changes to call state (including Phone and Connection changes).
+ * Notify registrants of a change in the call state. This notifies changes in {@link Call.State}
+ * Use this when changes in the precise call state are needed, else use notifyPhoneStateChanged.
*/
- /*package*/ void notifyCallStateChanged() {
+ /*package*/ void notifyPreciseCallStateChanged() {
/* we'd love it if this was package-scoped*/
- super.notifyCallStateChangedP();
+ super.notifyPreciseCallStateChangedP();
}
void notifyServiceStateChanged(ServiceState ss) {
@@ -934,7 +970,7 @@ public class CDMAPhone extends PhoneBase {
break;
}
- if (LOCAL_DEBUG) Log.d(LOG_TAG, "Baseband version: " + ar.result);
+ if (DBG) Log.d(LOG_TAG, "Baseband version: " + ar.result);
setSystemProperty(TelephonyProperties.PROPERTY_BASEBAND_VERSION, (String)ar.result);
}
break;
@@ -1128,10 +1164,10 @@ public class CDMAPhone extends PhoneBase {
mSMS.setCellBroadcastConfig(configValuesArray, response);
}
- public static final String IS683A_FEATURE_CODE = "*228" ;
- public static final int IS683A_FEATURE_CODE_NUM_DIGITS = 4 ;
- public static final int IS683A_SYS_SEL_CODE_NUM_DIGITS = 2 ;
- public static final int IS683A_SYS_SEL_CODE_OFFSET = 4;
+ private static final String IS683A_FEATURE_CODE = "*228";
+ private static final int IS683A_FEATURE_CODE_NUM_DIGITS = 4;
+ private static final int IS683A_SYS_SEL_CODE_NUM_DIGITS = 2;
+ private static final int IS683A_SYS_SEL_CODE_OFFSET = 4;
private static final int IS683_CONST_800MHZ_A_BAND = 0;
private static final int IS683_CONST_800MHZ_B_BAND = 1;
@@ -1141,6 +1177,7 @@ public class CDMAPhone extends PhoneBase {
private static final int IS683_CONST_1900MHZ_D_BLOCK = 5;
private static final int IS683_CONST_1900MHZ_E_BLOCK = 6;
private static final int IS683_CONST_1900MHZ_F_BLOCK = 7;
+ private static final int INVALID_SYSTEM_SELECTION_CODE = -1;
private boolean isIs683OtaSpDialStr(String dialStr) {
int sysSelCodeInt;
@@ -1151,38 +1188,146 @@ public class CDMAPhone extends PhoneBase {
if (dialStr.equals(IS683A_FEATURE_CODE)) {
isOtaspDialString = true;
}
- } else if ((dialStr.regionMatches(0, IS683A_FEATURE_CODE, 0,
- IS683A_FEATURE_CODE_NUM_DIGITS) == true)
- && (dialStrLen >=
- (IS683A_FEATURE_CODE_NUM_DIGITS + IS683A_SYS_SEL_CODE_NUM_DIGITS))) {
- StringBuilder sb = new StringBuilder(dialStr);
- // Separate the System Selection Code into its own string
- char[] sysSel = new char[2];
- sb.delete(0, IS683A_SYS_SEL_CODE_OFFSET);
- sb.getChars(0, IS683A_SYS_SEL_CODE_NUM_DIGITS, sysSel, 0);
-
- if ((PhoneNumberUtils.isISODigit(sysSel[0]))
- && (PhoneNumberUtils.isISODigit(sysSel[1]))) {
- String sysSelCode = new String(sysSel);
- sysSelCodeInt = Integer.parseInt((String)sysSelCode);
- switch (sysSelCodeInt) {
- case IS683_CONST_800MHZ_A_BAND:
- case IS683_CONST_800MHZ_B_BAND:
- case IS683_CONST_1900MHZ_A_BLOCK:
- case IS683_CONST_1900MHZ_B_BLOCK:
- case IS683_CONST_1900MHZ_C_BLOCK:
- case IS683_CONST_1900MHZ_D_BLOCK:
- case IS683_CONST_1900MHZ_E_BLOCK:
- case IS683_CONST_1900MHZ_F_BLOCK:
- isOtaspDialString = true;
- break;
+ } else {
+ sysSelCodeInt = extractSelCodeFromOtaSpNum(dialStr);
+ switch (sysSelCodeInt) {
+ case IS683_CONST_800MHZ_A_BAND:
+ case IS683_CONST_800MHZ_B_BAND:
+ case IS683_CONST_1900MHZ_A_BLOCK:
+ case IS683_CONST_1900MHZ_B_BLOCK:
+ case IS683_CONST_1900MHZ_C_BLOCK:
+ case IS683_CONST_1900MHZ_D_BLOCK:
+ case IS683_CONST_1900MHZ_E_BLOCK:
+ case IS683_CONST_1900MHZ_F_BLOCK:
+ isOtaspDialString = true;
+ break;
+ default:
+ break;
+ }
+ }
+ return isOtaspDialString;
+ }
+ /**
+ * This function extracts the system selection code from the dial string.
+ */
+ private int extractSelCodeFromOtaSpNum(String dialStr) {
+ int dialStrLen = dialStr.length();
+ int sysSelCodeInt = INVALID_SYSTEM_SELECTION_CODE;
+
+ if ((dialStr.regionMatches(0, IS683A_FEATURE_CODE,
+ 0, IS683A_FEATURE_CODE_NUM_DIGITS)) &&
+ (dialStrLen >= (IS683A_FEATURE_CODE_NUM_DIGITS +
+ IS683A_SYS_SEL_CODE_NUM_DIGITS))) {
+ // Since we checked the condition above, the system selection code
+ // extracted from dialStr will not cause any exception
+ sysSelCodeInt = Integer.parseInt (
+ dialStr.substring (IS683A_FEATURE_CODE_NUM_DIGITS,
+ IS683A_FEATURE_CODE_NUM_DIGITS + IS683A_SYS_SEL_CODE_NUM_DIGITS));
+ }
+ if (DBG) Log.d(LOG_TAG, "extractSelCodeFromOtaSpNum " + sysSelCodeInt);
+ return sysSelCodeInt;
+ }
- default:
+ /**
+ * This function checks if the system selection code extracted from
+ * the dial string "sysSelCodeInt' is the system selection code specified
+ * in the carrier ota sp number schema "sch".
+ */
+ private boolean
+ checkOtaSpNumBasedOnSysSelCode (int sysSelCodeInt, String sch[]) {
+ boolean isOtaSpNum = false;
+ try {
+ // Get how many number of system selection code ranges
+ int selRc = Integer.parseInt((String)sch[1]);
+ for (int i = 0; i < selRc; i++) {
+ if (!TextUtils.isEmpty(sch[i+2]) && !TextUtils.isEmpty(sch[i+3])) {
+ int selMin = Integer.parseInt((String)sch[i+2]);
+ int selMax = Integer.parseInt((String)sch[i+3]);
+ // Check if the selection code extracted from the dial string falls
+ // within any of the range pairs specified in the schema.
+ if ((sysSelCodeInt >= selMin) && (sysSelCodeInt <= selMax)) {
+ isOtaSpNum = true;
break;
+ }
}
}
+ } catch (NumberFormatException ex) {
+ // If the carrier ota sp number schema is not correct, we still allow dial
+ // and only log the error:
+ Log.e(LOG_TAG, "checkOtaSpNumBasedOnSysSelCode, error", ex);
}
- return isOtaspDialString;
+ return isOtaSpNum;
+ }
+
+ // Define the pattern/format for carrier specified OTASP number schema.
+ // It separates by comma and/or whitespace.
+ private static Pattern pOtaSpNumSchema = Pattern.compile("[,\\s]+");
+
+ /**
+ * The following function checks if a dial string is a carrier specified
+ * OTASP number or not by checking against the OTASP number schema stored
+ * in PROPERTY_OTASP_NUM_SCHEMA.
+ *
+ * Currently, there are 2 schemas for carriers to specify the OTASP number:
+ * 1) Use system selection code:
+ * The schema is:
+ * SELC,the # of code pairs,min1,max1,min2,max2,...
+ * e.g "SELC,3,10,20,30,40,60,70" indicates that there are 3 pairs of
+ * selection codes, and they are {10,20}, {30,40} and {60,70} respectively.
+ *
+ * 2) Use feature code:
+ * The schema is:
+ * "FC,length of feature code,feature code".
+ * e.g "FC,2,*2" indicates that the length of the feature code is 2,
+ * and the code itself is "*2".
+ */
+ private boolean isCarrierOtaSpNum(String dialStr) {
+ boolean isOtaSpNum = false;
+ int sysSelCodeInt = extractSelCodeFromOtaSpNum(dialStr);
+ if (sysSelCodeInt == INVALID_SYSTEM_SELECTION_CODE) {
+ return isOtaSpNum;
+ }
+ // mCarrierOtaSpNumSchema is retrieved from PROPERTY_OTASP_NUM_SCHEMA:
+ if (!TextUtils.isEmpty(mCarrierOtaSpNumSchema)) {
+ Matcher m = pOtaSpNumSchema.matcher(mCarrierOtaSpNumSchema);
+ if (DBG) {
+ Log.d(LOG_TAG, "isCarrierOtaSpNum,schema" + mCarrierOtaSpNumSchema);
+ }
+
+ if (m.find()) {
+ String sch[] = pOtaSpNumSchema.split(mCarrierOtaSpNumSchema);
+ // If carrier uses system selection code mechanism
+ if (!TextUtils.isEmpty(sch[0]) && sch[0].equals("SELC")) {
+ if (sysSelCodeInt!=INVALID_SYSTEM_SELECTION_CODE) {
+ isOtaSpNum=checkOtaSpNumBasedOnSysSelCode(sysSelCodeInt,sch);
+ } else {
+ if (DBG) {
+ Log.d(LOG_TAG, "isCarrierOtaSpNum,sysSelCodeInt is invalid");
+ }
+ }
+ } else if (!TextUtils.isEmpty(sch[0]) && sch[0].equals("FC")) {
+ int fcLen = Integer.parseInt((String)sch[1]);
+ String fc = (String)sch[2];
+ if (dialStr.regionMatches(0,fc,0,fcLen)) {
+ isOtaSpNum = true;
+ } else {
+ if (DBG) Log.d(LOG_TAG, "isCarrierOtaSpNum,not otasp number");
+ }
+ } else {
+ if (DBG) {
+ Log.d(LOG_TAG, "isCarrierOtaSpNum,ota schema not supported" + sch[0]);
+ }
+ }
+ } else {
+ if (DBG) {
+ Log.d(LOG_TAG, "isCarrierOtaSpNum,ota schema pattern not right" +
+ mCarrierOtaSpNumSchema);
+ }
+ }
+ } else {
+ if (DBG) Log.d(LOG_TAG, "isCarrierOtaSpNum,ota schema pattern empty");
+ }
+ return isOtaSpNum;
}
/**
@@ -1195,12 +1340,13 @@ public class CDMAPhone extends PhoneBase {
@Override
public boolean isOtaSpNumber(String dialStr){
boolean isOtaSpNum = false;
- if(dialStr != null){
- isOtaSpNum=isIs683OtaSpDialStr(dialStr);
+ if (dialStr != null) {
+ isOtaSpNum = isIs683OtaSpDialStr(dialStr);
if(isOtaSpNum == false){
- //TO DO:Add carrier specific OTASP number detection here.
+ isOtaSpNum = isCarrierOtaSpNum(dialStr);
}
}
+ if (DBG) Log.d(LOG_TAG, "isOtaSpNumber " + isOtaSpNum);
return isOtaSpNum;
}
@@ -1244,4 +1390,66 @@ public class CDMAPhone extends PhoneBase {
editor.commit();
}
+ /**
+ * Sets PROPERTY_ICC_OPERATOR_ISO_COUNTRY property
+ *
+ */
+ private void setIsoCountryProperty(String operatorNumeric) {
+ if (TextUtils.isEmpty(operatorNumeric)) {
+ setSystemProperty(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, "");
+ } else {
+ String iso = "";
+ try {
+ iso = MccTable.countryCodeForMcc(Integer.parseInt(
+ operatorNumeric.substring(0,3)));
+ } catch (NumberFormatException ex) {
+ Log.w(LOG_TAG, "countryCodeForMcc error" + ex);
+ } catch (StringIndexOutOfBoundsException ex) {
+ Log.w(LOG_TAG, "countryCodeForMcc error" + ex);
+ }
+
+ setSystemProperty(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, iso);
+ }
+ }
+
+ /**
+ * Sets the "current" field in the telephony provider according to the build-time
+ * operator numeric property
+ *
+ * @return true for success; false otherwise.
+ */
+ // TODO(Moto): move this method into PhoneBase, since it looks identical to
+ // the one in GsmPhone
+ private boolean updateCurrentCarrierInProvider(String operatorNumeric) {
+ if (!TextUtils.isEmpty(operatorNumeric)) {
+ try {
+ Uri uri = Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "current");
+ ContentValues map = new ContentValues();
+ map.put(Telephony.Carriers.NUMERIC, operatorNumeric);
+ getContext().getContentResolver().insert(uri, map);
+ return true;
+ } catch (SQLException e) {
+ Log.e(LOG_TAG, "Can't store current operator", e);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Updates MCC and MNC device configuration information for application retrieving
+ * correct version of resources
+ *
+ */
+ private void updateMccMncConfiguration(String operatorNumeric) {
+ if (operatorNumeric.length() >= 5) {
+ Configuration config = new Configuration();
+ config.mcc = Integer.parseInt(operatorNumeric.substring(0,3));
+ config.mnc = Integer.parseInt(operatorNumeric.substring(3));
+ try {
+ ActivityManagerNative.getDefault().updateConfiguration(config);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Can't update configuration", e);
+ }
+ }
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
index ed2ea90d3f23..cc456c5f5b19 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
@@ -222,7 +222,7 @@ public final class CdmaCallTracker extends CallTracker {
}
updatePhoneState();
- phone.notifyCallStateChanged();
+ phone.notifyPreciseCallStateChanged();
return pendingMO;
}
@@ -262,6 +262,7 @@ public final class CdmaCallTracker extends CallTracker {
// triggered by updateParent.
cwConn.updateParent(ringingCall, foregroundCall);
cwConn.onConnectedInOrOut();
+ updatePhoneState();
switchWaitingOrHoldingAndActive();
} else {
throw new CallStateException("phone not ringing");
@@ -305,7 +306,7 @@ public final class CdmaCallTracker extends CallTracker {
internalClearDisconnected();
updatePhoneState();
- phone.notifyCallStateChanged();
+ phone.notifyPreciseCallStateChanged();
}
boolean
@@ -531,17 +532,6 @@ public final class CdmaCallTracker extends CallTracker {
// Dropped connections are removed from the CallTracker
// list but kept in the Call list
connections[i] = null;
- } else if (conn != null && dc != null && !conn.compareTo(dc)) {
- // Connection in CLCC response does not match what
- // we were tracking. Assume dropped call and new call
-
- droppedDuringPoll.add(conn);
- connections[i] = new CdmaConnection (phone.getContext(), dc, this, i);
-
- if (connections[i].getCall() == ringingCall) {
- newRinging = connections[i];
- } // else something strange happened
- hasNonHangupStateChanged = true;
} else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */
boolean changed;
changed = conn.update(dc);
@@ -644,7 +634,7 @@ public final class CdmaCallTracker extends CallTracker {
}
if (hasNonHangupStateChanged || newRinging != null) {
- phone.notifyCallStateChanged();
+ phone.notifyPreciseCallStateChanged();
}
//dumpState();
@@ -678,7 +668,8 @@ public final class CdmaCallTracker extends CallTracker {
// the hangup reason is user ignoring or timing out. So conn.onDisconnect()
// is not called here. Instead, conn.onLocalDisconnect() is called.
conn.onLocalDisconnect();
- phone.notifyCallStateChanged();
+ phone.notifyPreciseCallStateChanged();
+ updatePhoneState();
return;
} else {
try {
@@ -821,7 +812,7 @@ public final class CdmaCallTracker extends CallTracker {
// the status of the call is after a call waiting is answered,
// 3 way call merged or a switch between calls.
foregroundCall.setGeneric(true);
- phone.notifyCallStateChanged();
+ phone.notifyPreciseCallStateChanged();
}
private Phone.SuppService getFailedService(int what) {
@@ -865,6 +856,7 @@ public final class CdmaCallTracker extends CallTracker {
// Create a new CdmaConnection which attaches itself to ringingCall.
ringingCall.setGeneric(false);
new CdmaConnection(phone.getContext(), cw, this, ringingCall);
+ updatePhoneState();
// Finally notify application
notifyCallWaitingInfo(cw);
@@ -926,7 +918,7 @@ public final class CdmaCallTracker extends CallTracker {
updatePhoneState();
- phone.notifyCallStateChanged();
+ phone.notifyPreciseCallStateChanged();
droppedDuringPoll.clear();
break;
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
index 92492b4ef893..e78570988f59 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
@@ -19,11 +19,8 @@ package com.android.internal.telephony.cdma;
import android.app.AlarmManager;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.ContentValues;
import android.content.Intent;
import android.database.ContentObserver;
-import android.database.SQLException;
-import android.net.Uri;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Message;
@@ -35,7 +32,6 @@ import android.os.SystemProperties;
import android.provider.Checkin;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
-import android.provider.Telephony;
import android.provider.Telephony.Intents;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
@@ -64,6 +60,7 @@ import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERAT
import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISROAMING;
import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_NUMERIC;
import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA;
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC;
import java.util.Arrays;
import java.util.Date;
@@ -401,8 +398,15 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
String cdmaSubscription[] = (String[])ar.result;
if (cdmaSubscription != null && cdmaSubscription.length >= 5) {
mMdn = cdmaSubscription[0];
- mHomeSystemId = Integer.parseInt(cdmaSubscription[1], 16);
- mHomeNetworkId = Integer.parseInt(cdmaSubscription[2], 16);
+ // TODO: Only grabbing the first SID/NID for now.
+ if (cdmaSubscription[1] != null) {
+ String[] sid = cdmaSubscription[1].split(",");
+ mHomeSystemId = sid.length > 0 ? Integer.parseInt(sid[0]) : 0;
+ }
+ if (cdmaSubscription[2] != null) {
+ String[] nid = cdmaSubscription[2].split(",");
+ mHomeNetworkId = nid.length > 0 ? Integer.parseInt(nid[0]) : 0;
+ }
mMin = cdmaSubscription[3];
mPrlVersion = cdmaSubscription[4];
Log.d(LOG_TAG,"GET_CDMA_SUBSCRIPTION MDN=" + mMdn);
@@ -679,27 +683,6 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
} else {
newSS.setOperatorName(opNames[0], opNames[1], opNames[2]);
}
-
- if (!(opNames[2].equals(currentCarrier))) {
- // TODO(Moto): jsh asks, "This uses the MCC+MNC of the current registered
- // network to set the "current" entry in the APN table. But the correct
- // entry should be the MCC+MNC that matches the subscribed operator
- // (eg, phone issuer). These can be different when roaming."
- try {
- // Set the current field of the telephony provider according to
- // the CDMA's operator
- Uri uri = Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "current");
- ContentValues map = new ContentValues();
- map.put(Telephony.Carriers.NUMERIC, opNames[2]);
- cr.insert(uri, map);
- // save current carrier for the next time check
- currentCarrier = opNames[2];
- } catch (SQLException e) {
- Log.e(LOG_TAG, "Can't store current operator", e);
- }
- } else {
- Log.i(LOG_TAG, "current carrier is not changed");
- }
} else {
Log.w(LOG_TAG, "error parsing opNames");
}
@@ -1495,4 +1478,19 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
return mPrlVersion;
}
+ /**
+ * Returns IMSI as MCC + MNC + MIN
+ */
+ /*package*/ String getImsi() {
+ // TODO(Moto): When RUIM is enabled, IMSI will come from RUIM
+ // not build-time props. Moto will provide implementation
+ // for RUIM-ready case later.
+ String operatorNumeric = SystemProperties.get(PROPERTY_ICC_OPERATOR_NUMERIC, "");
+
+ if (!TextUtils.isEmpty(operatorNumeric) && getCdmaMin() != null) {
+ return (operatorNumeric + getCdmaMin());
+ } else {
+ return null;
+ }
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java b/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java
index 7e50cc54d6cf..7c743141e07a 100644
--- a/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java
+++ b/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java
@@ -51,8 +51,6 @@ public final class RuimRecords extends IccRecords {
private String mImsi;
private String mMyMobileNumber;
- private String mSid;
- private String mNid;
private String mMin2Min1;
private String mPrlVersion;
@@ -116,21 +114,12 @@ public final class RuimRecords extends IccRecords {
adnCache.reset();
- phone.setSystemProperty(TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC, null);
- phone.setSystemProperty(TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY, null);
-
// recordsRequested is set to false indicating that the SIM
// read requests made so far are not valid. This is set to
// true only when fresh set of read requests are made.
recordsRequested = false;
}
- /** Returns null if RUIM is not yet ready */
- public String getIMSI_M() {
- // TODO(Moto): mImsi is not initialized, fix.
- return mImsi;
- }
-
public String getMdnNumber() {
return mMyMobileNumber;
}
@@ -223,8 +212,6 @@ public final class RuimRecords extends IccRecords {
}
mMyMobileNumber = localTemp[0];
- mSid = localTemp[1];
- mNid = localTemp[2];
mMin2Min1 = localTemp[3];
mPrlVersion = localTemp[4];
diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
index d1e4b4f21cab..ebbf096e14dc 100755
--- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
@@ -51,6 +51,7 @@ import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDI
import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
import static com.android.internal.telephony.TelephonyProperties.PROPERTY_BASEBAND_VERSION;
+import com.android.internal.telephony.Call;
import com.android.internal.telephony.CallForwardInfo;
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.CommandsInterface;
@@ -378,20 +379,19 @@ public class GSMPhone extends PhoneBase {
}
/**
- * Notify any interested party of a Phone state change.
+ * Notify any interested party of a Phone state change {@link Phone.State}
*/
/*package*/ void notifyPhoneStateChanged() {
mNotifier.notifyPhoneState(this);
}
/**
- * Notifies registrants (ie, activities in the Phone app) about
- * changes to call state (including Phone and Connection changes).
+ * Notify registrants of a change in the call state. This notifies changes in {@link Call.State}
+ * Use this when changes in the precise call state are needed, else use notifyPhoneStateChanged.
*/
- /*package*/ void
- notifyCallStateChanged() {
+ /*package*/ void notifyPreciseCallStateChanged() {
/* we'd love it if this was package-scoped*/
- super.notifyCallStateChangedP();
+ super.notifyPreciseCallStateChangedP();
}
/*package*/ void
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmCallTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmCallTracker.java
index 5c5090ffe911..f3b7596f6bd3 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmCallTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmCallTracker.java
@@ -212,7 +212,7 @@ public final class GsmCallTracker extends CallTracker {
}
updatePhoneState();
- phone.notifyCallStateChanged();
+ phone.notifyPreciseCallStateChanged();
return pendingMO;
}
@@ -279,7 +279,7 @@ public final class GsmCallTracker extends CallTracker {
internalClearDisconnected();
updatePhoneState();
- phone.notifyCallStateChanged();
+ phone.notifyPreciseCallStateChanged();
}
boolean
@@ -600,7 +600,7 @@ public final class GsmCallTracker extends CallTracker {
}
if (hasNonHangupStateChanged || newRinging != null) {
- phone.notifyCallStateChanged();
+ phone.notifyPreciseCallStateChanged();
}
//dumpState();
@@ -883,7 +883,7 @@ public final class GsmCallTracker extends CallTracker {
updatePhoneState();
- phone.notifyCallStateChanged();
+ phone.notifyPreciseCallStateChanged();
droppedDuringPoll.clear();
break;
diff --git a/tests/AndroidTests/AndroidManifest.xml b/tests/AndroidTests/AndroidManifest.xml
index 55d4d64bf547..845f54749641 100644
--- a/tests/AndroidTests/AndroidManifest.xml
+++ b/tests/AndroidTests/AndroidManifest.xml
@@ -48,6 +48,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_GSERVICES" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="com.android.unit_tests.permission.TEST_GRANTED" />
diff --git a/tests/AndroidTests/res/raw/v21_simple_1.vcf b/tests/AndroidTests/res/raw/v21_simple_1.vcf
new file mode 100644
index 000000000000..6aabb4c023e0
--- /dev/null
+++ b/tests/AndroidTests/res/raw/v21_simple_1.vcf
@@ -0,0 +1,3 @@
+BEGIN:VCARD
+N:Ando;Roid;
+END:VCARD
diff --git a/tests/AndroidTests/res/raw/v21_simple_2.vcf b/tests/AndroidTests/res/raw/v21_simple_2.vcf
new file mode 100644
index 000000000000..f0d5ab506ae6
--- /dev/null
+++ b/tests/AndroidTests/res/raw/v21_simple_2.vcf
@@ -0,0 +1,3 @@
+BEGIN:VCARD
+FN:Ando Roid
+END:VCARD
diff --git a/tests/AndroidTests/res/raw/v21_simple.vcf b/tests/AndroidTests/res/raw/v21_simple_3.vcf
index beddabb96086..beddabb96086 100644
--- a/tests/AndroidTests/res/raw/v21_simple.vcf
+++ b/tests/AndroidTests/res/raw/v21_simple_3.vcf
diff --git a/tests/AndroidTests/src/com/android/unit_tests/HtmlTest.java b/tests/AndroidTests/src/com/android/unit_tests/HtmlTest.java
index 27da4f1c72ae..027730fa3f43 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/HtmlTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/HtmlTest.java
@@ -16,11 +16,25 @@
package com.android.unit_tests;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
import android.graphics.Typeface;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
-import android.text.*;
-import android.text.style.*;
+import android.text.Html;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.QuoteSpan;
+import android.text.style.StrikethroughSpan;
+import android.text.style.StyleSpan;
+import android.text.style.SubscriptSpan;
+import android.text.style.SuperscriptSpan;
+import android.text.style.TextAppearanceSpan;
+import android.text.style.TypefaceSpan;
+import android.text.style.URLSpan;
+import android.text.style.UnderlineSpan;
import junit.framework.TestCase;
@@ -35,14 +49,54 @@ public class HtmlTest extends TestCase {
s = Html.fromHtml("<font color=\"#00FF00\">something</font>");
colors = s.getSpans(0, s.length(), ForegroundColorSpan.class);
- assertEquals(colors[0].getForegroundColor(), 0xFF00FF00);
+ assertEquals(1, colors.length);
+ assertEquals(0xFF00FF00, colors[0].getForegroundColor());
s = Html.fromHtml("<font color=\"navy\">something</font>");
colors = s.getSpans(0, s.length(), ForegroundColorSpan.class);
- assertEquals(colors[0].getForegroundColor(), 0xFF000080);
+ assertEquals(1, colors.length);
+ assertEquals(0xFF000080, colors[0].getForegroundColor());
s = Html.fromHtml("<font color=\"gibberish\">something</font>");
colors = s.getSpans(0, s.length(), ForegroundColorSpan.class);
+ assertEquals(0, colors.length);
+ }
+
+ @MediumTest
+ public void testResourceColor() throws Exception {
+ ColorStateList c =
+ Resources.getSystem().getColorStateList(android.R.color.primary_text_dark);
+ Spanned s;
+ TextAppearanceSpan[] colors;
+
+ s = Html.fromHtml("<font color=\"@android:color/primary_text_dark\">something</font>");
+ colors = s.getSpans(0, s.length(), TextAppearanceSpan.class);
+ assertEquals(1, colors.length);
+ assertEquals(c.toString(), colors[0].getTextColor().toString());
+
+ s = Html.fromHtml("<font color=\"@android:primary_text_dark\">something</font>");
+ colors = s.getSpans(0, s.length(), TextAppearanceSpan.class);
+ assertEquals(1, colors.length);
+ assertEquals(c.toString(), colors[0].getTextColor().toString());
+
+ s = Html.fromHtml("<font color=\"@color/primary_text_dark\">something</font>");
+ colors = s.getSpans(0, s.length(), TextAppearanceSpan.class);
+ assertEquals(1, colors.length);
+ assertEquals(c.toString(), colors[0].getTextColor().toString());
+
+ s = Html.fromHtml("<font color=\"@primary_text_dark\">something</font>");
+ colors = s.getSpans(0, s.length(), TextAppearanceSpan.class);
+ assertEquals(1, colors.length);
+ assertEquals(c.toString(), colors[0].getTextColor().toString());
+
+ s = Html.fromHtml("<font color=\"@" + android.R.color.primary_text_dark
+ + "\">something</font>");
+ colors = s.getSpans(0, s.length(), TextAppearanceSpan.class);
+ assertEquals(1, colors.length);
+ assertEquals(c.toString(), colors[0].getTextColor().toString());
+
+ s = Html.fromHtml("<font color=\"gibberish\">something</font>");
+ colors = s.getSpans(0, s.length(), TextAppearanceSpan.class);
assertEquals(colors.length, 0);
}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/SearchablesTest.java b/tests/AndroidTests/src/com/android/unit_tests/SearchablesTest.java
index ecc8dfe90599..4e5f7a947e95 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/SearchablesTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/SearchablesTest.java
@@ -93,8 +93,8 @@ public class SearchablesTest extends AndroidTestCase {
Context appContext = si.getActivityContext(mContext);
assertNotNull(appContext);
MoreAsserts.assertNotEqual(appContext, mContext);
- assertEquals("Android Search", appContext.getString(si.getHintId()));
- assertEquals("Google", appContext.getString(si.getLabelId()));
+ assertEquals("Quick Search Box", appContext.getString(si.getHintId()));
+ assertEquals("Quick Search Box", appContext.getString(si.getLabelId()));
}
/**
diff --git a/tests/AndroidTests/src/com/android/unit_tests/VpnTest.java b/tests/AndroidTests/src/com/android/unit_tests/VpnTest.java
new file mode 100755
index 000000000000..7dc13141e0c6
--- /dev/null
+++ b/tests/AndroidTests/src/com/android/unit_tests/VpnTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package com.android.unit_tests;
+
+import android.net.vpn.L2tpIpsecProfile;
+import android.net.vpn.VpnType;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+/**
+ * Unit test class to test VPN api
+ * Use the below command to run the vpn unit test only
+ * runtest vpntest or
+ * adb shell am instrument -e class 'com.android.unit_tests.VpnTest'
+ * -w com.android.unit_tests/android.test.InstrumentationTestRunner
+ */
+public class VpnTest extends AndroidTestCase {
+
+ @Override
+ public void setUp() {
+ }
+
+ @Override
+ public void tearDown() {
+ }
+
+ @SmallTest
+ public void testGetType() {
+ L2tpIpsecProfile li = new L2tpIpsecProfile();
+ assertTrue(VpnType.L2TP_IPSEC== li.getType());
+ }
+}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/activity/ServiceTest.java b/tests/AndroidTests/src/com/android/unit_tests/activity/ServiceTest.java
index db523dc4d506..95f6e362f065 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/activity/ServiceTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/activity/ServiceTest.java
@@ -27,10 +27,14 @@ import android.os.IBinder;
import android.os.Parcel;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
import android.util.Log;
// These test binders purport to support an interface whose canonical
// interface name is ServiceTest.SERVICE_LOCAL
+// Temporarily suppress, this test is causing unit test suite run to fail
+// TODO: remove this suppress
+@Suppress
public class ServiceTest extends ActivityTestsBase {
public static final String SERVICE_LOCAL =
@@ -131,7 +135,7 @@ public class ServiceTest extends ActivityTestsBase {
mSetReporter = setReporter;
mMonitor = !setReporter;
}
-
+
void setMonitor(boolean v) {
mMonitor = v;
}
@@ -148,7 +152,7 @@ public class ServiceTest extends ActivityTestsBase {
}
data.recycle();
}
-
+
if (mMonitor) {
mCount++;
if (mStartState == STATE_START_1) {
@@ -260,7 +264,7 @@ public class ServiceTest extends ActivityTestsBase {
waitForResultOrThrow(5 * 1000, "existing connection to lose service");
getContext().unbindService(conn);
-
+
conn = new TestConnection(true, true);
success = false;
try {
@@ -290,7 +294,7 @@ public class ServiceTest extends ActivityTestsBase {
waitForResultOrThrow(5 * 1000, "existing connection to lose service");
getContext().unbindService(conn);
-
+
conn = new TestConnection(true, true);
success = false;
try {
@@ -318,12 +322,12 @@ public class ServiceTest extends ActivityTestsBase {
mStartState = STATE_UNBIND_ONLY;
getContext().unbindService(conn);
waitForResultOrThrow(5 * 1000, "existing connection to unbind service");
-
+
// Expect to see the service rebound.
mStartState = STATE_REBIND;
getContext().bindService(service, conn, 0);
waitForResultOrThrow(5 * 1000, "existing connection to rebind service");
-
+
// Expect to see the service unbind and then destroyed.
mStartState = STATE_UNBIND;
getContext().stopService(service);
diff --git a/tests/AndroidTests/src/com/android/unit_tests/content/ConfigTest.java b/tests/AndroidTests/src/com/android/unit_tests/content/ConfigTest.java
index e6639d3720cd..a065d7016af0 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/content/ConfigTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/content/ConfigTest.java
@@ -133,7 +133,7 @@ public class ConfigTest extends AndroidTestCase {
case DENSITY:
// this is the ratio from the standard
- mMetrics.density = (((float)value)/((float)DisplayMetrics.DEFAULT_DENSITY));
+ mMetrics.density = (((float)value)/((float)DisplayMetrics.DENSITY_DEFAULT));
break;
default:
assert(false);
diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNode.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNode.java
new file mode 100644
index 000000000000..0ee74dfc16f0
--- /dev/null
+++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNode.java
@@ -0,0 +1,322 @@
+/*
+ * 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.
+ */
+package com.android.unit_tests.vcard;
+
+import android.content.ContentValues;
+
+import org.apache.commons.codec.binary.Base64;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.Map.Entry;
+import java.util.regex.Pattern;
+
+/**
+ * @hide old class just for test
+ */
+public class PropertyNode {
+ public String propName;
+ public String propValue;
+ public List<String> propValue_vector;
+
+ /** Store value as byte[],after decode.
+ * Used when propValue is encoded by something like BASE64, QUOTED-PRINTABLE, etc.
+ */
+ public byte[] propValue_bytes;
+
+ /** param store: key=paramType, value=paramValue
+ * Note that currently PropertyNode class does not support multiple param-values
+ * defined in vCard 3.0 (See also RFC 2426). multiple-values are stored as
+ * one String value like "A,B", not ["A", "B"]...
+ * TODO: fix this.
+ */
+ public ContentValues paramMap;
+
+ /** Only for TYPE=??? param store. */
+ public Set<String> paramMap_TYPE;
+
+ /** Store group values. Used only in VCard. */
+ public Set<String> propGroupSet;
+
+ public PropertyNode() {
+ propName = "";
+ propValue = "";
+ propValue_vector = new ArrayList<String>();
+ paramMap = new ContentValues();
+ paramMap_TYPE = new HashSet<String>();
+ propGroupSet = new HashSet<String>();
+ }
+
+ public PropertyNode(
+ String propName, String propValue, List<String> propValue_vector,
+ byte[] propValue_bytes, ContentValues paramMap, Set<String> paramMap_TYPE,
+ Set<String> propGroupSet) {
+ if (propName != null) {
+ this.propName = propName;
+ } else {
+ this.propName = "";
+ }
+ if (propValue != null) {
+ this.propValue = propValue;
+ } else {
+ this.propValue = "";
+ }
+ if (propValue_vector != null) {
+ this.propValue_vector = propValue_vector;
+ } else {
+ this.propValue_vector = new ArrayList<String>();
+ }
+ this.propValue_bytes = propValue_bytes;
+ if (paramMap != null) {
+ this.paramMap = paramMap;
+ } else {
+ this.paramMap = new ContentValues();
+ }
+ if (paramMap_TYPE != null) {
+ this.paramMap_TYPE = paramMap_TYPE;
+ } else {
+ this.paramMap_TYPE = new HashSet<String>();
+ }
+ if (propGroupSet != null) {
+ this.propGroupSet = propGroupSet;
+ } else {
+ this.propGroupSet = new HashSet<String>();
+ }
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof PropertyNode)) {
+ return false;
+ }
+
+ PropertyNode node = (PropertyNode)obj;
+
+ if (propName == null || !propName.equals(node.propName)) {
+ return false;
+ } else if (!paramMap.equals(node.paramMap)) {
+ return false;
+ } else if (!paramMap_TYPE.equals(node.paramMap_TYPE)) {
+ return false;
+ } else if (!propGroupSet.equals(node.propGroupSet)) {
+ return false;
+ }
+
+ if (propValue_bytes != null && Arrays.equals(propValue_bytes, node.propValue_bytes)) {
+ return true;
+ } else {
+ // Log.d("@@@", propValue + ", " + node.propValue);
+ if (!propValue.equals(node.propValue)) {
+ return false;
+ }
+
+ // The value in propValue_vector is not decoded even if it should be
+ // decoded by BASE64 or QUOTED-PRINTABLE. When the size of propValue_vector
+ // is 1, the encoded value is stored in propValue, so we do not have to
+ // check it.
+ return (propValue_vector.equals(node.propValue_vector) ||
+ propValue_vector.size() == 1 ||
+ node.propValue_vector.size() == 1);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("propName: ");
+ builder.append(propName);
+ builder.append(", paramMap: ");
+ builder.append(paramMap.toString());
+ builder.append(", propmMap_TYPE: ");
+ builder.append(paramMap_TYPE.toString());
+ builder.append(", propGroupSet: ");
+ builder.append(propGroupSet.toString());
+ if (propValue_vector != null && propValue_vector.size() > 1) {
+ builder.append(", propValue_vector size: ");
+ builder.append(propValue_vector.size());
+ }
+ if (propValue_bytes != null) {
+ builder.append(", propValue_bytes size: ");
+ builder.append(propValue_bytes.length);
+ }
+ builder.append(", propValue: ");
+ builder.append(propValue);
+ return builder.toString();
+ }
+
+ /**
+ * Encode this object into a string which can be decoded.
+ */
+ public String encode() {
+ // PropertyNode#toString() is for reading, not for parsing in the future.
+ // We construct appropriate String here.
+ StringBuilder builder = new StringBuilder();
+ if (propName.length() > 0) {
+ builder.append("propName:[");
+ builder.append(propName);
+ builder.append("],");
+ }
+ int size = propGroupSet.size();
+ if (size > 0) {
+ Set<String> set = propGroupSet;
+ builder.append("propGroup:[");
+ int i = 0;
+ for (String group : set) {
+ // We do not need to double quote groups.
+ // group = 1*(ALPHA / DIGIT / "-")
+ builder.append(group);
+ if (i < size - 1) {
+ builder.append(",");
+ }
+ i++;
+ }
+ builder.append("],");
+ }
+
+ if (paramMap.size() > 0 || paramMap_TYPE.size() > 0) {
+ ContentValues values = paramMap;
+ builder.append("paramMap:[");
+ size = paramMap.size();
+ int i = 0;
+ for (Entry<String, Object> entry : values.valueSet()) {
+ // Assuming param-key does not contain NON-ASCII nor symbols.
+ //
+ // According to vCard 3.0:
+ // param-name = iana-token / x-name
+ builder.append(entry.getKey());
+
+ // param-value may contain any value including NON-ASCIIs.
+ // We use the following replacing rule.
+ // \ -> \\
+ // , -> \,
+ // In String#replaceAll(), "\\\\" means a single backslash.
+ builder.append("=");
+ builder.append(entry.getValue().toString()
+ .replaceAll("\\\\", "\\\\\\\\")
+ .replaceAll(",", "\\\\,"));
+ if (i < size -1) {
+ builder.append(",");
+ }
+ i++;
+ }
+
+ Set<String> set = paramMap_TYPE;
+ size = paramMap_TYPE.size();
+ if (i > 0 && size > 0) {
+ builder.append(",");
+ }
+ i = 0;
+ for (String type : set) {
+ builder.append("TYPE=");
+ builder.append(type
+ .replaceAll("\\\\", "\\\\\\\\")
+ .replaceAll(",", "\\\\,"));
+ if (i < size - 1) {
+ builder.append(",");
+ }
+ i++;
+ }
+ builder.append("],");
+ }
+
+ size = propValue_vector.size();
+ if (size > 0) {
+ builder.append("propValue:[");
+ List<String> list = propValue_vector;
+ for (int i = 0; i < size; i++) {
+ builder.append(list.get(i)
+ .replaceAll("\\\\", "\\\\\\\\")
+ .replaceAll(",", "\\\\,"));
+ if (i < size -1) {
+ builder.append(",");
+ }
+ }
+ builder.append("],");
+ }
+
+ return builder.toString();
+ }
+
+ public static PropertyNode decode(String encodedString) {
+ PropertyNode propertyNode = new PropertyNode();
+ String trimed = encodedString.trim();
+ if (trimed.length() == 0) {
+ return propertyNode;
+ }
+ String[] elems = trimed.split("],");
+
+ for (String elem : elems) {
+ int index = elem.indexOf('[');
+ String name = elem.substring(0, index - 1);
+ Pattern pattern = Pattern.compile("(?<!\\\\),");
+ String[] values = pattern.split(elem.substring(index + 1), -1);
+ if (name.equals("propName")) {
+ propertyNode.propName = values[0];
+ } else if (name.equals("propGroupSet")) {
+ for (String value : values) {
+ propertyNode.propGroupSet.add(value);
+ }
+ } else if (name.equals("paramMap")) {
+ ContentValues paramMap = propertyNode.paramMap;
+ Set<String> paramMap_TYPE = propertyNode.paramMap_TYPE;
+ for (String value : values) {
+ String[] tmp = value.split("=", 2);
+ String mapKey = tmp[0];
+ // \, -> ,
+ // \\ -> \
+ // In String#replaceAll(), "\\\\" means a single backslash.
+ String mapValue =
+ tmp[1].replaceAll("\\\\,", ",").replaceAll("\\\\\\\\", "\\\\");
+ if (mapKey.equalsIgnoreCase("TYPE")) {
+ paramMap_TYPE.add(mapValue);
+ } else {
+ paramMap.put(mapKey, mapValue);
+ }
+ }
+ } else if (name.equals("propValue")) {
+ StringBuilder builder = new StringBuilder();
+ List<String> list = propertyNode.propValue_vector;
+ int length = values.length;
+ for (int i = 0; i < length; i++) {
+ String normValue = values[i]
+ .replaceAll("\\\\,", ",")
+ .replaceAll("\\\\\\\\", "\\\\");
+ list.add(normValue);
+ builder.append(normValue);
+ if (i < length - 1) {
+ builder.append(";");
+ }
+ }
+ propertyNode.propValue = builder.toString();
+ }
+ }
+
+ // At this time, QUOTED-PRINTABLE is already decoded to Java String.
+ // We just need to decode BASE64 String to binary.
+ String encoding = propertyNode.paramMap.getAsString("ENCODING");
+ if (encoding != null &&
+ (encoding.equalsIgnoreCase("BASE64") ||
+ encoding.equalsIgnoreCase("B"))) {
+ propertyNode.propValue_bytes =
+ Base64.decodeBase64(propertyNode.propValue_vector.get(0).getBytes());
+ }
+
+ return propertyNode;
+ }
+}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/VCardTests.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardTests.java
index b7f562ddc947..af5562ad4ba4 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/VCardTests.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardTests.java
@@ -14,36 +14,132 @@
* limitations under the License.
*/
-package com.android.unit_tests;
+package com.android.unit_tests.vcard;
import android.content.ContentValues;
-import android.syncml.pim.PropertyNode;
-import android.syncml.pim.VDataBuilder;
-import android.syncml.pim.VNode;
-import android.syncml.pim.vcard.VCardException;
-import android.syncml.pim.vcard.VCardParser_V21;
-import android.syncml.pim.vcard.VCardParser_V30;
+import android.pim.vcard.ContactStruct;
+import android.pim.vcard.EntryHandler;
+import android.pim.vcard.VCardBuilder;
+import android.pim.vcard.VCardBuilderCollection;
+import android.pim.vcard.VCardConfig;
+import android.pim.vcard.VCardDataBuilder;
+import android.pim.vcard.VCardParser;
+import android.pim.vcard.VCardParser_V21;
+import android.pim.vcard.VCardParser_V30;
+import android.pim.vcard.exception.VCardException;
import android.test.AndroidTestCase;
+import com.android.unit_tests.R;
+
import java.io.IOException;
import java.io.InputStream;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.Vector;
+import java.util.List;
+import java.util.Map;
public class VCardTests extends AndroidTestCase {
+ // TODO: Use EntityIterator, which is added in Eclair.
+ private static class EntryHolder implements EntryHandler {
+ public List<ContactStruct> contacts = new ArrayList<ContactStruct>();
+ public void onEntryCreated(ContactStruct contactStruct) {
+ contacts.add(contactStruct);
+ }
+ public void onFinal() {
+ }
+ }
+
+ static void verify(ContactStruct expected, ContactStruct actual) {
+ if (!equalsString(expected.getName(), actual.getName())) {
+ fail(String.format("Names do not equal: \"%s\" != \"%s\"",
+ expected.getName(), actual.getName()));
+ }
+ if (!equalsString(
+ expected.getPhoneticName(), actual.getPhoneticName())) {
+ fail(String.format("Phonetic names do not equal: \"%s\" != \"%s\"",
+ expected.getPhoneticName(), actual.getPhoneticName()));
+ }
+ {
+ final byte[] expectedPhotoBytes = expected.getPhotoBytes();
+ final byte[] actualPhotoBytes = actual.getPhotoBytes();
+ if (!((expectedPhotoBytes == null && actualPhotoBytes == null) ||
+ Arrays.equals(expectedPhotoBytes, actualPhotoBytes))) {
+ fail("photoBytes is not equal.");
+ }
+ }
+ verifyInternal(expected.getNotes(), actual.getNotes(), "notes");
+ verifyInternal(expected.getPhoneList(), actual.getPhoneList(), "phones");
+ verifyInternal(expected.getContactMethodList(), actual.getContactMethodList(),
+ "contact lists");
+ verifyInternal(expected.getOrganizationList(), actual.getOrganizationList(),
+ "organizations");
+ {
+ final Map<String, List<String>> expectedMap =
+ expected.getExtensionMap();
+ final Map<String, List<String>> actualMap =
+ actual.getExtensionMap();
+ if (verifySize((expectedMap == null ? 0 : expectedMap.size()),
+ (actualMap == null ? 0 : actualMap.size()), "extensions") > 0) {
+ for (String key : expectedMap.keySet()) {
+ if (!actualMap.containsKey(key)) {
+ fail(String.format(
+ "Actual does not have %s extension while expected has",
+ key));
+ }
+ final List<String> expectedList = expectedMap.get(key);
+ final List<String> actualList = actualMap.get(key);
+ verifyInternal(expectedList, actualList,
+ String.format("extension \"%s\"", key));
+ }
+ }
+ }
+ }
+
+ private static boolean equalsString(String a, String b) {
+ if (a == null || a.length() == 0) {
+ return b == null || b.length() == 0;
+ } else {
+ return a.equals(b);
+ }
+ }
+
+ private static int verifySize(int expectedSize, int actualSize, String name) {
+ if (expectedSize != actualSize) {
+ fail(String.format("Size of %s is different: %d != %d",
+ name, expectedSize, actualSize));
+ }
+ return expectedSize;
+ }
+
+ private static <T> void verifyInternal(final List<T> expected, final List<T> actual,
+ String name) {
+ if(verifySize((expected == null ? 0 : expected.size()),
+ (actual == null ? 0 : actual.size()), name) > 0) {
+ int size = expected.size();
+ for (int i = 0; i < size; i++) {
+ final T expectedObj = expected.get(i);
+ final T actualObj = actual.get(i);
+ if (!expected.equals(actual)) {
+ fail(String.format("The %i %s are different: %s != %s",
+ i, name, expectedObj, actualObj));
+ }
+ }
+ }
+ }
+
private class PropertyNodesVerifier {
- private HashMap<String, Vector<PropertyNode>> mPropertyNodeMap;
+ private HashMap<String, ArrayList<PropertyNode>> mPropertyNodeMap;
public PropertyNodesVerifier(PropertyNode... nodes) {
- mPropertyNodeMap = new HashMap<String, Vector<PropertyNode>>();
+ mPropertyNodeMap = new HashMap<String, ArrayList<PropertyNode>>();
for (PropertyNode propertyNode : nodes) {
String propName = propertyNode.propName;
- Vector<PropertyNode> expectedNodes =
+ ArrayList<PropertyNode> expectedNodes =
mPropertyNodeMap.get(propName);
if (expectedNodes == null) {
- expectedNodes = new Vector<PropertyNode>();
+ expectedNodes = new ArrayList<PropertyNode>();
mPropertyNodeMap.put(propName, expectedNodes);
}
expectedNodes.add(propertyNode);
@@ -53,7 +149,7 @@ public class VCardTests extends AndroidTestCase {
public void verify(VNode vnode) {
for (PropertyNode propertyNode : vnode.propList) {
String propName = propertyNode.propName;
- Vector<PropertyNode> nodes = mPropertyNodeMap.get(propName);
+ ArrayList<PropertyNode> nodes = mPropertyNodeMap.get(propName);
if (nodes == null) {
fail("Unexpected propName \"" + propName + "\" exists.");
}
@@ -81,8 +177,8 @@ public class VCardTests extends AndroidTestCase {
}
}
if (mPropertyNodeMap.size() != 0) {
- Vector<String> expectedProps = new Vector<String>();
- for (Vector<PropertyNode> nodes : mPropertyNodeMap.values()) {
+ ArrayList<String> expectedProps = new ArrayList<String>();
+ for (ArrayList<PropertyNode> nodes : mPropertyNodeMap.values()) {
for (PropertyNode node : nodes) {
expectedProps.add(node.propName);
}
@@ -93,25 +189,82 @@ public class VCardTests extends AndroidTestCase {
}
}
- public void testV21SimpleCase() throws IOException, VCardException {
- VCardParser_V21 parser = new VCardParser_V21();
- VDataBuilder builder = new VDataBuilder();
- InputStream is = getContext().getResources().openRawResource(R.raw.v21_simple);
+ public void testV21SimpleCase1_1() throws IOException, VCardException {
+ VCardParser parser = new VCardParser_V21();
+ VCardDataBuilder builder = new VCardDataBuilder(VCardConfig.NAME_ORDER_TYPE_ENGLISH);
+ EntryHolder holder = new EntryHolder();
+ builder.addEntryHandler(holder);
+ InputStream is = getContext().getResources().openRawResource(R.raw.v21_simple_1);
assertEquals(true, parser.parse(is,"ISO-8859-1", builder));
is.close();
- assertEquals(1, builder.vNodeList.size());
+ assertEquals(1, holder.contacts.size());
+ verify(new ContactStruct("Roid Ando", null,
+ null, null, null, null, null, null),
+ holder.contacts.get(0));
+ }
+
+ public void testV21SimpleCase1_2() throws IOException, VCardException {
+ VCardParser parser = new VCardParser_V21();
+ VCardDataBuilder builder = new VCardDataBuilder(VCardConfig.NAME_ORDER_TYPE_JAPANESE);
+ EntryHolder holder = new EntryHolder();
+ builder.addEntryHandler(holder);
+ InputStream is = getContext().getResources().openRawResource(R.raw.v21_simple_1);
+ assertEquals(true, parser.parse(is,"ISO-8859-1", builder));
+ is.close();
+ assertEquals(1, holder.contacts.size());
+ verify(new ContactStruct("Ando Roid", null,
+ null, null, null, null, null, null),
+ holder.contacts.get(0));
+ }
+
+ public void testV21SimpleCase2() throws IOException, VCardException {
+ VCardParser parser = new VCardParser_V21();
+ VCardDataBuilder builder = new VCardDataBuilder(VCardConfig.NAME_ORDER_TYPE_ENGLISH);
+ EntryHolder holder = new EntryHolder();
+ builder.addEntryHandler(holder);
+ InputStream is = getContext().getResources().openRawResource(R.raw.v21_simple_2);
+ assertEquals(true, parser.parse(is,"ISO-8859-1", builder));
+ is.close();
+ assertEquals(1, holder.contacts.size());
+ verify(new ContactStruct("Ando Roid", null,
+ null, null, null, null, null, null),
+ holder.contacts.get(0));
+ }
+
+ public void testV21SimpleCase3() throws IOException, VCardException {
+ VCardParser parser = new VCardParser_V21();
+ VCardDataBuilder builder1 = new VCardDataBuilder(VCardConfig.NAME_ORDER_TYPE_ENGLISH);
+ EntryHolder holder = new EntryHolder();
+ builder1.addEntryHandler(holder);
+ VNodeBuilder builder2 = new VNodeBuilder();
+ VCardBuilderCollection collection =
+ new VCardBuilderCollection(
+ new ArrayList<VCardBuilder>(Arrays.asList(builder1, builder2)));
+ InputStream is = getContext().getResources().openRawResource(R.raw.v21_simple_3);
+ assertEquals(true, parser.parse(is,"ISO-8859-1", collection));
+ is.close();
+
+ assertEquals(1, builder2.vNodeList.size());
+ VNode vnode = builder2.vNodeList.get(0);
PropertyNodesVerifier verifier = new PropertyNodesVerifier(
new PropertyNode("N", "Ando;Roid;",
Arrays.asList("Ando", "Roid", ""),
null, null, null, null),
new PropertyNode("FN", "Ando Roid",
null, null, null, null, null));
- verifier.verify(builder.vNodeList.get(0));
+ verifier.verify(vnode);
+
+ // FN is prefered.
+ assertEquals(1, holder.contacts.size());
+ ContactStruct actual = holder.contacts.get(0);
+ verify(new ContactStruct("Ando Roid", null,
+ null, null, null, null, null, null),
+ actual);
}
-
+
public void testV21BackslashCase() throws IOException, VCardException {
VCardParser_V21 parser = new VCardParser_V21();
- VDataBuilder builder = new VDataBuilder();
+ VNodeBuilder builder = new VNodeBuilder();
InputStream is = getContext().getResources().openRawResource(R.raw.v21_backslash);
assertEquals(true, parser.parse(is,"ISO-8859-1", builder));
is.close();
@@ -129,7 +282,7 @@ public class VCardTests extends AndroidTestCase {
public void testV21ComplicatedCase() throws IOException, VCardException {
VCardParser_V21 parser = new VCardParser_V21();
- VDataBuilder builder = new VDataBuilder();
+ VNodeBuilder builder = new VNodeBuilder();
InputStream is = getContext().getResources().openRawResource(R.raw.v21_complicated);
assertEquals(true, parser.parse(is,"ISO-8859-1", builder));
is.close();
@@ -585,7 +738,7 @@ public class VCardTests extends AndroidTestCase {
public void testV21Japanese1() throws IOException, VCardException {
VCardParser_V21 parser = new VCardParser_V21();
- VDataBuilder builder = new VDataBuilder();
+ VNodeBuilder builder = new VNodeBuilder();
InputStream is = getContext().getResources().openRawResource(R.raw.v21_japanese_1);
assertEquals(true, parser.parse(is,"ISO-8859-1", builder));
is.close();
@@ -616,7 +769,7 @@ public class VCardTests extends AndroidTestCase {
public void testV21Japanese2() throws IOException, VCardException {
VCardParser_V21 parser = new VCardParser_V21();
- VDataBuilder builder = new VDataBuilder();
+ VNodeBuilder builder = new VNodeBuilder();
InputStream is = getContext().getResources().openRawResource(R.raw.v21_japanese_2);
assertEquals(true, parser.parse(is,"ISO-8859-1", builder));
is.close();
@@ -660,7 +813,7 @@ public class VCardTests extends AndroidTestCase {
public void testV21MultipleEntryCase() throws IOException, VCardException {
VCardParser_V21 parser = new VCardParser_V21();
- VDataBuilder builder = new VDataBuilder();
+ VNodeBuilder builder = new VNodeBuilder();
InputStream is = getContext().getResources().openRawResource(R.raw.v21_multiple_entry);
assertEquals(true, parser.parse(is,"ISO-8859-1", builder));
is.close();
@@ -741,7 +894,7 @@ public class VCardTests extends AndroidTestCase {
public void testV30SimpleCase() throws IOException, VCardException {
VCardParser_V21 parser = new VCardParser_V30();
- VDataBuilder builder = new VDataBuilder();
+ VNodeBuilder builder = new VNodeBuilder();
InputStream is = getContext().getResources().openRawResource(R.raw.v30_simple);
assertEquals(true, parser.parse(is,"ISO-8859-1", builder));
is.close();
diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/VNode.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/VNode.java
new file mode 100644
index 000000000000..3eb827b60710
--- /dev/null
+++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/VNode.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+package com.android.unit_tests.vcard;
+
+import java.util.ArrayList;
+
+/**
+ * @hide old class. Just for testing
+ */
+public class VNode {
+ public String VName;
+
+ public ArrayList<PropertyNode> propList = new ArrayList<PropertyNode>();
+
+ /** 0:parse over. 1:parsing. */
+ public int parseStatus = 1;
+}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/VNodeBuilder.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/VNodeBuilder.java
new file mode 100644
index 000000000000..6d692237489a
--- /dev/null
+++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/VNodeBuilder.java
@@ -0,0 +1,313 @@
+/*
+ * 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.
+ */
+package com.android.unit_tests.vcard;
+
+import android.content.ContentValues;
+import android.pim.vcard.VCardBuilder;
+import android.pim.vcard.VCardConfig;
+import android.util.CharsetUtils;
+import android.util.Log;
+
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.codec.net.QuotedPrintableCodec;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Store the parse result to custom datastruct: VNode, PropertyNode
+ * Maybe several vcard instance, so use vNodeList to store.
+ * VNode: standy by a vcard instance.
+ * PropertyNode: standy by a property line of a card.
+ * @hide old class, just for testing use
+ */
+public class VNodeBuilder implements VCardBuilder {
+ static private String LOG_TAG = "VDATABuilder";
+
+ /**
+ * If there's no other information available, this class uses this charset for encoding
+ * byte arrays.
+ */
+ static public String TARGET_CHARSET = "UTF-8";
+
+ /** type=VNode */
+ public List<VNode> vNodeList = new ArrayList<VNode>();
+ private int mNodeListPos = 0;
+ private VNode mCurrentVNode;
+ private PropertyNode mCurrentPropNode;
+ private String mCurrentParamType;
+
+ /**
+ * The charset using which VParser parses the text.
+ */
+ private String mSourceCharset;
+
+ /**
+ * The charset with which byte array is encoded to String.
+ */
+ private String mTargetCharset;
+
+ private boolean mStrictLineBreakParsing;
+
+ public VNodeBuilder() {
+ this(VCardConfig.DEFAULT_CHARSET, TARGET_CHARSET, false);
+ }
+
+ public VNodeBuilder(String charset, boolean strictLineBreakParsing) {
+ this(null, charset, strictLineBreakParsing);
+ }
+
+ /**
+ * @hide sourceCharset is temporal.
+ */
+ public VNodeBuilder(String sourceCharset, String targetCharset,
+ boolean strictLineBreakParsing) {
+ if (sourceCharset != null) {
+ mSourceCharset = sourceCharset;
+ } else {
+ mSourceCharset = VCardConfig.DEFAULT_CHARSET;
+ }
+ if (targetCharset != null) {
+ mTargetCharset = targetCharset;
+ } else {
+ mTargetCharset = TARGET_CHARSET;
+ }
+ mStrictLineBreakParsing = strictLineBreakParsing;
+ }
+
+ public void start() {
+ }
+
+ public void end() {
+ }
+
+ // Note: I guess that this code assumes the Record may nest like this:
+ // START:VPOS
+ // ...
+ // START:VPOS2
+ // ...
+ // END:VPOS2
+ // ...
+ // END:VPOS
+ //
+ // However the following code has a bug.
+ // When error occurs after calling startRecord(), the entry which is probably
+ // the cause of the error remains to be in vNodeList, while endRecord() is not called.
+ //
+ // I leave this code as is since I'm not familiar with vcalendar specification.
+ // But I believe we should refactor this code in the future.
+ // Until this, the last entry has to be removed when some error occurs.
+ public void startRecord(String type) {
+
+ VNode vnode = new VNode();
+ vnode.parseStatus = 1;
+ vnode.VName = type;
+ // I feel this should be done in endRecord(), but it cannot be done because of
+ // the reason above.
+ vNodeList.add(vnode);
+ mNodeListPos = vNodeList.size() - 1;
+ mCurrentVNode = vNodeList.get(mNodeListPos);
+ }
+
+ public void endRecord() {
+ VNode endNode = vNodeList.get(mNodeListPos);
+ endNode.parseStatus = 0;
+ while(mNodeListPos > 0){
+ mNodeListPos--;
+ if((vNodeList.get(mNodeListPos)).parseStatus == 1)
+ break;
+ }
+ mCurrentVNode = vNodeList.get(mNodeListPos);
+ }
+
+ public void startProperty() {
+ mCurrentPropNode = new PropertyNode();
+ }
+
+ public void endProperty() {
+ mCurrentVNode.propList.add(mCurrentPropNode);
+ }
+
+ public void propertyName(String name) {
+ mCurrentPropNode.propName = name;
+ }
+
+ // Used only in VCard.
+ public void propertyGroup(String group) {
+ mCurrentPropNode.propGroupSet.add(group);
+ }
+
+ public void propertyParamType(String type) {
+ mCurrentParamType = type;
+ }
+
+ public void propertyParamValue(String value) {
+ if (mCurrentParamType == null ||
+ mCurrentParamType.equalsIgnoreCase("TYPE")) {
+ mCurrentPropNode.paramMap_TYPE.add(value);
+ } else {
+ mCurrentPropNode.paramMap.put(mCurrentParamType, value);
+ }
+
+ mCurrentParamType = null;
+ }
+
+ private String encodeString(String originalString, String targetCharset) {
+ if (mSourceCharset.equalsIgnoreCase(targetCharset)) {
+ return originalString;
+ }
+ Charset charset = Charset.forName(mSourceCharset);
+ ByteBuffer byteBuffer = charset.encode(originalString);
+ // byteBuffer.array() "may" return byte array which is larger than
+ // byteBuffer.remaining(). Here, we keep on the safe side.
+ byte[] bytes = new byte[byteBuffer.remaining()];
+ byteBuffer.get(bytes);
+ try {
+ return new String(bytes, targetCharset);
+ } catch (UnsupportedEncodingException e) {
+ Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset);
+ return null;
+ }
+ }
+
+ private String handleOneValue(String value, String targetCharset, String encoding) {
+ if (encoding != null) {
+ if (encoding.equals("BASE64") || encoding.equals("B")) {
+ // Assume BASE64 is used only when the number of values is 1.
+ mCurrentPropNode.propValue_bytes =
+ Base64.decodeBase64(value.getBytes());
+ return value;
+ } else if (encoding.equals("QUOTED-PRINTABLE")) {
+ String quotedPrintable = value
+ .replaceAll("= ", " ").replaceAll("=\t", "\t");
+ String[] lines;
+ if (mStrictLineBreakParsing) {
+ lines = quotedPrintable.split("\r\n");
+ } else {
+ StringBuilder builder = new StringBuilder();
+ int length = quotedPrintable.length();
+ ArrayList<String> list = new ArrayList<String>();
+ for (int i = 0; i < length; i++) {
+ char ch = quotedPrintable.charAt(i);
+ if (ch == '\n') {
+ list.add(builder.toString());
+ builder = new StringBuilder();
+ } else if (ch == '\r') {
+ list.add(builder.toString());
+ builder = new StringBuilder();
+ if (i < length - 1) {
+ char nextCh = quotedPrintable.charAt(i + 1);
+ if (nextCh == '\n') {
+ i++;
+ }
+ }
+ } else {
+ builder.append(ch);
+ }
+ }
+ String finalLine = builder.toString();
+ if (finalLine.length() > 0) {
+ list.add(finalLine);
+ }
+ lines = list.toArray(new String[0]);
+ }
+ StringBuilder builder = new StringBuilder();
+ for (String line : lines) {
+ if (line.endsWith("=")) {
+ line = line.substring(0, line.length() - 1);
+ }
+ builder.append(line);
+ }
+ byte[] bytes;
+ try {
+ bytes = builder.toString().getBytes(mSourceCharset);
+ } catch (UnsupportedEncodingException e1) {
+ Log.e(LOG_TAG, "Failed to encode: charset=" + mSourceCharset);
+ bytes = builder.toString().getBytes();
+ }
+
+ try {
+ bytes = QuotedPrintableCodec.decodeQuotedPrintable(bytes);
+ } catch (DecoderException e) {
+ Log.e(LOG_TAG, "Failed to decode quoted-printable: " + e);
+ return "";
+ }
+
+ try {
+ return new String(bytes, targetCharset);
+ } catch (UnsupportedEncodingException e) {
+ Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset);
+ return new String(bytes);
+ }
+ }
+ // Unknown encoding. Fall back to default.
+ }
+ return encodeString(value, targetCharset);
+ }
+
+ public void propertyValues(List<String> values) {
+ if (values == null || values.size() == 0) {
+ mCurrentPropNode.propValue_bytes = null;
+ mCurrentPropNode.propValue_vector.clear();
+ mCurrentPropNode.propValue_vector.add("");
+ mCurrentPropNode.propValue = "";
+ return;
+ }
+
+ ContentValues paramMap = mCurrentPropNode.paramMap;
+
+ String targetCharset = CharsetUtils.nameForDefaultVendor(paramMap.getAsString("CHARSET"));
+ String encoding = paramMap.getAsString("ENCODING");
+
+ if (targetCharset == null || targetCharset.length() == 0) {
+ targetCharset = mTargetCharset;
+ }
+
+ for (String value : values) {
+ mCurrentPropNode.propValue_vector.add(
+ handleOneValue(value, targetCharset, encoding));
+ }
+
+ mCurrentPropNode.propValue = listToString(mCurrentPropNode.propValue_vector);
+ }
+
+ private String listToString(List<String> list){
+ int size = list.size();
+ if (size > 1) {
+ StringBuilder typeListB = new StringBuilder();
+ for (String type : list) {
+ typeListB.append(type).append(";");
+ }
+ int len = typeListB.length();
+ if (len > 0 && typeListB.charAt(len - 1) == ';') {
+ return typeListB.substring(0, len - 1);
+ }
+ return typeListB.toString();
+ } else if (size == 1) {
+ return list.get(0);
+ } else {
+ return "";
+ }
+ }
+
+ public String getResult(){
+ return null;
+ }
+}
diff --git a/tests/CoreTests/android/core/RequestAPITest.java b/tests/CoreTests/android/core/RequestAPITest.java
index d89f5ae98255..94eb23e1b5dc 100644
--- a/tests/CoreTests/android/core/RequestAPITest.java
+++ b/tests/CoreTests/android/core/RequestAPITest.java
@@ -72,7 +72,7 @@ public class RequestAPITest extends AndroidTestCase implements HttpConstants {
RequestHandle handle =
mRequestQueue.queueRequest(
"http://localhost:8080/test1", "GET", headers, null,
- null, 0, false);
+ null, 0);
handle.waitUntilComplete();
fail("expected exception not thrown");
@@ -121,7 +121,7 @@ public class RequestAPITest extends AndroidTestCase implements HttpConstants {
mTestWebServer.setKeepAlive(false);
RequestHandle handle = mRequestQueue.queueRequest(
"http://localhost:8080/test1", "GET", headers, null,
- null, 0, false);
+ null, 0);
handle.waitUntilComplete();
}
@@ -197,7 +197,7 @@ public class RequestAPITest extends AndroidTestCase implements HttpConstants {
RequestHandle handle = mRequestQueue.queueRequest(
"http://localhost:8080/test1", "GET", null, testEventHandler,
- null, 0, false);
+ null, 0);
Log.d(LOGTAG, "testGet - sent request. Waiting");
handle.waitUntilComplete();
@@ -231,11 +231,11 @@ public class RequestAPITest extends AndroidTestCase implements HttpConstants {
RequestHandle handle0 = mRequestQueue.queueRequest(
"http://localhost:8080/test1", "GET", null, testEventHandler,
- null, 0, false);
+ null, 0);
handle0.waitUntilComplete();
RequestHandle handle1 = mRequestQueue.queueRequest(
"http://localhost:8080/test1", "GET", null, testEventHandler2,
- null, 0, false);
+ null, 0);
handle1.waitUntilComplete();
/* It's not correct to use same listener for multiple
@@ -270,7 +270,7 @@ public class RequestAPITest extends AndroidTestCase implements HttpConstants {
RequestHandle handle = mRequestQueue.queueRequest(
"http://localhost:8080/test1", "HEAD", null, testEventHandler,
- null, 0, false);
+ null, 0);
Log.d(LOGTAG, "testHead - sent request waiting");
handle.waitUntilComplete();
@@ -297,7 +297,7 @@ public class RequestAPITest extends AndroidTestCase implements HttpConstants {
RequestHandle handle = mRequestQueue.queueRequest(
"http://localhost:8080/test1", "GET", null, testEventHandler,
- null, 0, false);
+ null, 0);
Log.d(LOGTAG, "testChunked - sent request waiting");
handle.waitUntilComplete();
@@ -330,7 +330,7 @@ public class RequestAPITest extends AndroidTestCase implements HttpConstants {
Log.d(LOGTAG, testName + " start - rq = " + mRequestQueue);
RequestHandle requestHandle = mRequestQueue.queueRequest(
- "http://localhost:8080/test1", "GET", null, testEventHandler, null, 0, false);
+ "http://localhost:8080/test1", "GET", null, testEventHandler, null, 0);
Log.d(LOGTAG, testName + " - sent request waiting");
requestHandle.waitUntilComplete();
@@ -398,10 +398,10 @@ public class RequestAPITest extends AndroidTestCase implements HttpConstants {
leh2.expectHeaders();
RequestHandle handle0 = mRequestQueue.queueRequest(
- "http://localhost:8080/test1", "GET", null, testEventHandler, null, 0, false);
+ "http://localhost:8080/test1", "GET", null, testEventHandler, null, 0);
handle0.waitUntilComplete();
RequestHandle handle1 = mRequestQueue.queueRequest(
- "http://localhost:8080/test1", "HEAD", null, testEventHandler, null, 0, false);
+ "http://localhost:8080/test1", "HEAD", null, testEventHandler, null, 0);
Log.d(LOGTAG, "testGetAndHead - sent request. Waiting");
handle1.waitUntilComplete();
@@ -432,7 +432,7 @@ public class RequestAPITest extends AndroidTestCase implements HttpConstants {
Log.d(LOGTAG, "testPost start - rq = " + mRequestQueue);
RequestHandle handle = mRequestQueue.queueRequest(
- "http://localhost:8080/test1", "POST", null, testEventHandler, null, 0, false);
+ "http://localhost:8080/test1", "POST", null, testEventHandler, null, 0);
Log.d(LOGTAG, "testPost - sent request waiting");
handle.waitUntilComplete();
@@ -470,7 +470,7 @@ public class RequestAPITest extends AndroidTestCase implements HttpConstants {
InputStream bodyProvider = new ByteArrayInputStream(mBody.getBytes());
RequestHandle handle = mRequestQueue.queueRequest(
- "http://localhost:8080/test1", "POST", null, testEventHandler, bodyProvider, bodyLength, false);
+ "http://localhost:8080/test1", "POST", null, testEventHandler, bodyProvider, bodyLength);
Log.d(LOGTAG, "testPostWithData - sent request waiting");
handle.waitUntilComplete();
diff --git a/tests/CoreTests/com/android/internal/telephony/gsm/GSMPhoneTest.java b/tests/CoreTests/com/android/internal/telephony/gsm/GSMPhoneTest.java
index 710741297613..b96743a2fbc3 100644
--- a/tests/CoreTests/com/android/internal/telephony/gsm/GSMPhoneTest.java
+++ b/tests/CoreTests/com/android/internal/telephony/gsm/GSMPhoneTest.java
@@ -81,7 +81,7 @@ public class GSMPhoneTest extends AndroidTestCase implements PerformanceTestCase
mRadioControl = mGSMTestHandler.getSimulatedCommands();
mHandler = mGSMTestHandler.getHandler();
- mGSMPhone.registerForPhoneStateChanged(mHandler, EVENT_PHONE_STATE_CHANGED, null);
+ mGSMPhone.registerForPreciseCallStateChanged(mHandler, EVENT_PHONE_STATE_CHANGED, null);
mGSMPhone.registerForNewRingingConnection(mHandler, EVENT_RINGING, null);
mGSMPhone.registerForDisconnect(mHandler, EVENT_DISCONNECT, null);
@@ -109,7 +109,7 @@ public class GSMPhoneTest extends AndroidTestCase implements PerformanceTestCase
protected void tearDown() throws Exception {
mRadioControl.shutdown();
- mGSMPhone.unregisterForPhoneStateChanged(mHandler);
+ mGSMPhone.unregisterForPreciseCallStateChanged(mHandler);
mGSMPhone.unregisterForNewRingingConnection(mHandler);
mGSMPhone.unregisterForDisconnect(mHandler);
mGSMPhone.setOnPostDialCharacter(mHandler, 0, null);
diff --git a/tests/DpiTest/AndroidManifest.xml b/tests/DpiTest/AndroidManifest.xml
index 64ad7beeda34..68ecc6e89934 100644
--- a/tests/DpiTest/AndroidManifest.xml
+++ b/tests/DpiTest/AndroidManifest.xml
@@ -19,10 +19,15 @@
<uses-sdk android:minSdkVersion="3" android:targetSdkVersion="3" />
<supports-screens android:smallScreens="true" />
<application android:label="DpiTest">
- <activity android:name="DpiTestActivity" android:label="DpiTest">
+ <activity android:name="DpiTestActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:name="DpiTestNoCompatActivity" android:label="DpiTestNoCompat">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
diff --git a/tests/DpiTest/res/drawable-nodpi/logonodpi120.png b/tests/DpiTest/res/drawable-nodpi/logonodpi120.png
new file mode 100644
index 000000000000..46bbd5bd3d48
--- /dev/null
+++ b/tests/DpiTest/res/drawable-nodpi/logonodpi120.png
Binary files differ
diff --git a/tests/DpiTest/res/drawable-nodpi/logonodpi160.png b/tests/DpiTest/res/drawable-nodpi/logonodpi160.png
new file mode 100644
index 000000000000..c23b2ce83b43
--- /dev/null
+++ b/tests/DpiTest/res/drawable-nodpi/logonodpi160.png
Binary files differ
diff --git a/tests/DpiTest/res/drawable-nodpi/logonodpi240.png b/tests/DpiTest/res/drawable-nodpi/logonodpi240.png
new file mode 100644
index 000000000000..4d717a86143d
--- /dev/null
+++ b/tests/DpiTest/res/drawable-nodpi/logonodpi240.png
Binary files differ
diff --git a/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java b/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java
index 5a9f3f548c7d..8c69305d5760 100644
--- a/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java
+++ b/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java
@@ -17,6 +17,8 @@
package com.google.android.test.dpi;
import android.app.Activity;
+import android.app.ActivityThread;
+import android.app.Application;
import android.os.Bundle;
import android.graphics.BitmapFactory;
import android.graphics.Bitmap;
@@ -28,8 +30,42 @@ import android.widget.TextView;
import android.widget.ScrollView;
import android.view.View;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.CompatibilityInfo;
+import android.util.DisplayMetrics;
public class DpiTestActivity extends Activity {
+ public DpiTestActivity() {
+ super();
+ init(false);
+ }
+
+ public DpiTestActivity(boolean noCompat) {
+ super();
+ init(noCompat);
+ }
+
+ public void init(boolean noCompat) {
+ try {
+ // This is all a dirty hack. Don't think a real application should
+ // be doing it.
+ Application app = ActivityThread.currentActivityThread().getApplication();
+ ApplicationInfo ai = app.getPackageManager().getApplicationInfo(
+ "com.google.android.test.dpi",
+ PackageManager.GET_SUPPORTS_DENSITIES);
+ if (noCompat) {
+ ai.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS
+ | ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS
+ | ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS;
+ ai.supportsDensities = new int[] { ApplicationInfo.ANY_DENSITY };
+ app.getResources().setCompatibilityInfo(new CompatibilityInfo(ai));
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException("ouch", e);
+ }
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -73,6 +109,13 @@ public class DpiTestActivity extends Activity {
addLabelToRoot(root, "Autoscaled bitmap");
addChildToRoot(root, layout);
+ layout = new LinearLayout(this);
+ addResourceDrawable(layout, R.drawable.logonodpi120);
+ addResourceDrawable(layout, R.drawable.logonodpi160);
+ addResourceDrawable(layout, R.drawable.logonodpi240);
+ addLabelToRoot(root, "No-dpi resource drawable");
+ addChildToRoot(root, layout);
+
setContentView(scrollWrap(root));
}
@@ -155,7 +198,10 @@ public class DpiTestActivity extends Activity {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- setMeasuredDimension(mBitmap.getScaledWidth(), mBitmap.getScaledHeight());
+ final DisplayMetrics metrics = getResources().getDisplayMetrics();
+ setMeasuredDimension(
+ mBitmap.getScaledWidth(metrics),
+ mBitmap.getScaledHeight(metrics));
}
@Override
diff --git a/tests/DpiTest/src/com/google/android/test/dpi/DpiTestNoCompatActivity.java b/tests/DpiTest/src/com/google/android/test/dpi/DpiTestNoCompatActivity.java
new file mode 100644
index 000000000000..4d25e083e039
--- /dev/null
+++ b/tests/DpiTest/src/com/google/android/test/dpi/DpiTestNoCompatActivity.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+package com.google.android.test.dpi;
+
+public class DpiTestNoCompatActivity extends DpiTestActivity {
+ public DpiTestNoCompatActivity() {
+ super(true);
+ }
+}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java
index 39bbf16b73b2..ba461973fbfb 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LoadTestsAutoTest.java
@@ -73,6 +73,10 @@ public class LoadTestsAutoTest extends ActivityInstrumentationTestCase2<TestShel
runTestAndWaitUntilDone(activity, runner.mTestPath, runner.mTimeoutInMillis);
activity.clearCache();
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ }
dumpMemoryInfo();
// Kill activity
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index 34c6e0b7f523..48c1e5dba4f4 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -91,7 +91,7 @@ public class TestShellActivity extends Activity implements LayoutTestController
}
public void clearCache() {
- mWebView.clearCache(true);
+ mWebView.freeMemory();
}
@Override
@@ -228,8 +228,8 @@ public class TestShellActivity extends Activity implements LayoutTestController
@Override
public void onLowMemory() {
super.onLowMemory();
- Log.e(LOGTAG, "Low memory, kill self");
- System.exit(1);
+ Log.e(LOGTAG, "Low memory, clearing caches");
+ mWebView.freeMemory();
}
// Dump the page
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index 67af116d372d..14cad2f0b8b9 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -654,9 +654,15 @@ bool AaptGroupEntry::getDensityName(const char* name,
ResTable_config* out)
{
if (strcmp(name, kWildcardName) == 0) {
- if (out) out->density = 0;
+ if (out) out->density = ResTable_config::DENSITY_DEFAULT;
return true;
}
+
+ if (strcmp(name, "nodpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_NONE;
+ return true;
+ }
+
char* c = (char*)name;
while (*c >= '0' && *c <= '9') {
c++;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index d0a1c46bf644..fd77d5143d9e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -286,8 +286,8 @@ public final class Bridge implements ILayoutBridge {
}
return computeLayout(layoutDescription, projectKey,
- screenWidth, screenHeight, DisplayMetrics.DEFAULT_DENSITY,
- DisplayMetrics.DEFAULT_DENSITY, DisplayMetrics.DEFAULT_DENSITY,
+ screenWidth, screenHeight, DisplayMetrics.DENSITY_DEFAULT,
+ DisplayMetrics.DENSITY_DEFAULT, DisplayMetrics.DENSITY_DEFAULT,
themeName, isProjectTheme,
projectResources, frameworkResources, customViewLoader, logger);
}
@@ -304,8 +304,8 @@ public final class Bridge implements ILayoutBridge {
Map<String, Map<String, IResourceValue>> frameworkResources,
IProjectCallback customViewLoader, ILayoutLog logger) {
return computeLayout(layoutDescription, projectKey,
- screenWidth, screenHeight, DisplayMetrics.DEFAULT_DENSITY,
- DisplayMetrics.DEFAULT_DENSITY, DisplayMetrics.DEFAULT_DENSITY,
+ screenWidth, screenHeight, DisplayMetrics.DENSITY_DEFAULT,
+ DisplayMetrics.DENSITY_DEFAULT, DisplayMetrics.DENSITY_DEFAULT,
themeName, isProjectTheme,
projectResources, frameworkResources, customViewLoader, logger);
}
@@ -340,7 +340,7 @@ public final class Bridge implements ILayoutBridge {
try {
// setup the display Metrics.
DisplayMetrics metrics = new DisplayMetrics();
- metrics.density = density / (float) DisplayMetrics.DEFAULT_DENSITY;
+ metrics.density = density / (float) DisplayMetrics.DENSITY_DEFAULT;
metrics.scaledDensity = metrics.density;
metrics.widthPixels = screenWidth;
metrics.heightPixels = screenHeight;
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index a2cdcc1d600d..12abce57eafc 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -242,6 +242,7 @@ public class WifiStateTracker extends NetworkStateTracker {
private SettingsObserver mSettingsObserver;
private boolean mIsScanModeActive;
+ private boolean mEnableRssiPolling;
// Wi-Fi run states:
private static final int RUN_STATE_STARTING = 1;
@@ -340,6 +341,7 @@ public class WifiStateTracker extends NetworkStateTracker {
private void setSupplicantState(SupplicantState state) {
mWifiInfo.setSupplicantState(state);
updateNetworkInfo();
+ checkPollTimer();
}
public SupplicantState getSupplicantState() {
@@ -354,6 +356,7 @@ public class WifiStateTracker extends NetworkStateTracker {
private void setSupplicantState(String stateName) {
mWifiInfo.setSupplicantState(stateName);
updateNetworkInfo();
+ checkPollTimer();
}
/**
@@ -550,8 +553,10 @@ public class WifiStateTracker extends NetworkStateTracker {
* Set the interval timer for polling connection information
* that is not delivered asynchronously.
*/
- private synchronized void setPollTimer () {
- if (!hasMessages(EVENT_POLL_INTERVAL)) {
+ private synchronized void checkPollTimer() {
+ if (mEnableRssiPolling &&
+ mWifiInfo.getSupplicantState() == SupplicantState.COMPLETED &&
+ !hasMessages(EVENT_POLL_INTERVAL)) {
sendEmptyMessageDelayed(EVENT_POLL_INTERVAL, POLL_STATUS_INTERVAL_MSECS);
}
}
@@ -651,6 +656,13 @@ public class WifiStateTracker extends NetworkStateTracker {
setBluetoothScanMode(isBluetoothPlaying);
}
+ public void enableRssiPolling(boolean enable) {
+ if (mEnableRssiPolling != enable) {
+ mEnableRssiPolling = enable;
+ checkPollTimer();
+ }
+ }
+
@Override
public void releaseWakeLock() {
if (mReleaseWakeLockCallback != null) {
@@ -1031,9 +1043,7 @@ public class WifiStateTracker extends NetworkStateTracker {
case EVENT_POLL_INTERVAL:
if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
requestPolledInfo(mWifiInfo, true);
- if (mWifiInfo.getSupplicantState() == SupplicantState.COMPLETED) {
- setPollTimer();
- }
+ checkPollTimer();
}
break;
@@ -1170,7 +1180,7 @@ public class WifiStateTracker extends NetworkStateTracker {
}
private void configureInterface() {
- setPollTimer();
+ checkPollTimer();
mLastSignalLevel = -1;
if (!mUseStaticIp) {
if (!mHaveIpAddress && !mObtainingIpAddress) {