summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk3
-rw-r--r--CleanSpec.mk8
-rw-r--r--api/9.xml47
-rw-r--r--api/current.xml812
-rw-r--r--core/java/android/accounts/AccountManager.java142
-rw-r--r--core/java/android/animation/Sequencer.java18
-rw-r--r--core/java/android/app/ActionBar.java29
-rw-r--r--core/java/android/app/Activity.java11
-rw-r--r--core/java/android/app/Dialog.java24
-rw-r--r--core/java/android/app/Fragment.java7
-rw-r--r--core/java/android/app/FragmentManager.java158
-rw-r--r--core/java/android/app/FragmentTransaction.java46
-rw-r--r--core/java/android/app/LoaderManager.java13
-rw-r--r--core/java/android/app/NativeActivity.java41
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java40
-rw-r--r--core/java/android/content/ClipboardManager.java12
-rw-r--r--core/java/android/content/ClippedData.java227
-rw-r--r--core/java/android/content/ContentProvider.java218
-rw-r--r--core/java/android/content/ContentProviderClient.java43
-rw-r--r--core/java/android/content/ContentProviderNative.java77
-rw-r--r--core/java/android/content/ContentResolver.java153
-rw-r--r--core/java/android/content/CursorLoader.java2
-rw-r--r--core/java/android/content/IContentProvider.java8
-rw-r--r--core/java/android/content/Intent.java9
-rw-r--r--core/java/android/content/res/AssetFileDescriptor.java8
-rwxr-xr-xcore/java/android/content/res/Resources.java2
-rw-r--r--core/java/android/database/DatabaseUtils.java14
-rw-r--r--core/java/android/net/DownloadManager.java33
-rw-r--r--core/java/android/net/MobileDataStateTracker.java6
-rw-r--r--core/java/android/net/NetworkStateTracker.java19
-rw-r--r--core/java/android/net/Uri.java20
-rw-r--r--core/java/android/os/AsyncTask.java5
-rw-r--r--core/java/android/os/Looper.java5
-rw-r--r--core/java/android/os/ParcelFileDescriptor.java19
-rw-r--r--core/java/android/os/storage/IMountService.aidl11
-rw-r--r--core/java/android/os/storage/IObbActionListener.aidl34
-rw-r--r--core/java/android/os/storage/StorageManager.java84
-rw-r--r--core/java/android/preference/PreferenceActivity.java13
-rw-r--r--core/java/android/provider/Downloads.java8
-rw-r--r--core/java/android/provider/Mtp.java7
-rw-r--r--core/java/android/util/Finalizers.java127
-rw-r--r--core/java/android/util/JsonReader.java198
-rw-r--r--core/java/android/util/JsonWriter.java29
-rw-r--r--core/java/android/view/GLES20Canvas.java127
-rw-r--r--core/java/android/view/HardwareRenderer.java69
-rw-r--r--core/java/android/view/IWindowManager.aidl2
-rw-r--r--core/java/android/view/InputQueue.java2
-rwxr-xr-xcore/java/android/view/KeyEvent.java2
-rw-r--r--core/java/android/view/ScaleGestureDetector.java4
-rw-r--r--core/java/android/view/VelocityTracker.java236
-rw-r--r--core/java/android/view/View.java234
-rw-r--r--core/java/android/view/ViewDebug.java161
-rw-r--r--core/java/android/view/ViewGroup.java39
-rw-r--r--core/java/android/view/ViewRoot.java32
-rw-r--r--core/java/android/view/Window.java14
-rw-r--r--core/java/android/view/WindowManagerPolicy.java5
-rwxr-xr-xcore/java/android/view/WindowOrientationListener.java245
-rw-r--r--core/java/android/webkit/DeviceOrientationManager.java58
-rw-r--r--core/java/android/webkit/WebView.java20
-rw-r--r--core/java/android/webkit/WebViewCore.java19
-rw-r--r--core/java/android/webkit/WebViewDatabase.java3
-rw-r--r--core/java/android/widget/AbsListView.java6
-rw-r--r--core/java/android/widget/AdapterView.java8
-rw-r--r--core/java/android/widget/AdapterViewAnimator.java427
-rw-r--r--core/java/android/widget/FrameLayout.java21
-rw-r--r--core/java/android/widget/LinearLayout.java22
-rw-r--r--core/java/android/widget/ListPopupWindow.java2
-rw-r--r--core/java/android/widget/ListView.java2
-rw-r--r--core/java/android/widget/PopupWindow.java2
-rw-r--r--core/java/android/widget/ProgressBar.java8
-rw-r--r--core/java/android/widget/RelativeLayout.java4
-rw-r--r--core/java/android/widget/RemoteViewsAdapter.java1
-rw-r--r--core/java/android/widget/ResourceCursorAdapter.java26
-rw-r--r--core/java/android/widget/StackView.java487
-rw-r--r--core/java/android/widget/TableRow.java4
-rw-r--r--core/java/android/widget/TextView.java59
-rw-r--r--core/java/com/android/internal/app/ActionBarImpl.java56
-rwxr-xr-xcore/java/com/android/internal/app/IMediaContainerService.aidl2
-rw-r--r--core/java/com/android/internal/app/PlatLogoActivity.java34
-rw-r--r--core/java/com/android/internal/os/RuntimeInit.java10
-rw-r--r--core/java/com/android/internal/view/StandaloneActionMode.java2
-rw-r--r--core/java/com/android/internal/view/menu/ActionMenuView.java38
-rw-r--r--core/java/com/android/internal/view/menu/MenuPopupHelper.java4
-rw-r--r--core/java/com/android/internal/widget/ActionBarContextView.java21
-rw-r--r--core/java/com/android/internal/widget/ActionBarView.java25
-rw-r--r--core/jni/Android.mk13
-rw-r--r--core/jni/AndroidRuntime.cpp4
-rw-r--r--core/jni/android/graphics/Canvas.cpp13
-rw-r--r--core/jni/android/graphics/ColorFilter.cpp56
-rw-r--r--core/jni/android/graphics/Shader.cpp41
-rw-r--r--core/jni/android/graphics/TextLayout.cpp18
-rw-r--r--core/jni/android/graphics/TextLayout.h3
-rw-r--r--core/jni/android_app_NativeActivity.cpp50
-rw-r--r--core/jni/android_content_res_Configuration.cpp118
-rw-r--r--core/jni/android_os_ParcelFileDescriptor.cpp22
-rw-r--r--core/jni/android_view_GLES20Canvas.cpp179
-rw-r--r--core/jni/android_view_HardwareRenderer.cpp44
-rw-r--r--core/res/AndroidManifest.xml3
-rw-r--r--core/res/res/anim/animator_fade_in.xml26
-rw-r--r--core/res/res/anim/animator_fade_out.xml26
-rw-r--r--core/res/res/anim/fragment_close_enter.xml48
-rw-r--r--core/res/res/anim/fragment_close_exit.xml48
-rw-r--r--core/res/res/anim/fragment_open_enter.xml46
-rw-r--r--core/res/res/anim/fragment_open_exit.xml46
-rw-r--r--core/res/res/drawable-hdpi/btn_check_label_background_light.9.pngbin0 -> 224 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_off.pngbin1693 -> 1238 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_off_disable.pngbin1170 -> 672 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_off_disable_focused.pngbin1568 -> 672 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_off_disable_focused_light.pngbin0 -> 689 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_off_disable_light.pngbin0 -> 689 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_off_light.pngbin0 -> 1945 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_off_pressed.pngbin2413 -> 4311 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_off_pressed_light.pngbin0 -> 2367 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_off_selected.pngbin2378 -> 1924 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_off_selected_light.pngbin0 -> 2260 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_on.pngbin2115 -> 5628 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_on_disable.pngbin1417 -> 4963 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_on_disable_focused.pngbin1788 -> 4963 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_on_disable_focused_light.pngbin0 -> 3715 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_on_disable_light.pngbin0 -> 3715 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_on_light.pngbin0 -> 5059 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_on_pressed.pngbin2586 -> 7963 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_on_pressed_light.pngbin0 -> 5489 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_on_selected.pngbin2546 -> 5603 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_on_selected_light.pngbin0 -> 5162 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_label_background_light.9.pngbin0 -> 216 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_off.pngbin2491 -> 3171 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_off_light.pngbin0 -> 5508 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_off_pressed.pngbin3304 -> 5501 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_off_pressed_light.pngbin0 -> 6469 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_off_selected.pngbin3267 -> 5104 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_off_selected_light.pngbin0 -> 7433 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_on.pngbin2777 -> 6956 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_on_light.pngbin0 -> 7667 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_on_pressed.pngbin3454 -> 9234 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_on_pressed_light.pngbin0 -> 8606 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_on_selected.pngbin3441 -> 7780 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_on_selected_light.pngbin0 -> 9475 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_label_background_light.9.pngbin0 -> 178 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_off.pngbin1172 -> 968 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_off_disable.pngbin903 -> 613 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_off_disable_focused.pngbin1073 -> 613 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_off_disable_focused_light.pngbin0 -> 606 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_off_disable_light.pngbin0 -> 606 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_off_light.pngbin0 -> 1581 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_off_pressed.pngbin1630 -> 2269 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_off_pressed_light.pngbin0 -> 1748 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_off_selected.pngbin1598 -> 1661 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_off_selected_light.pngbin0 -> 1837 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_on.pngbin1390 -> 3109 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_on_disable.pngbin973 -> 2923 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_on_disable_focused.pngbin1138 -> 2923 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_on_disable_focused_light.pngbin0 -> 2100 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_on_disable_light.pngbin0 -> 2100 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_on_light.pngbin0 -> 2874 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_on_pressed.pngbin1680 -> 4053 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_on_pressed_light.pngbin0 -> 3109 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_on_selected.pngbin1661 -> 3340 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_on_selected_light.pngbin0 -> 2877 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_label_background_light.9.pngbin0 -> 178 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_off.pngbin1542 -> 1850 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_off_light.pngbin0 -> 3414 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_off_pressed.pngbin1928 -> 2748 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_off_pressed_light.pngbin0 -> 3749 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_off_selected.pngbin1954 -> 3390 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_off_selected_light.pngbin0 -> 4546 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_on.pngbin1692 -> 3714 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_on_light.pngbin0 -> 4169 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_on_pressed.pngbin1997 -> 4592 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_on_pressed_light.pngbin0 -> 4521 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_on_selected.pngbin2009 -> 4323 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_on_selected_light.pngbin0 -> 5127 bytes
-rw-r--r--core/res/res/drawable-nodpi/platlogo.jpgbin0 -> 102076 bytes
-rw-r--r--core/res/res/drawable/btn_check_light.xml65
-rw-r--r--core/res/res/drawable/btn_radio_light.xml35
-rw-r--r--core/res/res/layout-xlarge/screen_action_bar_overlay.xml47
-rw-r--r--core/res/res/layout/action_mode_bar.xml3
-rw-r--r--core/res/res/layout/preference_list_content.xml31
-rw-r--r--core/res/res/layout/screen_action_bar_overlay.xml53
-rw-r--r--core/res/res/values-cs/strings.xml4
-rw-r--r--core/res/res/values-de/strings.xml2
-rw-r--r--core/res/res/values-fr/strings.xml4
-rw-r--r--core/res/res/values-ru/strings.xml6
-rw-r--r--core/res/res/values-sv/strings.xml2
-rw-r--r--core/res/res/values-tr/strings.xml4
-rw-r--r--core/res/res/values-xlarge/styles.xml1
-rw-r--r--core/res/res/values-zh-rCN/strings.xml4
-rw-r--r--core/res/res/values-zh-rTW/strings.xml28
-rwxr-xr-xcore/res/res/values/attrs.xml18
-rw-r--r--core/res/res/values/public.xml7
-rw-r--r--core/res/res/values/strings.xml23
-rw-r--r--core/res/res/values/styles.xml16
-rw-r--r--core/res/res/values/themes.xml28
-rw-r--r--core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java11
-rw-r--r--core/tests/coretests/src/android/util/JsonReaderTest.java268
-rw-r--r--core/tests/coretests/src/android/util/JsonWriterTest.java4
-rw-r--r--graphics/java/android/graphics/Bitmap.java36
-rw-r--r--graphics/java/android/graphics/Canvas.java81
-rw-r--r--graphics/java/android/graphics/ComposeShader.java22
-rw-r--r--graphics/java/android/graphics/Path.java65
-rw-r--r--graphics/java/android/graphics/PorterDuffXfermode.java6
-rw-r--r--graphics/java/android/graphics/Region.java16
-rw-r--r--graphics/java/android/graphics/Xfermode.java6
-rw-r--r--graphics/java/android/graphics/drawable/GradientDrawable.java13
-rw-r--r--graphics/java/android/renderscript/Matrix4f.java70
-rw-r--r--include/android_runtime/android_content_res_Configuration.h36
-rw-r--r--include/media/stagefright/MediaDefs.h2
-rw-r--r--include/ui/InputReader.h2
-rw-r--r--include/utils/AssetManager.h2
-rw-r--r--include/utils/ObbFile.h4
-rw-r--r--include/utils/ResourceTypes.h119
-rw-r--r--include/utils/String8.h7
-rw-r--r--libs/hwui/Android.mk64
-rw-r--r--libs/hwui/FontRenderer.cpp52
-rw-r--r--libs/hwui/GradientCache.cpp3
-rw-r--r--libs/hwui/OpenGLRenderer.cpp72
-rw-r--r--libs/hwui/PathCache.cpp20
-rw-r--r--libs/hwui/PathCache.h1
-rw-r--r--libs/hwui/Program.cpp2
-rw-r--r--libs/hwui/ProgramCache.cpp37
-rw-r--r--libs/hwui/ProgramCache.h8
-rw-r--r--libs/hwui/SkiaShader.cpp13
-rw-r--r--libs/hwui/SkiaShader.h1
-rw-r--r--libs/hwui/Snapshot.h21
-rw-r--r--libs/hwui/TextureCache.cpp8
-rw-r--r--libs/hwui/TextureCache.h1
-rw-r--r--libs/rs/java/ImageProcessing/src/com/android/rs/image/vertical_blur.rs38
-rw-r--r--libs/rs/rsAllocation.cpp45
-rw-r--r--libs/rs/rsAllocation.h3
-rw-r--r--libs/rs/rsComponent.cpp4
-rw-r--r--libs/rs/rsComponent.h2
-rw-r--r--libs/rs/rsElement.cpp64
-rw-r--r--libs/rs/rsElement.h12
-rw-r--r--libs/rs/scriptc/rs_core.rsh165
-rw-r--r--libs/rs/scriptc/rs_types.rsh4
-rw-r--r--libs/ui/InputReader.cpp8
-rw-r--r--libs/utils/AssetManager.cpp6
-rw-r--r--libs/utils/ObbFile.cpp42
-rw-r--r--libs/utils/tests/Android.mk5
-rw-r--r--libs/utils/tests/String8_test.cpp75
-rw-r--r--media/java/android/media/AudioEffect.java13
-rw-r--r--media/java/android/media/AudioService.java22
-rw-r--r--media/java/android/media/MtpDatabase.java44
-rw-r--r--media/jni/android_media_MtpDatabase.cpp93
-rw-r--r--media/libmedia/AudioEffect.cpp25
-rw-r--r--media/libstagefright/AMRExtractor.cpp1
-rw-r--r--media/libstagefright/Android.mk1
-rw-r--r--media/libstagefright/AwesomePlayer.cpp18
-rw-r--r--media/libstagefright/CameraSource.cpp2
-rw-r--r--media/libstagefright/CameraSourceTimeLapse.cpp5
-rw-r--r--media/libstagefright/HTTPStream.cpp4
-rw-r--r--media/libstagefright/MP3Extractor.cpp1
-rw-r--r--media/libstagefright/MPEG4Extractor.cpp12
-rw-r--r--media/libstagefright/MPEG4Writer.cpp354
-rw-r--r--media/libstagefright/MediaDefs.cpp2
-rw-r--r--media/libstagefright/OMXCodec.cpp13
-rw-r--r--media/libstagefright/OggExtractor.cpp2
-rw-r--r--media/libstagefright/SampleTable.cpp76
-rw-r--r--media/libstagefright/WAVExtractor.cpp117
-rw-r--r--media/libstagefright/codecs/avc/enc/AVCEncoder.cpp39
-rw-r--r--media/libstagefright/codecs/g711/Android.mk4
-rw-r--r--media/libstagefright/codecs/g711/dec/Android.mk12
-rw-r--r--media/libstagefright/codecs/g711/dec/G711Decoder.cpp213
-rw-r--r--media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp10
-rw-r--r--media/libstagefright/include/G711Decoder.h57
-rw-r--r--media/libstagefright/include/SampleTable.h5
-rw-r--r--media/libstagefright/include/WAVExtractor.h1
-rw-r--r--media/libstagefright/matroska/MatroskaExtractor.cpp1
-rw-r--r--media/libstagefright/rtsp/APacketSource.cpp39
-rw-r--r--media/libstagefright/rtsp/ARTPConnection.cpp24
-rw-r--r--media/libstagefright/rtsp/ARTPConnection.h9
-rw-r--r--media/libstagefright/rtsp/ARTPSession.cpp5
-rw-r--r--media/libstagefright/rtsp/ARTPSource.cpp9
-rw-r--r--media/mtp/MtpDataPacket.cpp7
-rw-r--r--media/mtp/MtpDataPacket.h1
-rw-r--r--media/mtp/MtpDatabase.h7
-rw-r--r--media/mtp/MtpDebug.cpp323
-rw-r--r--media/mtp/MtpDebug.h3
-rw-r--r--media/mtp/MtpProperty.cpp4
-rw-r--r--media/mtp/MtpProperty.h2
-rw-r--r--media/mtp/MtpServer.cpp62
-rw-r--r--media/mtp/MtpTypes.h1
-rw-r--r--media/mtp/mtp.h4
-rw-r--r--media/tests/MediaFrameworkTest/AndroidManifest.xml5
-rwxr-xr-xmedia/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPowerTestRunner.java50
-rwxr-xr-xmedia/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java10
-rwxr-xr-xmedia/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java1
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioEffectTest.java1492
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java334
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java397
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java339
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVisualizerTest.java505
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/power/MediaPlayerPowerTest.java81
-rw-r--r--native/android/Android.mk1
-rw-r--r--native/android/asset_manager.cpp2
-rw-r--r--native/android/configuration.cpp202
-rw-r--r--native/copy-to-ndk.sh55
-rw-r--r--native/include/android/asset_manager.h10
-rw-r--r--native/include/android/asset_manager_jni.h40
-rw-r--r--native/include/android/configuration.h319
-rw-r--r--native/include/android/native_activity.h10
-rw-r--r--native/include/android/native_window.h11
-rw-r--r--opengl/libs/EGL/getProcAddress.cpp118
-rw-r--r--opengl/libs/GLES2/gl2.cpp20
-rw-r--r--packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java6
-rw-r--r--packages/SystemUI/res/drawable-hdpi/button_frame_default.9.pngbin0 -> 2878 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/button_frame_pressed.9.pngbin0 -> 5583 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/status_bar_expand_default.pngbin0 -> 1790 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/status_bar_expand_pressed.pngbin0 -> 4328 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/system_panel_airplane_default.pngbin0 -> 4960 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/system_panel_brightness_default.pngbin0 -> 6073 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/system_panel_orientation_default.pngbin0 -> 5144 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/system_panel_orientation_locked.pngbin0 -> 4923 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/system_panel_sound_default.pngbin0 -> 3294 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/button_frame_default.9.pngbin0 -> 1550 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/button_frame_pressed.9.pngbin0 -> 2842 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/status_bar_expand_default.pngbin0 -> 1426 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/status_bar_expand_pressed.pngbin0 -> 2265 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/system_panel_airplane_default.pngbin0 -> 3028 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/system_panel_brightness_default.pngbin0 -> 3683 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/system_panel_orientation_default.pngbin0 -> 3829 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/system_panel_orientation_locked.pngbin0 -> 3447 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/system_panel_sound_default.pngbin0 -> 2040 bytes
-rw-r--r--packages/SystemUI/res/drawable/button_frame.xml21
-rw-r--r--packages/SystemUI/res/drawable/status_bar_expand.xml21
-rw-r--r--packages/SystemUI/res/layout-xlarge/status_bar.xml129
-rw-r--r--packages/SystemUI/res/layout-xlarge/status_bar_center.xml54
-rw-r--r--packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml28
-rw-r--r--packages/SystemUI/res/layout-xlarge/sysbar_panel_system.xml127
-rw-r--r--packages/SystemUI/res/values/strings.xml3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/TickerView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationIconArea.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tablet/SystemPanel.java279
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java228
-rw-r--r--policy/src/com/android/internal/policy/impl/PhoneWindow.java89
-rwxr-xr-xpolicy/src/com/android/internal/policy/impl/PhoneWindowManager.java49
-rw-r--r--services/audioflinger/AudioFlinger.cpp13
-rw-r--r--services/java/com/android/server/ConnectivityService.java21
-rw-r--r--services/java/com/android/server/InputManager.java29
-rw-r--r--services/java/com/android/server/InputMethodManagerService.java29
-rw-r--r--services/java/com/android/server/MountService.java522
-rw-r--r--services/java/com/android/server/ViewServer.java4
-rw-r--r--services/java/com/android/server/WifiService.java402
-rw-r--r--services/java/com/android/server/WifiWatchdogService.java13
-rw-r--r--services/java/com/android/server/WindowManagerService.java173
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java1
-rw-r--r--services/jni/com_android_server_InputManager.cpp78
-rw-r--r--services/surfaceflinger/Android.mk1
-rw-r--r--services/surfaceflinger/DisplayHardware/DisplayHardware.cpp22
-rw-r--r--services/surfaceflinger/DisplayHardware/DisplayHardware.h8
-rw-r--r--services/surfaceflinger/DisplayHardware/HWComposer.cpp96
-rw-r--r--services/surfaceflinger/DisplayHardware/HWComposer.h70
-rw-r--r--services/surfaceflinger/Layer.cpp57
-rw-r--r--services/surfaceflinger/Layer.h2
-rw-r--r--services/surfaceflinger/LayerBase.cpp28
-rw-r--r--services/surfaceflinger/LayerBase.h15
-rw-r--r--services/surfaceflinger/LayerBuffer.cpp2
-rw-r--r--services/surfaceflinger/SurfaceFlinger.cpp212
-rw-r--r--services/surfaceflinger/SurfaceFlinger.h33
-rw-r--r--telephony/java/com/android/internal/telephony/CallManager.java11
-rw-r--r--telephony/java/com/android/internal/telephony/PhoneFactory.java11
-rw-r--r--telephony/java/com/android/internal/telephony/cat/AppInterface.java3
-rw-r--r--telephony/java/com/android/internal/telephony/cat/CatService.java144
-rw-r--r--telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java35
-rw-r--r--telephony/java/com/android/internal/telephony/cat/Input.java4
-rw-r--r--telephony/java/com/android/internal/telephony/cat/ResponseData.java12
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GSMPhone.java4
-rwxr-xr-xtelephony/java/com/android/internal/telephony/sip/SipPhone.java6
-rw-r--r--telephony/java/com/android/internal/telephony/sip/SipPhoneFactory.java46
-rw-r--r--telephony/java/com/android/internal/telephony/sip/SipPhoneProxy.java763
-rw-r--r--test-runner/src/android/test/mock/MockContentProvider.java18
-rw-r--r--test-runner/src/android/test/mock/MockIContentProvider.java10
-rw-r--r--tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java8
-rw-r--r--tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java4
-rw-r--r--tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java9
-rw-r--r--tests/DumpRenderTree2/Android.mk2
-rw-r--r--tests/DumpRenderTree2/AndroidManifest.xml15
-rw-r--r--tests/DumpRenderTree2/assets/run_layout_tests.py65
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/CrashedDummyResult.java85
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSender.java110
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSenderImpl.java563
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java10
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java50
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java77
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java55
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListActivity.java79
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java14
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/OnEverythingFinishedCallback.java25
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/ScriptTestRunner.java37
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/Starter.java74
-rw-r--r--tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java4
-rw-r--r--tests/HwAccelerationTest/AndroidManifest.xml18
-rw-r--r--tests/HwAccelerationTest/src/com/google/android/test/hwui/AdvancedBlendActivity.java138
-rw-r--r--tests/HwAccelerationTest/src/com/google/android/test/hwui/SimplePathsActivity.java40
-rw-r--r--tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java15
-rw-r--r--tools/aapt/Android.mk2
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Canvas.java23
-rw-r--r--tools/localize/Android.mk2
-rw-r--r--tools/obbtool/Android.mk30
-rw-r--r--tools/obbtool/Main.cpp225
-rw-r--r--voip/java/android/net/sip/BinderHelper.java79
-rw-r--r--voip/java/android/net/sip/SipAudioCallImpl.java5
-rw-r--r--voip/java/android/net/sip/SipManager.java3
-rw-r--r--wifi/java/android/net/wifi/IWifiManager.aidl8
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java91
-rw-r--r--wifi/java/android/net/wifi/WifiMonitor.java33
-rw-r--r--wifi/java/android/net/wifi/WifiStateMachine.java3571
-rw-r--r--wifi/java/android/net/wifi/WifiStateTracker.java3812
410 files changed, 19509 insertions, 7553 deletions
diff --git a/Android.mk b/Android.mk
index 7c29c73721c3..65f7a35550be 100644
--- a/Android.mk
+++ b/Android.mk
@@ -123,6 +123,7 @@ LOCAL_SRC_FILES += \
core/java/android/os/storage/IMountService.aidl \
core/java/android/os/storage/IMountServiceListener.aidl \
core/java/android/os/storage/IMountShutdownObserver.aidl \
+ core/java/android/os/storage/IObbActionListener.aidl \
core/java/android/os/INetworkManagementService.aidl \
core/java/android/os/INetStatService.aidl \
core/java/android/os/IPermissionController.aidl \
@@ -368,7 +369,7 @@ framework_docs_LOCAL_DROIDDOC_OPTIONS := \
-since ./frameworks/base/api/7.xml 7 \
-since ./frameworks/base/api/8.xml 8 \
-since ./frameworks/base/api/current.xml HC \
- -error 1 -error 2 -warning 3 -error 4 -error 6 -error 8 \
+ -error 101 -error 102 -warning 103 -error 104 -error 106 -error 108 \
-overview $(LOCAL_PATH)/core/java/overview.html
framework_docs_LOCAL_ADDITIONAL_JAVA_DIR:=$(call intermediates-dir-for,JAVA_LIBRARIES,framework)
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 1b7daa68e3f3..f73e4d5d555c 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -68,6 +68,14 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libreverb_inte
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libreverbtest_intermediates)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/soundfx/)
$(call add-clean-step, find . -type f -name "*.rs" -print0 | xargs -0 touch)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libandroid_runtime_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/libandroid_runtime.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/symbols/system/lib/libandroid_runtime.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libandroid_runtime.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libhwui_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/libhwui.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/symbols/system/lib/libhwui.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libhwui.so)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/api/9.xml b/api/9.xml
index abb67f9a6359..f151a1695ba6 100644
--- a/api/9.xml
+++ b/api/9.xml
@@ -58593,16 +58593,6 @@
<parameter name="bitmap" type="android.graphics.Bitmap">
</parameter>
</constructor>
-<constructor name="Canvas"
- type="android.graphics.Canvas"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="gl" type="javax.microedition.khronos.opengles.GL">
-</parameter>
-</constructor>
<method name="clipPath"
return="boolean"
abstract="false"
@@ -59519,17 +59509,6 @@
<parameter name="paint" type="android.graphics.Paint">
</parameter>
</method>
-<method name="freeGlCaches"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
<method name="getClipBounds"
return="boolean"
abstract="false"
@@ -59576,17 +59555,6 @@
visibility="public"
>
</method>
-<method name="getGL"
- return="javax.microedition.khronos.opengles.GL"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
<method name="getHeight"
return="int"
abstract="false"
@@ -59950,21 +59918,6 @@
<parameter name="matrix" type="android.graphics.Matrix">
</parameter>
</method>
-<method name="setViewport"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="width" type="int">
-</parameter>
-<parameter name="height" type="int">
-</parameter>
-</method>
<method name="skew"
return="void"
abstract="false"
diff --git a/api/current.xml b/api/current.xml
index cc2bb3c12984..2c88f11e9877 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -1482,6 +1482,28 @@
visibility="public"
>
</field>
+<field name="animator_fade_in"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17432609"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="animator_fade_out"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17432610"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="anticipate_interpolator"
type="int"
transient="false"
@@ -4310,6 +4332,50 @@
visibility="public"
>
</field>
+<field name="fragmentCloseEnterAnimation"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843561"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="fragmentCloseExitAnimation"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843562"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="fragmentOpenEnterAnimation"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843559"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="fragmentOpenExitAnimation"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843560"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="freezesText"
type="int"
transient="false"
@@ -10580,6 +10646,17 @@
visibility="public"
>
</field>
+<field name="windowActionBarOverlay"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843558"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="windowActionBarStyle"
type="int"
transient="false"
@@ -20795,6 +20872,19 @@
<parameter name="sequenceItems" type="android.animation.Animatable...">
</parameter>
</method>
+<method name="setTarget"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="target" type="java.lang.Object">
+</parameter>
+</method>
</class>
<class name="Sequencer.Builder"
extends="java.lang.Object"
@@ -20936,6 +21026,17 @@
visibility="public"
>
</method>
+<method name="getHeight"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getNavigationMode"
return="int"
abstract="true"
@@ -20980,6 +21081,17 @@
visibility="public"
>
</method>
+<method name="hide"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="insertTab"
return="void"
abstract="true"
@@ -20995,6 +21107,17 @@
<parameter name="position" type="int">
</parameter>
</method>
+<method name="isShowing"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="newTab"
return="android.app.ActionBar.Tab"
abstract="true"
@@ -21287,6 +21410,17 @@
<parameter name="resId" type="int">
</parameter>
</method>
+<method name="show"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<field name="DISPLAY_HIDE_HOME"
type="int"
transient="false"
@@ -26353,6 +26487,17 @@
<parameter name="id" type="int">
</parameter>
</method>
+<method name="getActionBar"
+ return="android.app.ActionBar"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getContext"
return="android.content.Context"
abstract="false"
@@ -27839,8 +27984,8 @@
<parameter name="savedInstanceState" type="android.os.Bundle">
</parameter>
</method>
-<method name="onCreateAnimation"
- return="android.view.animation.Animation"
+<method name="onCreateAnimatable"
+ return="android.animation.Animatable"
abstract="false"
native="false"
synchronized="false"
@@ -28346,39 +28491,6 @@
<parameter name="fragment" type="android.app.Fragment">
</parameter>
</method>
-<field name="TRANSIT_ACTIVITY_CLOSE"
- type="int"
- transient="false"
- volatile="false"
- value="8199"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TRANSIT_ACTIVITY_OPEN"
- type="int"
- transient="false"
- volatile="false"
- value="4102"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TRANSIT_ENTER"
- type="int"
- transient="false"
- volatile="false"
- value="4097"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
<field name="TRANSIT_ENTER_MASK"
type="int"
transient="false"
@@ -28390,17 +28502,6 @@
visibility="public"
>
</field>
-<field name="TRANSIT_EXIT"
- type="int"
- transient="false"
- volatile="false"
- value="8194"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
<field name="TRANSIT_EXIT_MASK"
type="int"
transient="false"
@@ -28412,88 +28513,33 @@
visibility="public"
>
</field>
-<field name="TRANSIT_HIDE"
- type="int"
- transient="false"
- volatile="false"
- value="8196"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TRANSIT_NONE"
- type="int"
- transient="false"
- volatile="false"
- value="0"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TRANSIT_PREVIEW_DONE"
- type="int"
- transient="false"
- volatile="false"
- value="5"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TRANSIT_SHOW"
- type="int"
- transient="false"
- volatile="false"
- value="4099"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TRANSIT_TASK_CLOSE"
- type="int"
- transient="false"
- volatile="false"
- value="8201"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TRANSIT_TASK_OPEN"
+<field name="TRANSIT_FRAGMENT_CLOSE"
type="int"
transient="false"
volatile="false"
- value="4104"
+ value="8194"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
-<field name="TRANSIT_TASK_TO_BACK"
+<field name="TRANSIT_FRAGMENT_OPEN"
type="int"
transient="false"
volatile="false"
- value="8203"
+ value="4097"
static="true"
final="true"
deprecated="not deprecated"
visibility="public"
>
</field>
-<field name="TRANSIT_TASK_TO_FRONT"
+<field name="TRANSIT_NONE"
type="int"
transient="false"
volatile="false"
- value="4106"
+ value="0"
static="true"
final="true"
deprecated="not deprecated"
@@ -28511,50 +28557,6 @@
visibility="public"
>
</field>
-<field name="TRANSIT_WALLPAPER_CLOSE"
- type="int"
- transient="false"
- volatile="false"
- value="8204"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TRANSIT_WALLPAPER_INTRA_CLOSE"
- type="int"
- transient="false"
- volatile="false"
- value="8207"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TRANSIT_WALLPAPER_INTRA_OPEN"
- type="int"
- transient="false"
- volatile="false"
- value="4110"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="TRANSIT_WALLPAPER_OPEN"
- type="int"
- transient="false"
- volatile="false"
- value="4109"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
</interface>
<class name="Instrumentation"
extends="java.lang.Object"
@@ -30658,6 +30660,17 @@
<parameter name="holder" type="android.view.SurfaceHolder">
</parameter>
</method>
+<field name="KEY_NATIVE_SAVED_STATE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android:native_state&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="META_DATA_LIB_NAME"
type="java.lang.String"
transient="false"
@@ -38871,7 +38884,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
@@ -39106,6 +39119,19 @@
<parameter name="uri" type="android.net.Uri">
</parameter>
</constructor>
+<method name="coerceToText"
+ return="java.lang.CharSequence"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</method>
<method name="getIntent"
return="android.content.Intent"
abstract="false"
@@ -39463,6 +39489,21 @@
<parameter name="values" type="android.content.ContentValues[]">
</parameter>
</method>
+<method name="compareMimeTypes"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="concreteType" type="java.lang.String">
+</parameter>
+<parameter name="desiredType" type="java.lang.String">
+</parameter>
+</method>
<method name="delete"
return="int"
abstract="true"
@@ -39513,6 +39554,21 @@
visibility="public"
>
</method>
+<method name="getStreamTypes"
+ return="java.lang.String[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="mimeTypeFilter" type="java.lang.String">
+</parameter>
+</method>
<method name="getType"
return="java.lang.String"
abstract="true"
@@ -39649,6 +39705,48 @@
<exception name="FileNotFoundException" type="java.io.FileNotFoundException">
</exception>
</method>
+<method name="openPipeHelper"
+ return="android.os.ParcelFileDescriptor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="mimeType" type="java.lang.String">
+</parameter>
+<parameter name="opts" type="android.os.Bundle">
+</parameter>
+<parameter name="args" type="T">
+</parameter>
+<parameter name="func" type="android.content.ContentProvider.PipeDataWriter&lt;T&gt;">
+</parameter>
+<exception name="FileNotFoundException" type="java.io.FileNotFoundException">
+</exception>
+</method>
+<method name="openTypedAssetFile"
+ return="android.content.res.AssetFileDescriptor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="mimeTypeFilter" type="java.lang.String">
+</parameter>
+<parameter name="opts" type="android.os.Bundle">
+</parameter>
+<exception name="FileNotFoundException" type="java.io.FileNotFoundException">
+</exception>
+</method>
<method name="query"
return="android.database.Cursor"
abstract="true"
@@ -39740,6 +39838,35 @@
</parameter>
</method>
</class>
+<interface name="ContentProvider.PipeDataWriter"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="writeDataToPipe"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="output" type="android.os.ParcelFileDescriptor">
+</parameter>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="mimeType" type="java.lang.String">
+</parameter>
+<parameter name="opts" type="android.os.Bundle">
+</parameter>
+<parameter name="args" type="T">
+</parameter>
+</method>
+</interface>
<class name="ContentProviderClient"
extends="java.lang.Object"
abstract="false"
@@ -39812,6 +39939,23 @@
visibility="public"
>
</method>
+<method name="getStreamTypes"
+ return="java.lang.String[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="url" type="android.net.Uri">
+</parameter>
+<parameter name="mimeTypeFilter" type="java.lang.String">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
<method name="getType"
return="java.lang.String"
abstract="false"
@@ -39882,6 +40026,27 @@
<exception name="RemoteException" type="android.os.RemoteException">
</exception>
</method>
+<method name="openTypedAssetFileDescriptor"
+ return="android.content.res.AssetFileDescriptor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="mimeType" type="java.lang.String">
+</parameter>
+<parameter name="opts" type="android.os.Bundle">
+</parameter>
+<exception name="FileNotFoundException" type="java.io.FileNotFoundException">
+</exception>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
<method name="query"
return="android.database.Cursor"
abstract="false"
@@ -40652,6 +40817,21 @@
<parameter name="authority" type="java.lang.String">
</parameter>
</method>
+<method name="getStreamTypes"
+ return="java.lang.String[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="url" type="android.net.Uri">
+</parameter>
+<parameter name="mimeTypeFilter" type="java.lang.String">
+</parameter>
+</method>
<method name="getSyncAdapterTypes"
return="android.content.SyncAdapterType[]"
abstract="false"
@@ -40849,6 +41029,25 @@
<exception name="FileNotFoundException" type="java.io.FileNotFoundException">
</exception>
</method>
+<method name="openTypedAssetFileDescriptor"
+ return="android.content.res.AssetFileDescriptor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="mimeType" type="java.lang.String">
+</parameter>
+<parameter name="opts" type="android.os.Bundle">
+</parameter>
+<exception name="FileNotFoundException" type="java.io.FileNotFoundException">
+</exception>
+</method>
<method name="query"
return="android.database.Cursor"
abstract="false"
@@ -47262,6 +47461,17 @@
visibility="public"
>
</field>
+<field name="ACTION_PASTE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.action.PASTE&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="ACTION_PICK"
type="java.lang.String"
transient="false"
@@ -61715,6 +61925,21 @@
<parameter name="sqlString" type="java.lang.String">
</parameter>
</method>
+<method name="appendSelectionArgs"
+ return="java.lang.String[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="originalValues" type="java.lang.String[]">
+</parameter>
+<parameter name="newValues" type="java.lang.String[]">
+</parameter>
+</method>
<method name="appendValueToSql"
return="void"
abstract="false"
@@ -68809,16 +69034,6 @@
<parameter name="bitmap" type="android.graphics.Bitmap">
</parameter>
</constructor>
-<constructor name="Canvas"
- type="android.graphics.Canvas"
- static="false"
- final="false"
- deprecated="deprecated"
- visibility="public"
->
-<parameter name="gl" type="javax.microedition.khronos.opengles.GL">
-</parameter>
-</constructor>
<method name="clipPath"
return="boolean"
abstract="false"
@@ -69735,17 +69950,6 @@
<parameter name="paint" type="android.graphics.Paint">
</parameter>
</method>
-<method name="freeGlCaches"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="true"
- final="false"
- deprecated="deprecated"
- visibility="public"
->
-</method>
<method name="getClipBounds"
return="boolean"
abstract="false"
@@ -69800,7 +70004,7 @@
static="false"
final="false"
deprecated="deprecated"
- visibility="public"
+ visibility="protected"
>
</method>
<method name="getHeight"
@@ -70177,21 +70381,6 @@
<parameter name="matrix" type="android.graphics.Matrix">
</parameter>
</method>
-<method name="setViewport"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="deprecated"
- visibility="public"
->
-<parameter name="width" type="int">
-</parameter>
-<parameter name="height" type="int">
-</parameter>
-</method>
<method name="skew"
return="void"
abstract="false"
@@ -91630,7 +91819,7 @@
type="int"
transient="false"
volatile="false"
- value="1"
+ value="0"
static="true"
final="true"
deprecated="not deprecated"
@@ -91641,7 +91830,7 @@
type="int"
transient="false"
volatile="false"
- value="0"
+ value="1"
static="true"
final="true"
deprecated="not deprecated"
@@ -102917,19 +103106,6 @@
<parameter name="value" type="java.lang.String">
</parameter>
</method>
-<method name="setShowNotification"
- return="android.net.DownloadManager.Request"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="flags" type="int">
-</parameter>
-</method>
<method name="setTitle"
return="android.net.DownloadManager.Request"
abstract="false"
@@ -102976,17 +103152,6 @@
visibility="public"
>
</field>
-<field name="NOTIFICATION_WHEN_RUNNING"
- type="int"
- transient="false"
- volatile="false"
- value="1"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
</class>
<class name="LocalServerSocket"
extends="java.lang.Object"
@@ -104426,6 +104591,21 @@
visibility="public"
>
</method>
+<method name="getBooleanQueryParameter"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="key" type="java.lang.String">
+</parameter>
+<parameter name="defaultValue" type="boolean">
+</parameter>
+</method>
<method name="getEncodedAuthority"
return="java.lang.String"
abstract="true"
@@ -133993,6 +134173,19 @@
<exception name="IOException" type="java.io.IOException">
</exception>
</method>
+<method name="createPipe"
+ return="android.os.ParcelFileDescriptor[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
<method name="describeContents"
return="int"
abstract="false"
@@ -135983,6 +136176,8 @@
>
<parameter name="filename" type="java.lang.String">
</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
</method>
<method name="isUsbMassStorageConnected"
return="boolean"
@@ -136048,6 +136243,8 @@
</parameter>
<parameter name="force" type="boolean">
</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
</method>
<method name="unregisterListener"
return="void"
@@ -180378,6 +180575,19 @@
<exception name="IOException" type="java.io.IOException">
</exception>
</method>
+<method name="setLenient"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="lenient" type="boolean">
+</parameter>
+</method>
<method name="skipValue"
return="void"
abstract="false"
@@ -180448,6 +180658,8 @@
deprecated="not deprecated"
visibility="public"
>
+<implements name="java.io.Closeable">
+</implements>
<constructor name="JsonWriter"
type="android.util.JsonWriter"
static="false"
@@ -180564,7 +180776,7 @@
<exception name="IOException" type="java.io.IOException">
</exception>
</method>
-<method name="setIndentSpaces"
+<method name="setIndent"
return="void"
abstract="false"
native="false"
@@ -180574,7 +180786,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="indent" type="int">
+<parameter name="indent" type="java.lang.String">
</parameter>
</method>
<method name="value"
@@ -193505,6 +193717,28 @@
visibility="public"
>
</method>
+<method name="getRotationX"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getRotationY"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getScaleX"
return="float"
abstract="false"
@@ -195675,6 +195909,32 @@
<parameter name="rotation" type="float">
</parameter>
</method>
+<method name="setRotationX"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="rotationX" type="float">
+</parameter>
+</method>
+<method name="setRotationY"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="rotationY" type="float">
+</parameter>
+</method>
<method name="setSaveEnabled"
return="void"
abstract="false"
@@ -200879,7 +201139,7 @@
visibility="public"
>
</field>
-<field name="FEATURE_ACTION_MODE_OVERLAY"
+<field name="FEATURE_ACTION_BAR_OVERLAY"
type="int"
transient="false"
volatile="false"
@@ -200890,6 +201150,17 @@
visibility="public"
>
</field>
+<field name="FEATURE_ACTION_MODE_OVERLAY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="10"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="FEATURE_CONTEXT_MENU"
type="int"
transient="false"
@@ -200916,7 +201187,7 @@
type="int"
transient="false"
volatile="false"
- value="10"
+ value="11"
static="true"
final="true"
deprecated="not deprecated"
@@ -213060,6 +213331,17 @@
visibility="public"
>
</method>
+<method name="getVisibleTitleHeight"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getZoomControls"
return="android.view.View"
abstract="false"
@@ -216383,7 +216665,7 @@
</interface>
<class name="AdapterViewAnimator"
extends="android.widget.AdapterView"
- abstract="false"
+ abstract="true"
static="false"
final="false"
deprecated="not deprecated"
@@ -216433,28 +216715,6 @@
visibility="public"
>
</method>
-<method name="getDefaultInAnimation"
- return="android.view.animation.Animation"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
-<method name="getDefaultOutAnimation"
- return="android.view.animation.Animation"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
<method name="getDisplayedChild"
return="int"
abstract="false"
@@ -216653,23 +216913,6 @@
visibility="public"
>
</method>
-<method name="showOnly"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="childIndex" type="int">
-</parameter>
-<parameter name="animate" type="boolean">
-</parameter>
-<parameter name="onLayout" type="boolean">
-</parameter>
-</method>
<method name="showPrevious"
return="void"
abstract="false"
@@ -227845,6 +228088,22 @@
<parameter name="autoRequery" type="boolean">
</parameter>
</constructor>
+<constructor name="ResourceCursorAdapter"
+ type="android.widget.ResourceCursorAdapter"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="layout" type="int">
+</parameter>
+<parameter name="c" type="android.database.Cursor">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</constructor>
<method name="newView"
return="android.view.View"
abstract="false"
@@ -229975,6 +230234,37 @@
</parameter>
</method>
</interface>
+<class name="StackView"
+ extends="android.widget.AdapterViewAnimator"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="StackView"
+ type="android.widget.StackView"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</constructor>
+<constructor name="StackView"
+ type="android.widget.StackView"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+</constructor>
+</class>
<class name="TabHost"
extends="android.widget.FrameLayout"
abstract="false"
@@ -272652,7 +272942,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="loop" type="boolean">
+<parameter name="disable" type="boolean">
</parameter>
<exception name="SocketException" type="java.net.SocketException">
</exception>
@@ -274211,7 +274501,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="value" type="boolean">
+<parameter name="keepAlive" type="boolean">
</parameter>
<exception name="SocketException" type="java.net.SocketException">
</exception>
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index b0adaec3cadd..fd3a0d046486 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -45,26 +45,26 @@ import com.google.android.collect.Maps;
/**
* This class provides access to a centralized registry of the user's
- * online accounts. With this service, users only need to enter their
- * credentials (username and password) once for any account, granting
- * applications access to online resources with "one-click" approval.
+ * online accounts. The user enters credentials (username and password) once
+ * per account, granting applications access to online resources with
+ * "one-click" approval.
*
* <p>Different online services have different ways of handling accounts and
* authentication, so the account manager uses pluggable <em>authenticator</em>
- * modules for different <em>account types</em>. The authenticators (which
- * may be written by third parties) handle the actual details of validating
- * account credentials and storing account information. For example, Google,
- * Facebook, and Microsoft Exchange each have their own authenticator.
+ * modules for different <em>account types</em>. Authenticators (which may be
+ * written by third parties) handle the actual details of validating account
+ * credentials and storing account information. For example, Google, Facebook,
+ * and Microsoft Exchange each have their own authenticator.
*
* <p>Many servers support some notion of an <em>authentication token</em>,
* which can be used to authenticate a request to the server without sending
* the user's actual password. (Auth tokens are normally created with a
* separate request which does include the user's credentials.) AccountManager
- * can generate these auth tokens for applications, so the application doesn't
- * need to handle passwords directly. Auth tokens are normally reusable, and
- * cached by AccountManager, but must be refreshed periodically. It's the
- * responsibility of applications to <em>invalidate</em> auth tokens when they
- * stop working so the AccountManager knows it needs to regenerate them.
+ * can generate auth tokens for applications, so the application doesn't need to
+ * handle passwords directly. Auth tokens are normally reusable and cached by
+ * AccountManager, but must be refreshed periodically. It's the responsibility
+ * of applications to <em>invalidate</em> auth tokens when they stop working so
+ * the AccountManager knows it needs to regenerate them.
*
* <p>Applications accessing a server normally go through these steps:
*
@@ -84,14 +84,19 @@ import com.google.android.collect.Maps;
* {@link #addAccount} may be called to prompt the user to create an
* account of the appropriate type.
*
+ * <li><b>Important:</b> If the application is using a previously remembered
+ * account selection, it must make sure the account is still in the list
+ * of accounts returned by {@link #getAccountsByType}. Requesting an auth token
+ * for an account no longer on the device results in an undefined failure.
+ *
* <li>Request an auth token for the selected account(s) using one of the
* {@link #getAuthToken} methods or related helpers. Refer to the description
* of each method for exact usage and error handling details.
*
* <li>Make the request using the auth token. The form of the auth token,
* the format of the request, and the protocol used are all specific to the
- * service you are accessing. The application makes the request itself, using
- * whatever network and protocol libraries are useful.
+ * service you are accessing. The application may use whatever network and
+ * protocol libraries are useful.
*
* <li><b>Important:</b> If the request fails with an authentication error,
* it could be that a cached auth token is stale and no longer honored by
@@ -103,7 +108,7 @@ import com.google.android.collect.Maps;
* appropriate actions taken.
* </ul>
*
- * <p>Some AccountManager methods may require interaction with the user to
+ * <p>Some AccountManager methods may need to interact with the user to
* prompt for credentials, present options, or ask the user to add an account.
* The caller may choose whether to allow AccountManager to directly launch the
* necessary user interface and wait for the user, or to return an Intent which
@@ -113,18 +118,17 @@ import com.google.android.collect.Maps;
* the current foreground {@link Activity} context.
*
* <p>Many AccountManager methods take {@link AccountManagerCallback} and
- * {@link Handler} as parameters. These methods return immediately but
+ * {@link Handler} as parameters. These methods return immediately and
* run asynchronously. If a callback is provided then
* {@link AccountManagerCallback#run} will be invoked on the Handler's
* thread when the request completes, successfully or not.
- * An {@link AccountManagerFuture} is returned by these requests and also
- * supplied to the callback (if any). The result is retrieved by calling
- * {@link AccountManagerFuture#getResult()} which waits for the operation
- * to complete (if necessary) and either returns the result or throws an
- * exception if an error occurred during the operation.
- * To make the request synchronously, call
+ * The result is retrieved by calling {@link AccountManagerFuture#getResult()}
+ * on the {@link AccountManagerFuture} returned by the method (and also passed
+ * to the callback). This method waits for the operation to complete (if
+ * necessary) and either returns the result or throws an exception if an error
+ * occurred during the operation. To make the request synchronously, call
* {@link AccountManagerFuture#getResult()} immediately on receiving the
- * future from the method. No callback need be supplied.
+ * future from the method; no callback need be supplied.
*
* <p>Requests which may block, including
* {@link AccountManagerFuture#getResult()}, must never be called on
@@ -143,32 +147,32 @@ public class AccountManager {
public static final int ERROR_CODE_BAD_REQUEST = 8;
/**
- * The Bundle key used for the {@link String} account name in results
+ * Bundle key used for the {@link String} account name in results
* from methods which return information about a particular account.
*/
public static final String KEY_ACCOUNT_NAME = "authAccount";
/**
- * The Bundle key used for the {@link String} account type in results
+ * Bundle key used for the {@link String} account type in results
* from methods which return information about a particular account.
*/
public static final String KEY_ACCOUNT_TYPE = "accountType";
/**
- * The Bundle key used for the auth token value in results
+ * Bundle key used for the auth token value in results
* from {@link #getAuthToken} and friends.
*/
public static final String KEY_AUTHTOKEN = "authtoken";
/**
- * The Bundle key used for an {@link Intent} in results from methods that
+ * Bundle key used for an {@link Intent} in results from methods that
* may require the caller to interact with the user. The Intent can
* be used to start the corresponding user interface activity.
*/
public static final String KEY_INTENT = "intent";
/**
- * The Bundle key used to supply the password directly in options to
+ * Bundle key used to supply the password directly in options to
* {@link #confirmCredentials}, rather than prompting the user with
* the standard password prompt.
*/
@@ -476,7 +480,7 @@ public class AccountManager {
* @param account The {@link Account} to add
* @param password The password to associate with the account, null for none
* @param userdata String values to use for the account's userdata, null for none
- * @return Whether the account was successfully added. False if the account
+ * @return True if the account was successfully added, false if the account
* already exists, the account is null, or another error occurs.
*/
public boolean addAccountExplicitly(Account account, String password, Bundle userdata) {
@@ -733,15 +737,14 @@ public class AccountManager {
* sense to ask the user directly for a password.
*
* <p>If a previously generated auth token is cached for this account and
- * type, then it will be returned. Otherwise, if we have a saved password
- * the server accepts, it will be used to generate a new auth token.
- * Otherwise, the user will be asked for a password, which will be sent to
- * the server to generate a new auth token.
+ * type, then it is returned. Otherwise, if a saved password is
+ * available, it is sent to the server to generate a new auth token.
+ * Otherwise, the user is prompted to enter a password.
*
- * <p>The value of the auth token type depends on the authenticator.
- * Some services use different tokens to access different functionality --
- * for example, Google uses different auth tokens to access Gmail and
- * Google Calendar for the same account.
+ * <p>Some authenticators have auth token <em>types</em>, whose value
+ * is authenticator-dependent. Some services use different token types to
+ * access different functionality -- for example, Google uses different auth
+ * tokens to access Gmail and Google Calendar for the same account.
*
* <p>This method may be called from any thread, but the returned
* {@link AccountManagerFuture} must not be used on the main thread.
@@ -778,6 +781,9 @@ public class AccountManager {
* <li> {@link IOException} if the authenticator experienced an I/O problem
* creating a new auth token, usually because of network trouble
* </ul>
+ * If the account is no longer present on the device, the return value is
+ * authenticator-dependent. The caller should verify the validity of the
+ * account before requesting an auth token.
*/
public AccountManagerFuture<Bundle> getAuthToken(
final Account account, final String authTokenType, final Bundle options,
@@ -800,29 +806,27 @@ public class AccountManager {
* user should not be immediately interrupted with a password prompt.
*
* <p>If a previously generated auth token is cached for this account and
- * type, then it will be returned. Otherwise, if we have saved credentials
- * the server accepts, it will be used to generate a new auth token.
- * Otherwise, an Intent will be returned which, when started, will prompt
- * the user for a password. If the notifyAuthFailure parameter is set,
- * the same Intent will be associated with a status bar notification,
+ * type, then it is returned. Otherwise, if a saved password is
+ * available, it is sent to the server to generate a new auth token.
+ * Otherwise, an {@link Intent} is returned which, when started, will
+ * prompt the user for a password. If the notifyAuthFailure parameter is
+ * set, a status bar notification is also created with the same Intent,
* alerting the user that they need to enter a password at some point.
*
- * <p>If the intent is left in a notification, you will need to wait until
- * the user gets around to entering a password before trying again,
- * which could be hours or days or never. When it does happen, the
- * account manager will broadcast the {@link #LOGIN_ACCOUNTS_CHANGED_ACTION}
- * {@link Intent}, which applications can use to trigger another attempt
- * to fetch an auth token.
+ * <p>In that case, you may need to wait until the user responds, which
+ * could take hours or days or forever. When the user does respond and
+ * supply a new password, the account manager will broadcast the
+ * {@link #LOGIN_ACCOUNTS_CHANGED_ACTION} Intent, which applications can
+ * use to try again.
*
- * <p>If notifications are not enabled, it is the application's
- * responsibility to launch the returned intent at some point to let
- * the user enter credentials. In either case, the result from this
- * call will not wait for user action.
+ * <p>If notifyAuthFailure is not set, it is the application's
+ * responsibility to launch the returned Intent at some point.
+ * Either way, the result from this call will not wait for user action.
*
- * <p>The value of the auth token type depends on the authenticator.
- * Some services use different tokens to access different functionality --
- * for example, Google uses different auth tokens to access Gmail and
- * Google Calendar for the same account.
+ * <p>Some authenticators have auth token <em>types</em>, whose value
+ * is authenticator-dependent. Some services use different token types to
+ * access different functionality -- for example, Google uses different auth
+ * tokens to access Gmail and Google Calendar for the same account.
*
* <p>This method may be called from any thread, but the returned
* {@link AccountManagerFuture} must not be used on the main thread.
@@ -851,7 +855,7 @@ public class AccountManager {
* must enter credentials, the returned Bundle contains only
* {@link #KEY_INTENT} with the {@link Intent} needed to launch a prompt.
*
- * <p>If an error occurred, {@link AccountManagerFuture#getResult()} throws:
+ * If an error occurred, {@link AccountManagerFuture#getResult()} throws:
* <ul>
* <li> {@link AuthenticatorException} if the authenticator failed to respond
* <li> {@link OperationCanceledException} if the operation is canceled for
@@ -859,6 +863,9 @@ public class AccountManager {
* <li> {@link IOException} if the authenticator experienced an I/O problem
* creating a new auth token, usually because of network trouble
* </ul>
+ * If the account is no longer present on the device, the return value is
+ * authenticator-dependent. The caller should verify the validity of the
+ * account before requesting an auth token.
*/
public AccountManagerFuture<Bundle> getAuthToken(
final Account account, final String authTokenType, final boolean notifyAuthFailure,
@@ -910,9 +917,8 @@ public class AccountManager {
*
* If no activity was specified, the returned Bundle contains only
* {@link #KEY_INTENT} with the {@link Intent} needed to launch the
- * actual account creation process.
- *
- * <p>If an error occurred, {@link AccountManagerFuture#getResult()} throws:
+ * actual account creation process. If an error occurred,
+ * {@link AccountManagerFuture#getResult()} throws:
* <ul>
* <li> {@link AuthenticatorException} if no authenticator was registered for
* this account type or the authenticator failed to respond
@@ -979,9 +985,8 @@ public class AccountManager {
*
* If no activity or password was specified, the returned Bundle contains
* only {@link #KEY_INTENT} with the {@link Intent} needed to launch the
- * password prompt.
- *
- * <p>If an error occurred, {@link AccountManagerFuture#getResult()} throws:
+ * password prompt. If an error occurred,
+ * {@link AccountManagerFuture#getResult()} throws:
* <ul>
* <li> {@link AuthenticatorException} if the authenticator failed to respond
* <li> {@link OperationCanceledException} if the operation was canceled for
@@ -1040,9 +1045,8 @@ public class AccountManager {
*
* If no activity was specified, the returned Bundle contains only
* {@link #KEY_INTENT} with the {@link Intent} needed to launch the
- * password prompt.
- *
- * <p>If an error occurred, {@link AccountManagerFuture#getResult()} throws:
+ * password prompt. If an error occurred,
+ * {@link AccountManagerFuture#getResult()} throws:
* <ul>
* <li> {@link AuthenticatorException} if the authenticator failed to respond
* <li> {@link OperationCanceledException} if the operation was canceled for
@@ -1091,8 +1095,8 @@ public class AccountManager {
* which is empty if properties were edited successfully, or
* if no activity was specified, contains only {@link #KEY_INTENT}
* needed to launch the authenticator's settings dialog.
- *
- * <p>If an error occurred, {@link AccountManagerFuture#getResult()} throws:
+ * If an error occurred, {@link AccountManagerFuture#getResult()}
+ * throws:
* <ul>
* <li> {@link AuthenticatorException} if no authenticator was registered for
* this account type or the authenticator failed to respond
@@ -1617,7 +1621,7 @@ public class AccountManager {
* <li> {@link #KEY_AUTHTOKEN} - the auth token you wanted
* </ul>
*
- * <p>If an error occurred, {@link AccountManagerFuture#getResult()} throws:
+ * If an error occurred, {@link AccountManagerFuture#getResult()} throws:
* <ul>
* <li> {@link AuthenticatorException} if no authenticator was registered for
* this account type or the authenticator failed to respond
diff --git a/core/java/android/animation/Sequencer.java b/core/java/android/animation/Sequencer.java
index 3278a3e612d3..2406d8af5722 100644
--- a/core/java/android/animation/Sequencer.java
+++ b/core/java/android/animation/Sequencer.java
@@ -140,6 +140,24 @@ public final class Sequencer extends Animatable {
}
/**
+ * Sets the target object for all current {@link #getChildAnimations() child animations}
+ * of this Sequencer that take targets ({@link android.animation.PropertyAnimator} and
+ * Sequencer).
+ *
+ * @param target The object being animated
+ */
+ public void setTarget(Object target) {
+ for (Node node : mNodes) {
+ Animatable animation = node.animation;
+ if (animation instanceof Sequencer) {
+ ((Sequencer)animation).setTarget(target);
+ } else if (animation instanceof PropertyAnimator) {
+ ((PropertyAnimator)animation).setTarget(target);
+ }
+ }
+ }
+
+ /**
* This method creates a <code>Builder</code> object, which is used to
* set up playing constraints. This initial <code>play()</code> method
* tells the <code>Builder</code> the animation that is the dependency for
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
index d33494b7ac51..38086f072745 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -18,6 +18,7 @@ package android.app;
import android.graphics.drawable.Drawable;
import android.view.View;
+import android.view.Window;
import android.widget.SpinnerAdapter;
/**
@@ -383,6 +384,34 @@ public abstract class ActionBar {
public abstract void selectTab(Tab tab);
/**
+ * Retrieve the current height of the ActionBar.
+ *
+ * @return The ActionBar's height
+ */
+ public abstract int getHeight();
+
+ /**
+ * Show the ActionBar if it is not currently showing.
+ * If the window hosting the ActionBar does not have the feature
+ * {@link Window#FEATURE_ACTION_BAR_OVERLAY} it will resize application
+ * content to fit the new space available.
+ */
+ public abstract void show();
+
+ /**
+ * Hide the ActionBar if it is not currently showing.
+ * If the window hosting the ActionBar does not have the feature
+ * {@link Window#FEATURE_ACTION_BAR_OVERLAY} it will resize application
+ * content to fit the new space available.
+ */
+ public abstract void hide();
+
+ /**
+ * @return <code>true</code> if the ActionBar is showing, <code>false</code> otherwise.
+ */
+ public abstract boolean isShowing();
+
+ /**
* Callback interface for ActionBar navigation events.
*/
public interface NavigationCallback {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index d49adc23554e..d7bab1bc8080 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1748,13 +1748,11 @@ public class Activity extends ContextThemeWrapper
/**
* Retrieve a reference to this activity's ActionBar.
- *
- * <p><em>Note:</em> The ActionBar is initialized when a content view
- * is set. This function will return null if called before {@link #setContentView}
- * or {@link #addContentView}.
+ *
* @return The Activity's ActionBar, or null if it does not have one.
*/
public ActionBar getActionBar() {
+ initActionBar();
return mActionBar;
}
@@ -1764,7 +1762,7 @@ public class Activity extends ContextThemeWrapper
*/
private void initActionBar() {
Window window = getWindow();
- if (!window.hasFeature(Window.FEATURE_ACTION_BAR) || mActionBar != null) {
+ if (isChild() || !window.hasFeature(Window.FEATURE_ACTION_BAR) || mActionBar != null) {
return;
}
@@ -2074,7 +2072,7 @@ public class Activity extends ContextThemeWrapper
* to pop, else false.
*/
public boolean popBackStack() {
- return popBackStack(null, -1);
+ return popBackStack(null, 0);
}
/**
@@ -4093,6 +4091,7 @@ public class Activity extends ContextThemeWrapper
}
public ActionMode onStartActionMode(ActionMode.Callback callback) {
+ initActionBar();
if (mActionBar != null) {
return mActionBar.startActionMode(callback);
}
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index a9420b4edeff..b4c138e1660e 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -16,6 +16,7 @@
package android.app;
+import com.android.internal.app.ActionBarImpl;
import com.android.internal.policy.PolicyManager;
import android.content.ComponentName;
@@ -27,9 +28,9 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.view.ActionMode;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
-import android.view.ActionMode;
import android.view.ContextThemeWrapper;
import android.view.Gravity;
import android.view.KeyEvent;
@@ -77,6 +78,7 @@ public class Dialog implements DialogInterface, Window.Callback,
final WindowManager mWindowManager;
Window mWindow;
View mDecor;
+ private ActionBarImpl mActionBar;
/**
* This field should be made private, so it is hidden from the SDK.
* {@hide}
@@ -178,6 +180,15 @@ public class Dialog implements DialogInterface, Window.Callback,
}
/**
+ * Retrieve the {@link ActionBar} attached to this dialog, if present.
+ *
+ * @return The ActionBar attached to the dialog or null if no ActionBar is present.
+ */
+ public ActionBar getActionBar() {
+ return mActionBar;
+ }
+
+ /**
* Sets the Activity that owns this dialog. An example use: This Dialog will
* use the suggested volume control stream of the Activity.
*
@@ -228,6 +239,11 @@ public class Dialog implements DialogInterface, Window.Callback,
onStart();
mDecor = mWindow.getDecorView();
+
+ if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
+ mActionBar = new ActionBarImpl(this);
+ }
+
WindowManager.LayoutParams l = mWindow.getAttributes();
if ((l.softInputMode
& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
@@ -834,12 +850,14 @@ public class Dialog implements DialogInterface, Window.Callback,
}
public ActionMode onStartActionMode(ActionMode.Callback callback) {
- // TODO Support context modes in dialogs
+ if (mActionBar != null) {
+ return mActionBar.startActionMode(callback);
+ }
return null;
}
/**
- * @return The activity associated with this dialog, or null if there is no assocaited activity.
+ * @return The activity associated with this dialog, or null if there is no associated activity.
*/
private ComponentName getAssociatedActivity() {
Activity activity = mOwnerActivity;
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 0bb200c95353..2f61345e4195 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -16,6 +16,7 @@
package android.app;
+import android.animation.Animatable;
import android.content.ComponentCallbacks;
import android.content.Context;
import android.content.Intent;
@@ -35,7 +36,6 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.OnCreateContextMenuListener;
-import android.view.animation.Animation;
import android.widget.AdapterView;
import java.lang.reflect.InvocationTargetException;
@@ -495,7 +495,10 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
mCalled = true;
}
- public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
+ /**
+ * Called when a fragment loads an animation.
+ */
+ public Animatable onCreateAnimatable(int transit, boolean enter, int nextAnim) {
return null;
}
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 35b461011f66..54e37b0df573 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -16,6 +16,9 @@
package android.app;
+import android.animation.Animatable;
+import android.animation.PropertyAnimator;
+import android.animation.Sequencer;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.os.Handler;
@@ -28,7 +31,6 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import java.util.ArrayList;
@@ -106,16 +108,16 @@ public class FragmentManager {
}
};
- Animation loadAnimation(Fragment fragment, int transit, boolean enter,
+ Animatable loadAnimatable(Fragment fragment, int transit, boolean enter,
int transitionStyle) {
- Animation animObj = fragment.onCreateAnimation(transitionStyle, enter,
+ Animatable animObj = fragment.onCreateAnimatable(transit, enter,
fragment.mNextAnim);
if (animObj != null) {
return animObj;
}
if (fragment.mNextAnim != 0) {
- Animation anim = AnimationUtils.loadAnimation(mActivity, fragment.mNextAnim);
+ Animatable anim = AnimationUtils.loadAnimator(mActivity, fragment.mNextAnim);
if (anim != null) {
return anim;
}
@@ -138,7 +140,7 @@ public class FragmentManager {
}
TypedArray attrs = mActivity.obtainStyledAttributes(transitionStyle,
- com.android.internal.R.styleable.WindowAnimation);
+ com.android.internal.R.styleable.FragmentAnimation);
int anim = attrs.getResourceId(styleIndex, 0);
attrs.recycle();
@@ -146,7 +148,7 @@ public class FragmentManager {
return null;
}
- return AnimationUtils.loadAnimation(mActivity, anim);
+ return AnimationUtils.loadAnimator(mActivity, anim);
}
void moveToState(Fragment f, int newState, int transit, int transitionStyle) {
@@ -208,10 +210,15 @@ public class FragmentManager {
if (f.mView != null) {
f.mView.setSaveFromParentEnabled(false);
if (container != null) {
- Animation anim = loadAnimation(f, transit, true,
+ Animatable anim = loadAnimatable(f, transit, true,
transitionStyle);
if (anim != null) {
- f.mView.setAnimation(anim);
+ if (anim instanceof Sequencer) {
+ ((Sequencer)anim).setTarget(f.mView);
+ } else if (anim instanceof PropertyAnimator) {
+ ((PropertyAnimator)anim).setTarget(f.mView);
+ }
+ anim.start();
}
container.addView(f.mView);
f.restoreViewState();
@@ -290,10 +297,15 @@ public class FragmentManager {
}
if (f.mContainer != null) {
if (mCurState > Fragment.INITIALIZING) {
- Animation anim = loadAnimation(f, transit, false,
+ Animatable anim = loadAnimatable(f, transit, true,
transitionStyle);
if (anim != null) {
- f.mView.setAnimation(anim);
+ if (anim instanceof Sequencer) {
+ ((Sequencer)anim).setTarget(f.mView);
+ } else if (anim instanceof PropertyAnimator) {
+ ((PropertyAnimator)anim).setTarget(f.mView);
+ }
+ anim.start();
}
}
f.mContainer.removeView(f.mView);
@@ -420,10 +432,15 @@ public class FragmentManager {
if (!fragment.mHidden) {
fragment.mHidden = true;
if (fragment.mView != null) {
- Animation anim = loadAnimation(fragment, transition, false,
+ Animatable anim = loadAnimatable(fragment, transition, true,
transitionStyle);
if (anim != null) {
- fragment.mView.setAnimation(anim);
+ if (anim instanceof Sequencer) {
+ ((Sequencer)anim).setTarget(fragment.mView);
+ } else if (anim instanceof PropertyAnimator) {
+ ((PropertyAnimator)anim).setTarget(fragment.mView);
+ }
+ anim.start();
}
fragment.mView.setVisibility(View.GONE);
}
@@ -439,10 +456,15 @@ public class FragmentManager {
if (fragment.mHidden) {
fragment.mHidden = false;
if (fragment.mView != null) {
- Animation anim = loadAnimation(fragment, transition, true,
+ Animatable anim = loadAnimatable(fragment, transition, true,
transitionStyle);
if (anim != null) {
- fragment.mView.setAnimation(anim);
+ if (anim instanceof Sequencer) {
+ ((Sequencer)anim).setTarget(fragment.mView);
+ } else if (anim instanceof PropertyAnimator) {
+ ((PropertyAnimator)anim).setTarget(fragment.mView);
+ }
+ anim.start();
}
fragment.mView.setVisibility(View.VISIBLE);
}
@@ -981,47 +1003,11 @@ public class FragmentManager {
public static int reverseTransit(int transit) {
int rev = 0;
switch (transit) {
- case FragmentTransaction.TRANSIT_ENTER:
- rev = FragmentTransaction.TRANSIT_EXIT;
- break;
- case FragmentTransaction.TRANSIT_EXIT:
- rev = FragmentTransaction.TRANSIT_ENTER;
- break;
- case FragmentTransaction.TRANSIT_SHOW:
- rev = FragmentTransaction.TRANSIT_HIDE;
- break;
- case FragmentTransaction.TRANSIT_HIDE:
- rev = FragmentTransaction.TRANSIT_SHOW;
- break;
- case FragmentTransaction.TRANSIT_ACTIVITY_OPEN:
- rev = FragmentTransaction.TRANSIT_ACTIVITY_CLOSE;
- break;
- case FragmentTransaction.TRANSIT_ACTIVITY_CLOSE:
- rev = FragmentTransaction.TRANSIT_ACTIVITY_OPEN;
- break;
- case FragmentTransaction.TRANSIT_TASK_OPEN:
- rev = FragmentTransaction.TRANSIT_TASK_CLOSE;
- break;
- case FragmentTransaction.TRANSIT_TASK_CLOSE:
- rev = FragmentTransaction.TRANSIT_TASK_OPEN;
+ case FragmentTransaction.TRANSIT_FRAGMENT_OPEN:
+ rev = FragmentTransaction.TRANSIT_FRAGMENT_CLOSE;
break;
- case FragmentTransaction.TRANSIT_TASK_TO_FRONT:
- rev = FragmentTransaction.TRANSIT_TASK_TO_BACK;
- break;
- case FragmentTransaction.TRANSIT_TASK_TO_BACK:
- rev = FragmentTransaction.TRANSIT_TASK_TO_FRONT;
- break;
- case FragmentTransaction.TRANSIT_WALLPAPER_OPEN:
- rev = FragmentTransaction.TRANSIT_WALLPAPER_CLOSE;
- break;
- case FragmentTransaction.TRANSIT_WALLPAPER_CLOSE:
- rev = FragmentTransaction.TRANSIT_WALLPAPER_OPEN;
- break;
- case FragmentTransaction.TRANSIT_WALLPAPER_INTRA_OPEN:
- rev = FragmentTransaction.TRANSIT_WALLPAPER_INTRA_CLOSE;
- break;
- case FragmentTransaction.TRANSIT_WALLPAPER_INTRA_CLOSE:
- rev = FragmentTransaction.TRANSIT_WALLPAPER_INTRA_OPEN;
+ case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE:
+ rev = FragmentTransaction.TRANSIT_FRAGMENT_OPEN;
break;
}
return rev;
@@ -1031,67 +1017,15 @@ public class FragmentManager {
public static int transitToStyleIndex(int transit, boolean enter) {
int animAttr = -1;
switch (transit) {
- case FragmentTransaction.TRANSIT_ENTER:
- animAttr = com.android.internal.R.styleable.WindowAnimation_windowEnterAnimation;
- break;
- case FragmentTransaction.TRANSIT_EXIT:
- animAttr = com.android.internal.R.styleable.WindowAnimation_windowExitAnimation;
- break;
- case FragmentTransaction.TRANSIT_SHOW:
- animAttr = com.android.internal.R.styleable.WindowAnimation_windowShowAnimation;
- break;
- case FragmentTransaction.TRANSIT_HIDE:
- animAttr = com.android.internal.R.styleable.WindowAnimation_windowHideAnimation;
- break;
- case FragmentTransaction.TRANSIT_ACTIVITY_OPEN:
- animAttr = enter
- ? com.android.internal.R.styleable.WindowAnimation_activityOpenEnterAnimation
- : com.android.internal.R.styleable.WindowAnimation_activityOpenExitAnimation;
- break;
- case FragmentTransaction.TRANSIT_ACTIVITY_CLOSE:
- animAttr = enter
- ? com.android.internal.R.styleable.WindowAnimation_activityCloseEnterAnimation
- : com.android.internal.R.styleable.WindowAnimation_activityCloseExitAnimation;
- break;
- case FragmentTransaction.TRANSIT_TASK_OPEN:
- animAttr = enter
- ? com.android.internal.R.styleable.WindowAnimation_taskOpenEnterAnimation
- : com.android.internal.R.styleable.WindowAnimation_taskOpenExitAnimation;
- break;
- case FragmentTransaction.TRANSIT_TASK_CLOSE:
- animAttr = enter
- ? com.android.internal.R.styleable.WindowAnimation_taskCloseEnterAnimation
- : com.android.internal.R.styleable.WindowAnimation_taskCloseExitAnimation;
- break;
- case FragmentTransaction.TRANSIT_TASK_TO_FRONT:
- animAttr = enter
- ? com.android.internal.R.styleable.WindowAnimation_taskToFrontEnterAnimation
- : com.android.internal.R.styleable.WindowAnimation_taskToFrontExitAnimation;
- break;
- case FragmentTransaction.TRANSIT_TASK_TO_BACK:
- animAttr = enter
- ? com.android.internal.R.styleable.WindowAnimation_taskToBackEnterAnimation
- : com.android.internal.R.styleable.WindowAnimation_taskToBackExitAnimation;
- break;
- case FragmentTransaction.TRANSIT_WALLPAPER_OPEN:
- animAttr = enter
- ? com.android.internal.R.styleable.WindowAnimation_wallpaperOpenEnterAnimation
- : com.android.internal.R.styleable.WindowAnimation_wallpaperOpenExitAnimation;
- break;
- case FragmentTransaction.TRANSIT_WALLPAPER_CLOSE:
- animAttr = enter
- ? com.android.internal.R.styleable.WindowAnimation_wallpaperCloseEnterAnimation
- : com.android.internal.R.styleable.WindowAnimation_wallpaperCloseExitAnimation;
- break;
- case FragmentTransaction.TRANSIT_WALLPAPER_INTRA_OPEN:
+ case FragmentTransaction.TRANSIT_FRAGMENT_OPEN:
animAttr = enter
- ? com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenEnterAnimation
- : com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation;
+ ? com.android.internal.R.styleable.FragmentAnimation_fragmentOpenEnterAnimation
+ : com.android.internal.R.styleable.FragmentAnimation_fragmentOpenExitAnimation;
break;
- case FragmentTransaction.TRANSIT_WALLPAPER_INTRA_CLOSE:
+ case FragmentTransaction.TRANSIT_FRAGMENT_CLOSE:
animAttr = enter
- ? com.android.internal.R.styleable.WindowAnimation_wallpaperIntraCloseEnterAnimation
- : com.android.internal.R.styleable.WindowAnimation_wallpaperIntraCloseExitAnimation;
+ ? com.android.internal.R.styleable.FragmentAnimation_fragmentCloseEnterAnimation
+ : com.android.internal.R.styleable.FragmentAnimation_fragmentCloseExitAnimation;
break;
}
return animAttr;
diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java
index 65cf85cff724..04598a375c90 100644
--- a/core/java/android/app/FragmentTransaction.java
+++ b/core/java/android/app/FragmentTransaction.java
@@ -100,47 +100,11 @@ public interface FragmentTransaction {
public final int TRANSIT_UNSET = -1;
/** No animation for transition. */
public final int TRANSIT_NONE = 0;
- /** Window has been added to the screen. */
- public final int TRANSIT_ENTER = 1 | TRANSIT_ENTER_MASK;
- /** Window has been removed from the screen. */
- public final int TRANSIT_EXIT = 2 | TRANSIT_EXIT_MASK;
- /** Window has been made visible. */
- public final int TRANSIT_SHOW = 3 | TRANSIT_ENTER_MASK;
- /** Window has been made invisible. */
- public final int TRANSIT_HIDE = 4 | TRANSIT_EXIT_MASK;
- /** The "application starting" preview window is no longer needed, and will
- * animate away to show the real window. */
- public final int TRANSIT_PREVIEW_DONE = 5;
- /** A window in a new activity is being opened on top of an existing one
- * in the same task. */
- public final int TRANSIT_ACTIVITY_OPEN = 6 | TRANSIT_ENTER_MASK;
- /** The window in the top-most activity is being closed to reveal the
- * previous activity in the same task. */
- public final int TRANSIT_ACTIVITY_CLOSE = 7 | TRANSIT_EXIT_MASK;
- /** A window in a new task is being opened on top of an existing one
- * in another activity's task. */
- public final int TRANSIT_TASK_OPEN = 8 | TRANSIT_ENTER_MASK;
- /** A window in the top-most activity is being closed to reveal the
- * previous activity in a different task. */
- public final int TRANSIT_TASK_CLOSE = 9 | TRANSIT_EXIT_MASK;
- /** A window in an existing task is being displayed on top of an existing one
- * in another activity's task. */
- public final int TRANSIT_TASK_TO_FRONT = 10 | TRANSIT_ENTER_MASK;
- /** A window in an existing task is being put below all other tasks. */
- public final int TRANSIT_TASK_TO_BACK = 11 | TRANSIT_EXIT_MASK;
- /** A window in a new activity that doesn't have a wallpaper is being
- * opened on top of one that does, effectively closing the wallpaper. */
- public final int TRANSIT_WALLPAPER_CLOSE = 12 | TRANSIT_EXIT_MASK;
- /** A window in a new activity that does have a wallpaper is being
- * opened on one that didn't, effectively opening the wallpaper. */
- public final int TRANSIT_WALLPAPER_OPEN = 13 | TRANSIT_ENTER_MASK;
- /** A window in a new activity is being opened on top of an existing one,
- * and both are on top of the wallpaper. */
- public final int TRANSIT_WALLPAPER_INTRA_OPEN = 14 | TRANSIT_ENTER_MASK;
- /** The window in the top-most activity is being closed to reveal the
- * previous activity, and both are on top of he wallpaper. */
- public final int TRANSIT_WALLPAPER_INTRA_CLOSE = 15 | TRANSIT_EXIT_MASK;
-
+ /** Fragment is being added */
+ public final int TRANSIT_FRAGMENT_OPEN = 1 | TRANSIT_ENTER_MASK;
+ /** Fragment is being removed */
+ public final int TRANSIT_FRAGMENT_CLOSE = 2 | TRANSIT_EXIT_MASK;
+
public FragmentTransaction setCustomAnimations(int enter, int exit);
public FragmentTransaction setTransition(int transit);
diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java
index e7bdd8b2731e..28abcaa7735f 100644
--- a/core/java/android/app/LoaderManager.java
+++ b/core/java/android/app/LoaderManager.java
@@ -185,11 +185,14 @@ class LoaderManagerImpl implements LoaderManager {
void stop() {
if (DEBUG) Log.v(TAG, " Stopping: " + this);
mStarted = false;
- if (mLoader != null && mListenerRegistered) {
- // Let the loader know we're done with it
- mListenerRegistered = false;
- mLoader.unregisterListener(this);
- mLoader.stopLoading();
+ if (!mRetaining) {
+ if (mLoader != null && mListenerRegistered) {
+ // Let the loader know we're done with it
+ mListenerRegistered = false;
+ mLoader.unregisterListener(this);
+ mLoader.stopLoading();
+ }
+ mData = null;
}
}
diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java
index eaf06757b6f7..4dc88b31f1d9 100644
--- a/core/java/android/app/NativeActivity.java
+++ b/core/java/android/app/NativeActivity.java
@@ -10,6 +10,7 @@ import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
+import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.os.Build;
import android.os.Bundle;
@@ -32,12 +33,27 @@ import java.lang.ref.WeakReference;
/**
* Convenience for implementing an activity that will be implemented
- * purely in native code. That is, a game (or game-like thing).
+ * purely in native code. That is, a game (or game-like thing). There
+ * is no need to derive from this class; you can simply declare it in your
+ * manifest, and use the NDK APIs from there.
+ *
+ * <p>A typical manifest would look like:
+ *
+ * {@sample development/ndk/platforms/android-9/samples/native-activity/AndroidManifest.xml
+ * manifest}
+ *
+ * <p>A very simple example of native code that is run by NativeActivity
+ * follows. This reads input events from the user and uses OpenGLES to
+ * draw into the native activity's window.
+ *
+ * {@sample development/ndk/platforms/android-9/samples/native-activity/jni/main.c all}
*/
public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
InputQueue.Callback, OnGlobalLayoutListener {
public static final String META_DATA_LIB_NAME = "android.app.lib_name";
+ public static final String KEY_NATIVE_SAVED_STATE = "android:native_state";
+
private NativeContentView mNativeContentView;
private InputMethodManager mIMM;
private InputMethodCallback mInputMethodCallback;
@@ -59,14 +75,15 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
private native int loadNativeCode(String path, MessageQueue queue,
String internalDataPath, String externalDataPath, int sdkVersion,
- AssetManager assetMgr);
+ AssetManager assetMgr, byte[] savedState);
private native void unloadNativeCode(int handle);
private native void onStartNative(int handle);
private native void onResumeNative(int handle);
- private native void onSaveInstanceStateNative(int handle);
+ private native byte[] onSaveInstanceStateNative(int handle);
private native void onPauseNative(int handle);
private native void onStopNative(int handle);
+ private native void onConfigurationChangedNative(int handle);
private native void onLowMemoryNative(int handle);
private native void onWindowFocusChangedNative(int handle, boolean focused);
private native void onSurfaceCreatedNative(int handle, Surface surface);
@@ -165,10 +182,13 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
throw new IllegalArgumentException("Unable to find native library: " + libname);
}
+ byte[] nativeSavedState = savedInstanceState != null
+ ? savedInstanceState.getByteArray(KEY_NATIVE_SAVED_STATE) : null;
+
mNativeHandle = loadNativeCode(path, Looper.myQueue(),
getFilesDir().toString(),
Environment.getExternalStorageAppFilesDirectory(ai.packageName).toString(),
- Build.VERSION.SDK_INT, getAssets());
+ Build.VERSION.SDK_INT, getAssets(), nativeSavedState);
if (mNativeHandle == 0) {
throw new IllegalArgumentException("Unable to load native library: " + path);
@@ -206,7 +226,10 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
- onSaveInstanceStateNative(mNativeHandle);
+ byte[] state = onSaveInstanceStateNative(mNativeHandle);
+ if (state != null) {
+ outState.putByteArray(KEY_NATIVE_SAVED_STATE, state);
+ }
}
@Override
@@ -222,6 +245,14 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
}
@Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ if (!mDestroyed) {
+ onConfigurationChangedNative(mNativeHandle);
+ }
+ }
+
+ @Override
public void onLowMemory() {
super.onLowMemory();
if (!mDestroyed) {
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 8eda844380e0..03bcadc69b85 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -468,12 +468,17 @@ public final class BluetoothAdapter {
* <p>Valid Bluetooth names are a maximum of 248 UTF-8 characters, however
* many remote devices can only display the first 40 characters, and some
* may be limited to just 20.
+ * <p>If Bluetooth state is not {@link #STATE_ON}, this API
+ * will return false. After turning on Bluetooth,
+ * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
+ * to get the updated value.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
*
* @param name a valid Bluetooth name
* @return true if the name was set, false otherwise
*/
public boolean setName(String name) {
+ if (getState() != STATE_ON) return false;
try {
return mService.setName(name);
} catch (RemoteException e) {Log.e(TAG, "", e);}
@@ -488,11 +493,16 @@ public final class BluetoothAdapter {
* {@link #SCAN_MODE_NONE},
* {@link #SCAN_MODE_CONNECTABLE},
* {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}.
+ * <p>If Bluetooth state is not {@link #STATE_ON}, this API
+ * will return {@link #SCAN_MODE_NONE}. After turning on Bluetooth,
+ * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
+ * to get the updated value.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*
* @return scan mode
*/
public int getScanMode() {
+ if (getState() != STATE_ON) return SCAN_MODE_NONE;
try {
return mService.getScanMode();
} catch (RemoteException e) {Log.e(TAG, "", e);}
@@ -511,6 +521,10 @@ public final class BluetoothAdapter {
* {@link #SCAN_MODE_NONE},
* {@link #SCAN_MODE_CONNECTABLE},
* {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}.
+ * <p>If Bluetooth state is not {@link #STATE_ON}, this API
+ * will return false. After turning on Bluetooth,
+ * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
+ * to get the updated value.
* <p>Requires {@link android.Manifest.permission#WRITE_SECURE_SETTINGS}
* <p>Applications cannot set the scan mode. They should use
* <code>startActivityForResult(
@@ -524,6 +538,7 @@ public final class BluetoothAdapter {
* @hide
*/
public boolean setScanMode(int mode, int duration) {
+ if (getState() != STATE_ON) return false;
try {
return mService.setScanMode(mode, duration);
} catch (RemoteException e) {Log.e(TAG, "", e);}
@@ -532,11 +547,13 @@ public final class BluetoothAdapter {
/** @hide */
public boolean setScanMode(int mode) {
+ if (getState() != STATE_ON) return false;
return setScanMode(mode, 120);
}
/** @hide */
public int getDiscoverableTimeout() {
+ if (getState() != STATE_ON) return -1;
try {
return mService.getDiscoverableTimeout();
} catch (RemoteException e) {Log.e(TAG, "", e);}
@@ -545,6 +562,7 @@ public final class BluetoothAdapter {
/** @hide */
public void setDiscoverableTimeout(int timeout) {
+ if (getState() != STATE_ON) return;
try {
mService.setDiscoverableTimeout(timeout);
} catch (RemoteException e) {Log.e(TAG, "", e);}
@@ -572,11 +590,16 @@ public final class BluetoothAdapter {
* <p>Device discovery will only find remote devices that are currently
* <i>discoverable</i> (inquiry scan enabled). Many Bluetooth devices are
* not discoverable by default, and need to be entered into a special mode.
+ * <p>If Bluetooth state is not {@link #STATE_ON}, this API
+ * will return false. After turning on Bluetooth,
+ * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
+ * to get the updated value.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
*
* @return true on success, false on error
*/
public boolean startDiscovery() {
+ if (getState() != STATE_ON) return false;
try {
return mService.startDiscovery();
} catch (RemoteException e) {Log.e(TAG, "", e);}
@@ -593,10 +616,15 @@ public final class BluetoothAdapter {
* the Activity, but is run as a system service, so an application should
* always call cancel discovery even if it did not directly request a
* discovery, just to be sure.
+ * <p>If Bluetooth state is not {@link #STATE_ON}, this API
+ * will return false. After turning on Bluetooth,
+ * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
+ * to get the updated value.
*
* @return true on success, false on error
*/
public boolean cancelDiscovery() {
+ if (getState() != STATE_ON) return false;
try {
mService.cancelDiscovery();
} catch (RemoteException e) {Log.e(TAG, "", e);}
@@ -614,11 +642,16 @@ public final class BluetoothAdapter {
* <p>Applications can also register for {@link #ACTION_DISCOVERY_STARTED}
* or {@link #ACTION_DISCOVERY_FINISHED} to be notified when discovery
* starts or completes.
+ * <p>If Bluetooth state is not {@link #STATE_ON}, this API
+ * will return false. After turning on Bluetooth,
+ * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
+ * to get the updated value.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH}.
*
* @return true if discovering
*/
public boolean isDiscovering() {
+ if (getState() != STATE_ON) return false;
try {
return mService.isDiscovering();
} catch (RemoteException e) {Log.e(TAG, "", e);}
@@ -628,11 +661,18 @@ public final class BluetoothAdapter {
/**
* Return the set of {@link BluetoothDevice} objects that are bonded
* (paired) to the local adapter.
+ * <p>If Bluetooth state is not {@link #STATE_ON}, this API
+ * will return an empty set. After turning on Bluetooth,
+ * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON}
+ * to get the updated value.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH}.
*
* @return unmodifiable set of {@link BluetoothDevice}, or null on error
*/
public Set<BluetoothDevice> getBondedDevices() {
+ if (getState() != STATE_ON) {
+ return toDeviceSet(new String[0]);
+ }
try {
return toDeviceSet(mService.listBonds());
} catch (RemoteException e) {Log.e(TAG, "", e);}
diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java
index 5371fa5b29c0..d685cf37dde5 100644
--- a/core/java/android/content/ClipboardManager.java
+++ b/core/java/android/content/ClipboardManager.java
@@ -34,6 +34,12 @@ import java.util.ArrayList;
* You do not instantiate this class directly; instead, retrieve it through
* {@link android.content.Context#getSystemService}.
*
+ * <p>
+ * The ClipboardManager API itself is very simple: it consists of methods
+ * to atomically get and set the current primary clipboard data. That data
+ * is expressed as a {@link ClippedData} object, which defines the protocol
+ * for data exchange between applications.
+ *
* @see android.content.Context#getSystemService
*/
public class ClipboardManager extends android.text.ClipboardManager {
@@ -152,7 +158,7 @@ public class ClipboardManager extends android.text.ClipboardManager {
public CharSequence getText() {
ClippedData clip = getPrimaryClip();
if (clip != null && clip.getItemCount() > 0) {
- return clip.getItem(0).getText();
+ return clip.getItem(0).coerceToText(mContext);
}
return null;
}
@@ -167,11 +173,11 @@ public class ClipboardManager extends android.text.ClipboardManager {
}
/**
- * Returns true if the clipboard has a primary clip containing text; false otherwise.
+ * @deprecated Use {@link #hasPrimaryClip()} instead.
*/
public boolean hasText() {
try {
- return getService().hasClipboardText();
+ return getService().hasPrimaryClip();
} catch (RemoteException e) {
return false;
}
diff --git a/core/java/android/content/ClippedData.java b/core/java/android/content/ClippedData.java
index ebb194f3b55b..c3f0237eb36d 100644
--- a/core/java/android/content/ClippedData.java
+++ b/core/java/android/content/ClippedData.java
@@ -16,12 +16,18 @@
package android.content;
+import android.content.res.AssetFileDescriptor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import android.util.Log;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
import java.util.ArrayList;
/**
@@ -31,19 +37,88 @@ import java.util.ArrayList;
* each of which can hold one or more representations of an item of data.
* For display to the user, it also has a label and iconic representation.</p>
*
- * <p>The types than an individial item can currently contain are:</p>
- *
- * <ul>
- * <li> Text: a basic string of text. This is actually a CharSequence,
- * so it can be formatted text supported by corresponding Android built-in
- * style spans. (Custom application spans are not supported and will be
- * stripped when transporting through the clipboard.)
- * <li> Intent: an arbitrary Intent object. A typical use is the shortcut
- * to create when pasting a clipped item on to the home screen.
- * <li> Uri: a URI reference. Currently this should only be a content: URI.
- * This representation allows an application to share complex or large clips,
- * by providing a URI to a content provider holding the data.
- * </ul>
+ * <p>Each Item instance can be one of three main classes of data: a simple
+ * CharSequence of text, a single Intent object, or a Uri. See {@link Item}
+ * for more details.
+ *
+ * <a name="ImplementingPaste"></a>
+ * <h3>Implementing Paste or Drop</h3>
+ *
+ * <p>To implement a paste or drop of a ClippedData object into an application,
+ * the application must correctly interpret the data for its use. If the {@link Item}
+ * it contains is simple text or an Intent, there is little to be done: text
+ * can only be interpreted as text, and an Intent will typically be used for
+ * creating shortcuts (such as placing icons on the home screen) or other
+ * actions.
+ *
+ * <p>If all you want is the textual representation of the clipped data, you
+ * can use the convenience method {@link Item#coerceToText Item.coerceToText}.
+ *
+ * <p>More complicated exchanges will be done through URIs, in particular
+ * "content:" URIs. A content URI allows the recipient of a ClippedData item
+ * to interact closely with the ContentProvider holding the data in order to
+ * negotiate the transfer of that data.
+ *
+ * <p>For example, here is the paste function of a simple NotePad application.
+ * When retrieving the data from the clipboard, it can do either two things:
+ * if the clipboard contains a URI reference to an existing note, it copies
+ * the entire structure of the note into a new note; otherwise, it simply
+ * coerces the clip into text and uses that as the new note's contents.
+ *
+ * {@sample development/samples/NotePad/src/com/example/android/notepad/NoteEditor.java
+ * paste}
+ *
+ * <p>In many cases an application can paste various types of streams of data. For
+ * example, an e-mail application may want to allow the user to paste an image
+ * or other binary data as an attachment. This is accomplished through the
+ * ContentResolver {@link ContentResolver#getStreamTypes(Uri, String)} and
+ * {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, android.os.Bundle)}
+ * methods. These allow a client to discover the type(s) of data that a particular
+ * content URI can make available as a stream and retrieve the stream of data.
+ *
+ * <p>For example, the implementation of {@link Item#coerceToText Item.coerceToText}
+ * itself uses this to try to retrieve a URI clip as a stream of text:
+ *
+ * {@sample frameworks/base/core/java/android/content/ClippedData.java coerceToText}
+ *
+ * <a name="ImplementingCopy"></a>
+ * <h3>Implementing Copy or Drag</h3>
+ *
+ * <p>To be the source of a clip, the application must construct a ClippedData
+ * object that any recipient can interpret best for their context. If the clip
+ * is to contain a simple text, Intent, or URI, this is easy: an {@link Item}
+ * containing the appropriate data type can be constructed and used.
+ *
+ * <p>More complicated data types require the implementation of support in
+ * a ContentProvider for describing and generating the data for the recipient.
+ * A common scenario is one where an application places on the clipboard the
+ * content: URI of an object that the user has copied, with the data at that
+ * URI consisting of a complicated structure that only other applications with
+ * direct knowledge of the structure can use.
+ *
+ * <p>For applications that do not have intrinsic knowledge of the data structure,
+ * the content provider holding it can make the data available as an arbitrary
+ * number of types of data streams. This is done by implementing the
+ * ContentProvider {@link ContentProvider#getStreamTypes(Uri, String)} and
+ * {@link ContentProvider#openTypedAssetFile(Uri, String, android.os.Bundle)}
+ * methods.
+ *
+ * <p>Going back to our simple NotePad application, this is the implementation
+ * it may have to convert a single note URI (consisting of a title and the note
+ * text) into a stream of plain text data.
+ *
+ * {@sample development/samples/NotePad/src/com/example/android/notepad/NotePadProvider.java
+ * stream}
+ *
+ * <p>The copy operation in our NotePad application is now just a simple matter
+ * of making a clip containing the URI of the note being copied:
+ *
+ * {@sample development/samples/NotePad/src/com/example/android/notepad/NotesList.java
+ * copy}
+ *
+ * <p>Note if a paste operation needs this clip as text (for example to paste
+ * into an editor), then {@link Item#coerceToText(Context)} will ask the content
+ * provider for the clip URI as text and successfully paste the entire note.
*/
public class ClippedData implements Parcelable {
CharSequence mLabel;
@@ -51,40 +126,166 @@ public class ClippedData implements Parcelable {
final ArrayList<Item> mItems = new ArrayList<Item>();
+ /**
+ * Description of a single item in a ClippedData.
+ *
+ * <p>The types than an individual item can currently contain are:</p>
+ *
+ * <ul>
+ * <li> Text: a basic string of text. This is actually a CharSequence,
+ * so it can be formatted text supported by corresponding Android built-in
+ * style spans. (Custom application spans are not supported and will be
+ * stripped when transporting through the clipboard.)
+ * <li> Intent: an arbitrary Intent object. A typical use is the shortcut
+ * to create when pasting a clipped item on to the home screen.
+ * <li> Uri: a URI reference. This may be any URI (such as an http: URI
+ * representing a bookmark), however it is often a content: URI. Using
+ * content provider references as clips like this allows an application to
+ * share complex or large clips through the standard content provider
+ * facilities.
+ * </ul>
+ */
public static class Item {
CharSequence mText;
Intent mIntent;
Uri mUri;
+ /**
+ * Create an Item consisting of a single block of (possibly styled) text.
+ */
public Item(CharSequence text) {
mText = text;
}
+ /**
+ * Create an Item consisting of an arbitrary Intent.
+ */
public Item(Intent intent) {
mIntent = intent;
}
+ /**
+ * Create an Item consisting of an arbitrary URI.
+ */
public Item(Uri uri) {
mUri = uri;
}
+ /**
+ * Create a complex Item, containing multiple representations of
+ * text, intent, and/or URI.
+ */
public Item(CharSequence text, Intent intent, Uri uri) {
mText = text;
mIntent = intent;
mUri = uri;
}
+ /**
+ * Retrieve the raw text contained in this Item.
+ */
public CharSequence getText() {
return mText;
}
+ /**
+ * Retrieve the raw Intent contained in this Item.
+ */
public Intent getIntent() {
return mIntent;
}
+ /**
+ * Retrieve the raw URI contained in this Item.
+ */
public Uri getUri() {
return mUri;
}
+
+ /**
+ * Turn this item into text, regardless of the type of data it
+ * actually contains.
+ *
+ * <p>The algorithm for deciding what text to return is:
+ * <ul>
+ * <li> If {@link #getText} is non-null, return that.
+ * <li> If {@link #getUri} is non-null, try to retrieve its data
+ * as a text stream from its content provider. If this succeeds, copy
+ * the text into a String and return it. If it is not a content: URI or
+ * the content provider does not supply a text representation, return
+ * the raw URI as a string.
+ * <li> If {@link #getIntent} is non-null, convert that to an intent:
+ * URI and returnit.
+ * <li> Otherwise, return an empty string.
+ * </ul>
+ *
+ * @param context The caller's Context, from which its ContentResolver
+ * and other things can be retrieved.
+ * @return Returns the item's textual representation.
+ */
+//BEGIN_INCLUDE(coerceToText)
+ public CharSequence coerceToText(Context context) {
+ // If this Item has an explicit textual value, simply return that.
+ if (mText != null) {
+ return mText;
+ }
+
+ // If this Item has a URI value, try using that.
+ if (mUri != null) {
+
+ // First see if the URI can be opened as a plain text stream
+ // (of any sub-type). If so, this is the best textual
+ // representation for it.
+ FileInputStream stream = null;
+ try {
+ // Ask for a stream of the desired type.
+ AssetFileDescriptor descr = context.getContentResolver()
+ .openTypedAssetFileDescriptor(mUri, "text/*", null);
+ stream = descr.createInputStream();
+ InputStreamReader reader = new InputStreamReader(stream, "UTF-8");
+
+ // Got it... copy the stream into a local string and return it.
+ StringBuilder builder = new StringBuilder(128);
+ char[] buffer = new char[8192];
+ int len;
+ while ((len=reader.read(buffer)) > 0) {
+ builder.append(buffer, 0, len);
+ }
+ return builder.toString();
+
+ } catch (FileNotFoundException e) {
+ // Unable to open content URI as text... not really an
+ // error, just something to ignore.
+
+ } catch (IOException e) {
+ // Something bad has happened.
+ Log.w("ClippedData", "Failure loading text", e);
+ return e.toString();
+
+ } finally {
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ // If we couldn't open the URI as a stream, then the URI itself
+ // probably serves fairly well as a textual representation.
+ return mUri.toString();
+ }
+
+ // Finally, if all we have is an Intent, then we can just turn that
+ // into text. Not the most user-friendly thing, but it's something.
+ if (mIntent != null) {
+ return mIntent.toUri(Intent.URI_INTENT_SCHEME);
+ }
+
+ // Shouldn't get here, but just in case...
+ return "";
+ }
+//END_INCLUDE(coerceToText)
}
/**
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index a3252ed2f40c..e1d431f67811 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -28,6 +28,7 @@ import android.database.IBulkCursor;
import android.database.IContentObserver;
import android.database.SQLException;
import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Binder;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
@@ -36,6 +37,7 @@ import android.util.Log;
import java.io.File;
import java.io.FileNotFoundException;
+import java.io.IOException;
import java.util.ArrayList;
/**
@@ -251,6 +253,18 @@ public abstract class ContentProvider implements ComponentCallbacks {
return ContentProvider.this.call(method, request, args);
}
+ @Override
+ public String[] getStreamTypes(Uri uri, String mimeTypeFilter) {
+ return ContentProvider.this.getStreamTypes(uri, mimeTypeFilter);
+ }
+
+ @Override
+ public AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeType, Bundle opts)
+ throws FileNotFoundException {
+ enforceReadPermission(uri);
+ return ContentProvider.this.openTypedAssetFile(uri, mimeType, opts);
+ }
+
private void enforceReadPermission(Uri uri) {
final int uid = Binder.getCallingUid();
if (uid == mMyUid) {
@@ -752,6 +766,164 @@ public abstract class ContentProvider implements ComponentCallbacks {
}
/**
+ * Helper to compare two MIME types, where one may be a pattern.
+ * @param concreteType A fully-specified MIME type.
+ * @param desiredType A desired MIME type that may be a pattern such as *\/*.
+ * @return Returns true if the two MIME types match.
+ */
+ public static boolean compareMimeTypes(String concreteType, String desiredType) {
+ final int typeLength = desiredType.length();
+ if (typeLength == 3 && desiredType.equals("*/*")) {
+ return true;
+ }
+
+ final int slashpos = desiredType.indexOf('/');
+ if (slashpos > 0) {
+ if (typeLength == slashpos+2 && desiredType.charAt(slashpos+1) == '*') {
+ if (desiredType.regionMatches(0, concreteType, 0, slashpos+1)) {
+ return true;
+ }
+ } else if (desiredType.equals(concreteType)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Called by a client to determine the types of data streams that this
+ * content provider supports for the given URI. The default implementation
+ * returns null, meaning no types. If your content provider stores data
+ * of a particular type, return that MIME type if it matches the given
+ * mimeTypeFilter. If it can perform type conversions, return an array
+ * of all supported MIME types that match mimeTypeFilter.
+ *
+ * @param uri The data in the content provider being queried.
+ * @param mimeTypeFilter The type of data the client desires. May be
+ * a pattern, such as *\/* to retrieve all possible data types.
+ * @returns Returns null if there are no possible data streams for the
+ * given mimeTypeFilter. Otherwise returns an array of all available
+ * concrete MIME types.
+ *
+ * @see #getType(Uri)
+ * @see #openTypedAssetFile(Uri, String, Bundle)
+ * @see #compareMimeTypes(String, String)
+ */
+ public String[] getStreamTypes(Uri uri, String mimeTypeFilter) {
+ return null;
+ }
+
+ /**
+ * Called by a client to open a read-only stream containing data of a
+ * particular MIME type. This is like {@link #openAssetFile(Uri, String)},
+ * except the file can only be read-only and the content provider may
+ * perform data conversions to generate data of the desired type.
+ *
+ * <p>The default implementation compares the given mimeType against the
+ * result of {@link #getType(Uri)} and, if the match, simple calls
+ * {@link #openAssetFile(Uri, String)}.
+ *
+ * <p>See {@link ClippedData} for examples of the use and implementation
+ * of this method.
+ *
+ * @param uri The data in the content provider being queried.
+ * @param mimeTypeFilter The type of data the client desires. May be
+ * a pattern, such as *\/*, if the caller does not have specific type
+ * requirements; in this case the content provider will pick its best
+ * type matching the pattern.
+ * @param opts Additional options from the client. The definitions of
+ * these are specific to the content provider being called.
+ *
+ * @return Returns a new AssetFileDescriptor from which the client can
+ * read data of the desired type.
+ *
+ * @throws FileNotFoundException Throws FileNotFoundException if there is
+ * no file associated with the given URI or the mode is invalid.
+ * @throws SecurityException Throws SecurityException if the caller does
+ * not have permission to access the data.
+ * @throws IllegalArgumentException Throws IllegalArgumentException if the
+ * content provider does not support the requested MIME type.
+ *
+ * @see #getStreamTypes(Uri, String)
+ * @see #openAssetFile(Uri, String)
+ * @see #compareMimeTypes(String, String)
+ */
+ public AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeTypeFilter, Bundle opts)
+ throws FileNotFoundException {
+ String baseType = getType(uri);
+ if (baseType != null && compareMimeTypes(baseType, mimeTypeFilter)) {
+ return openAssetFile(uri, "r");
+ }
+ throw new FileNotFoundException("Can't open " + uri + " as type " + mimeTypeFilter);
+ }
+
+ /**
+ * Interface to write a stream of data to a pipe. Use with
+ * {@link ContentProvider#openPipeHelper}.
+ */
+ public interface PipeDataWriter<T> {
+ /**
+ * Called from a background thread to stream data out to a pipe.
+ * Note that the pipe is blocking, so this thread can block on
+ * writes for an arbitrary amount of time if the client is slow
+ * at reading.
+ *
+ * @param output The pipe where data should be written. This will be
+ * closed for you upon returning from this function.
+ * @param uri The URI whose data is to be written.
+ * @param mimeType The desired type of data to be written.
+ * @param opts Options supplied by caller.
+ * @param args Your own custom arguments.
+ */
+ public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, String mimeType,
+ Bundle opts, T args);
+ }
+
+ /**
+ * A helper function for implementing {@link #openTypedAssetFile}, for
+ * creating a data pipe and background thread allowing you to stream
+ * generated data back to the client. This function returns a new
+ * ParcelFileDescriptor that should be returned to the caller (the caller
+ * is responsible for closing it).
+ *
+ * @param uri The URI whose data is to be written.
+ * @param mimeType The desired type of data to be written.
+ * @param opts Options supplied by caller.
+ * @param args Your own custom arguments.
+ * @param func Interface implementing the function that will actually
+ * stream the data.
+ * @return Returns a new ParcelFileDescriptor holding the read side of
+ * the pipe. This should be returned to the caller for reading; the caller
+ * is responsible for closing it when done.
+ */
+ public <T> ParcelFileDescriptor openPipeHelper(final Uri uri, final String mimeType,
+ final Bundle opts, final T args, final PipeDataWriter<T> func)
+ throws FileNotFoundException {
+ try {
+ final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe();
+
+ AsyncTask<Object, Object, Object> task = new AsyncTask<Object, Object, Object>() {
+ @Override
+ protected Object doInBackground(Object... params) {
+ func.writeDataToPipe(fds[1], uri, mimeType, opts, args);
+ try {
+ fds[1].close();
+ } catch (IOException e) {
+ Log.w(TAG, "Failure closing pipe", e);
+ }
+ return null;
+ }
+ };
+ task.execute((Object[])null);
+
+ return fds[0];
+ } catch (IOException e) {
+ throw new FileNotFoundException("failure making pipe");
+ }
+ }
+
+ /**
* Returns true if this instance is a temporary content provider.
* @return true if this instance is a temporary content provider
*/
@@ -777,6 +949,11 @@ public abstract class ContentProvider implements ComponentCallbacks {
* @param info Registered information about this content provider
*/
public void attachInfo(Context context, ProviderInfo info) {
+ /*
+ * We may be using AsyncTask from binder threads. Make it init here
+ * so its static handler is on the main thread.
+ */
+ AsyncTask.init();
/*
* Only allow it to be set once, so after the content service gives
@@ -824,7 +1001,7 @@ public abstract class ContentProvider implements ComponentCallbacks {
/**
* @hide -- until interface has proven itself
*
- * Call an provider-defined method. This can be used to implement
+ * Call a provider-defined method. This can be used to implement
* interfaces that are cheaper than using a Cursor.
*
* @param method Method name to call. Opaque to framework.
@@ -836,29 +1013,26 @@ public abstract class ContentProvider implements ComponentCallbacks {
}
/**
- * Shuts down this instance of the ContentProvider. It is useful when writing tests that use
- * the ContentProvider.
+ * Implement this to shut down the ContentProvider instance. You can then
+ * invoke this method in unit tests.
+ *
* <p>
- * If a unittest starts the ContentProvider in its test(..() methods, it could run into sqlite
- * errors "disk I/O error" or "corruption" in the following scenario:
- * <ul>
- * <li>Say, there are 2 test methods in the unittest</li>
- * <li>test1() (or setUp()) causes ContentProvider object to be initialized and
- * assume it opens a database connection to "foo.db"</li>
- * <li>est1() completes and test2() starts</li>
- * <li>During the execution of test2() there will be 2 connections to "foo.db"</li>
- * <li>Different threads in the ContentProvider may have one of these two connection
- * handles. This is not a problem per se</li>
- * <li>But if the two threads with 2 database connections don't interact correctly,
- * there could be unexpected errors from sqlite</li>
- * <li>Some of those unexpected errros are "disk I/O error" or "corruption" error</li>
- * <li>Common practice in tearDown() is to delete test directory (and the database files)</li>
- * <li>If this is done while some threads are still holding unclosed database connections,
- * sqlite quite easily gets into corruption and disk I/O errors</li>
- * </ul>
+ * Android normally handles ContentProvider startup and shutdown
+ * automatically. You do not need to start up or shut down a
+ * ContentProvider. When you invoke a test method on a ContentProvider,
+ * however, a ContentProvider instance is started and keeps running after
+ * the test finishes, even if a succeeding test instantiates another
+ * ContentProvider. A conflict develops because the two instances are
+ * usually running against the same underlying data source (for example, an
+ * sqlite database).
+ * </p>
* <p>
- * tearDown() in the unittests should call this method to have ContentProvider gracefully
- * shutdown all database connections.
+ * Implementing shutDown() avoids this conflict by providing a way to
+ * terminate the ContentProvider. This method can also prevent memory leaks
+ * from multiple instantiations of the ContentProvider, and it can ensure
+ * unit test isolation by allowing you to completely clean up the test
+ * fixture before moving on to the next test.
+ * </p>
*/
public void shutdown() {
Log.w(TAG, "implement ContentProvider shutdown() to make sure all database " +
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index 0858ea51c76c..0540109b4f91 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -18,6 +18,7 @@ package android.content;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Bundle;
import android.os.RemoteException;
import android.os.ParcelFileDescriptor;
import android.content.res.AssetFileDescriptor;
@@ -43,53 +44,77 @@ public class ContentProviderClient {
mContentResolver = contentResolver;
}
- /** see {@link ContentProvider#query} */
+ /** See {@link ContentProvider#query ContentProvider.query} */
public Cursor query(Uri url, String[] projection, String selection,
String[] selectionArgs, String sortOrder) throws RemoteException {
return mContentProvider.query(url, projection, selection, selectionArgs, sortOrder);
}
- /** see {@link ContentProvider#getType} */
+ /** See {@link ContentProvider#getType ContentProvider.getType} */
public String getType(Uri url) throws RemoteException {
return mContentProvider.getType(url);
}
- /** see {@link ContentProvider#insert} */
+ /** See {@link ContentProvider#getStreamTypes ContentProvider.getStreamTypes} */
+ public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException {
+ return mContentProvider.getStreamTypes(url, mimeTypeFilter);
+ }
+
+ /** See {@link ContentProvider#insert ContentProvider.insert} */
public Uri insert(Uri url, ContentValues initialValues)
throws RemoteException {
return mContentProvider.insert(url, initialValues);
}
- /** see {@link ContentProvider#bulkInsert} */
+ /** See {@link ContentProvider#bulkInsert ContentProvider.bulkInsert} */
public int bulkInsert(Uri url, ContentValues[] initialValues) throws RemoteException {
return mContentProvider.bulkInsert(url, initialValues);
}
- /** see {@link ContentProvider#delete} */
+ /** See {@link ContentProvider#delete ContentProvider.delete} */
public int delete(Uri url, String selection, String[] selectionArgs)
throws RemoteException {
return mContentProvider.delete(url, selection, selectionArgs);
}
- /** see {@link ContentProvider#update} */
+ /** See {@link ContentProvider#update ContentProvider.update} */
public int update(Uri url, ContentValues values, String selection,
String[] selectionArgs) throws RemoteException {
return mContentProvider.update(url, values, selection, selectionArgs);
}
- /** see {@link ContentProvider#openFile} */
+ /**
+ * See {@link ContentProvider#openFile ContentProvider.openFile}. Note that
+ * this <em>does not</em>
+ * take care of non-content: URIs such as file:. It is strongly recommended
+ * you use the {@link ContentResolver#openFileDescriptor
+ * ContentResolver.openFileDescriptor} API instead.
+ */
public ParcelFileDescriptor openFile(Uri url, String mode)
throws RemoteException, FileNotFoundException {
return mContentProvider.openFile(url, mode);
}
- /** see {@link ContentProvider#openAssetFile} */
+ /**
+ * See {@link ContentProvider#openAssetFile ContentProvider.openAssetFile}.
+ * Note that this <em>does not</em>
+ * take care of non-content: URIs such as file:. It is strongly recommended
+ * you use the {@link ContentResolver#openAssetFileDescriptor
+ * ContentResolver.openAssetFileDescriptor} API instead.
+ */
public AssetFileDescriptor openAssetFile(Uri url, String mode)
throws RemoteException, FileNotFoundException {
return mContentProvider.openAssetFile(url, mode);
}
- /** see {@link ContentProvider#applyBatch} */
+ /** See {@link ContentProvider#openTypedAssetFile ContentProvider.openTypedAssetFile} */
+ public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri,
+ String mimeType, Bundle opts)
+ throws RemoteException, FileNotFoundException {
+ return mContentProvider.openTypedAssetFile(uri, mimeType, opts);
+ }
+
+ /** See {@link ContentProvider#applyBatch ContentProvider.applyBatch} */
public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
throws RemoteException, OperationApplicationException {
return mContentProvider.applyBatch(operations);
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index fdb3d20c9f99..d1ab8d576412 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -257,6 +257,38 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
reply.writeBundle(responseBundle);
return true;
}
+
+ case GET_STREAM_TYPES_TRANSACTION:
+ {
+ data.enforceInterface(IContentProvider.descriptor);
+ Uri url = Uri.CREATOR.createFromParcel(data);
+ String mimeTypeFilter = data.readString();
+ String[] types = getStreamTypes(url, mimeTypeFilter);
+ reply.writeNoException();
+ reply.writeStringArray(types);
+
+ return true;
+ }
+
+ case OPEN_TYPED_ASSET_FILE_TRANSACTION:
+ {
+ data.enforceInterface(IContentProvider.descriptor);
+ Uri url = Uri.CREATOR.createFromParcel(data);
+ String mimeType = data.readString();
+ Bundle opts = data.readBundle();
+
+ AssetFileDescriptor fd;
+ fd = openTypedAssetFile(url, mimeType, opts);
+ reply.writeNoException();
+ if (fd != null) {
+ reply.writeInt(1);
+ fd.writeToParcel(reply,
+ Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ } else {
+ reply.writeInt(0);
+ }
+ return true;
+ }
}
} catch (Exception e) {
DatabaseUtils.writeExceptionToParcel(reply, e);
@@ -568,5 +600,50 @@ final class ContentProviderProxy implements IContentProvider
return bundle;
}
+ public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+
+ data.writeInterfaceToken(IContentProvider.descriptor);
+
+ url.writeToParcel(data, 0);
+ data.writeString(mimeTypeFilter);
+
+ mRemote.transact(IContentProvider.GET_STREAM_TYPES_TRANSACTION, data, reply, 0);
+
+ DatabaseUtils.readExceptionFromParcel(reply);
+ String[] out = reply.createStringArray();
+
+ data.recycle();
+ reply.recycle();
+
+ return out;
+ }
+
+ public AssetFileDescriptor openTypedAssetFile(Uri url, String mimeType, Bundle opts)
+ throws RemoteException, FileNotFoundException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+
+ data.writeInterfaceToken(IContentProvider.descriptor);
+
+ url.writeToParcel(data, 0);
+ data.writeString(mimeType);
+ data.writeBundle(opts);
+
+ mRemote.transact(IContentProvider.OPEN_TYPED_ASSET_FILE_TRANSACTION, data, reply, 0);
+
+ DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(reply);
+ int has = reply.readInt();
+ AssetFileDescriptor fd = has != 0
+ ? AssetFileDescriptor.CREATOR.createFromParcel(reply) : null;
+
+ data.recycle();
+ reply.recycle();
+
+ return fd;
+ }
+
private IBinder mRemote;
}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 2ea0df967881..22feb9a984a6 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -186,8 +186,7 @@ public abstract class ContentResolver {
* using the content:// scheme.
* @return A MIME type for the content, or null if the URL is invalid or the type is unknown
*/
- public final String getType(Uri url)
- {
+ public final String getType(Uri url) {
IContentProvider provider = acquireProvider(url);
if (provider == null) {
return null;
@@ -204,6 +203,39 @@ public abstract class ContentResolver {
}
/**
+ * Query for the possible MIME types for the representations the given
+ * content URL can be returned when opened as as stream with
+ * {@link #openTypedAssetFileDescriptor}. Note that the types here are
+ * not necessarily a superset of the type returned by {@link #getType} --
+ * many content providers can not return a raw stream for the structured
+ * data that they contain.
+ *
+ * @param url A Uri identifying content (either a list or specific type),
+ * using the content:// scheme.
+ * @param mimeTypeFilter The desired MIME type. This may be a pattern,
+ * such as *\/*, to query for all available MIME types that match the
+ * pattern.
+ * @return Returns an array of MIME type strings for all availablle
+ * data streams that match the given mimeTypeFilter. If there are none,
+ * null is returned.
+ */
+ public String[] getStreamTypes(Uri url, String mimeTypeFilter) {
+ IContentProvider provider = acquireProvider(url);
+ if (provider == null) {
+ return null;
+ }
+ try {
+ return provider.getStreamTypes(url, mimeTypeFilter);
+ } catch (RemoteException e) {
+ return null;
+ } catch (java.lang.Exception e) {
+ return null;
+ } finally {
+ releaseProvider(provider);
+ }
+ }
+
+ /**
* <p>
* Query the given URI, returning a {@link Cursor} over the result set.
* </p>
@@ -349,7 +381,7 @@ public abstract class ContentResolver {
}
/**
- * Open a raw file descriptor to access data under a "content:" URI. This
+ * Open a raw file descriptor to access data under a URI. This
* is like {@link #openAssetFileDescriptor(Uri, String)}, but uses the
* underlying {@link ContentProvider#openFile}
* ContentProvider.openFile()} method, so will <em>not</em> work with
@@ -399,7 +431,7 @@ public abstract class ContentResolver {
}
/**
- * Open a raw file descriptor to access data under a "content:" URI. This
+ * Open a raw file descriptor to access data under a URI. This
* interacts with the underlying {@link ContentProvider#openAssetFile}
* method of the provider associated with the given URI, to retrieve any file stored there.
*
@@ -433,6 +465,11 @@ public abstract class ContentResolver {
* </li>
* </ul>
*
+ * <p>Note that if this function is called for read-only input (mode is "r")
+ * on a content: URI, it will instead call {@link #openTypedAssetFileDescriptor}
+ * for you with a MIME type of "*\/*". This allows such callers to benefit
+ * from any built-in data conversion that a provider implements.
+ *
* @param uri The desired URI to open.
* @param mode The file mode to use, as per {@link ContentProvider#openAssetFile
* ContentProvider.openAssetFile}.
@@ -459,29 +496,97 @@ public abstract class ContentResolver {
new File(uri.getPath()), modeToMode(uri, mode));
return new AssetFileDescriptor(pfd, 0, -1);
} else {
- IContentProvider provider = acquireProvider(uri);
- if (provider == null) {
- throw new FileNotFoundException("No content provider: " + uri);
- }
- try {
- AssetFileDescriptor fd = provider.openAssetFile(uri, mode);
- if(fd == null) {
- releaseProvider(provider);
- return null;
+ if ("r".equals(mode)) {
+ return openTypedAssetFileDescriptor(uri, "*/*", null);
+ } else {
+ IContentProvider provider = acquireProvider(uri);
+ if (provider == null) {
+ throw new FileNotFoundException("No content provider: " + uri);
}
- ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
- fd.getParcelFileDescriptor(), provider);
- return new AssetFileDescriptor(pfd, fd.getStartOffset(),
- fd.getDeclaredLength());
- } catch (RemoteException e) {
- releaseProvider(provider);
- throw new FileNotFoundException("Dead content provider: " + uri);
- } catch (FileNotFoundException e) {
+ try {
+ AssetFileDescriptor fd = provider.openAssetFile(uri, mode);
+ if(fd == null) {
+ releaseProvider(provider);
+ return null;
+ }
+ ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
+ fd.getParcelFileDescriptor(), provider);
+
+ // Success! Don't release the provider when exiting, let
+ // ParcelFileDescriptorInner do that when it is closed.
+ provider = null;
+
+ return new AssetFileDescriptor(pfd, fd.getStartOffset(),
+ fd.getDeclaredLength());
+ } catch (RemoteException e) {
+ throw new FileNotFoundException("Dead content provider: " + uri);
+ } catch (FileNotFoundException e) {
+ throw e;
+ } finally {
+ if (provider != null) {
+ releaseProvider(provider);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Open a raw file descriptor to access (potentially type transformed)
+ * data from a "content:" URI. This interacts with the underlying
+ * {@link ContentProvider#openTypedAssetFile} method of the provider
+ * associated with the given URI, to retrieve retrieve any appropriate
+ * data stream for the data stored there.
+ *
+ * <p>Unlike {@link #openAssetFileDescriptor}, this function only works
+ * with "content:" URIs, because content providers are the only facility
+ * with an associated MIME type to ensure that the returned data stream
+ * is of the desired type.
+ *
+ * <p>All text/* streams are encoded in UTF-8.
+ *
+ * @param uri The desired URI to open.
+ * @param mimeType The desired MIME type of the returned data. This can
+ * be a pattern such as *\/*, which will allow the content provider to
+ * select a type, though there is no way for you to determine what type
+ * it is returning.
+ * @param opts Additional provider-dependent options.
+ * @return Returns a new ParcelFileDescriptor from which you can read the
+ * data stream from the provider. Note that this may be a pipe, meaning
+ * you can't seek in it. The only seek you should do is if the
+ * AssetFileDescriptor contains an offset, to move to that offset before
+ * reading. You own this descriptor and are responsible for closing it when done.
+ * @throws FileNotFoundException Throws FileNotFoundException of no
+ * data of the desired type exists under the URI.
+ */
+ public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri,
+ String mimeType, Bundle opts) throws FileNotFoundException {
+ IContentProvider provider = acquireProvider(uri);
+ if (provider == null) {
+ throw new FileNotFoundException("No content provider: " + uri);
+ }
+ try {
+ AssetFileDescriptor fd = provider.openTypedAssetFile(uri, mimeType, opts);
+ if (fd == null) {
releaseProvider(provider);
- throw e;
- } catch (RuntimeException e) {
+ return null;
+ }
+ ParcelFileDescriptor pfd = new ParcelFileDescriptorInner(
+ fd.getParcelFileDescriptor(), provider);
+
+ // Success! Don't release the provider when exiting, let
+ // ParcelFileDescriptorInner do that when it is closed.
+ provider = null;
+
+ return new AssetFileDescriptor(pfd, fd.getStartOffset(),
+ fd.getDeclaredLength());
+ } catch (RemoteException e) {
+ throw new FileNotFoundException("Dead content provider: " + uri);
+ } catch (FileNotFoundException e) {
+ throw e;
+ } finally {
+ if (provider != null) {
releaseProvider(provider);
- throw e;
}
}
}
diff --git a/core/java/android/content/CursorLoader.java b/core/java/android/content/CursorLoader.java
index 850ff7f0cf2b..42599edb6ce1 100644
--- a/core/java/android/content/CursorLoader.java
+++ b/core/java/android/content/CursorLoader.java
@@ -100,8 +100,8 @@ public class CursorLoader extends AsyncTaskLoader<Cursor> {
public void stopLoading() {
if (mCursor != null && !mCursor.isClosed()) {
mCursor.close();
- mCursor = null;
}
+ mCursor = null;
// Attempt to cancel the current load task if possible.
cancelLoad();
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index 67e7581e5ffd..8f122ce8f39d 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -59,6 +59,7 @@ public interface IContentProvider extends IInterface {
throws RemoteException, FileNotFoundException;
public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
throws RemoteException, OperationApplicationException;
+
/**
* @hide -- until interface has proven itself
*
@@ -71,6 +72,11 @@ public interface IContentProvider extends IInterface {
*/
public Bundle call(String method, String request, Bundle args) throws RemoteException;
+ // Data interchange.
+ public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException;
+ public AssetFileDescriptor openTypedAssetFile(Uri url, String mimeType, Bundle opts)
+ throws RemoteException, FileNotFoundException;
+
/* IPC constants */
static final String descriptor = "android.content.IContentProvider";
@@ -84,4 +90,6 @@ public interface IContentProvider extends IInterface {
static final int OPEN_ASSET_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 14;
static final int APPLY_BATCH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 19;
static final int CALL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 20;
+ static final int GET_STREAM_TYPES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 21;
+ static final int OPEN_TYPED_ASSET_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 22;
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 2acc4a05792a..58174fbaa0de 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -985,6 +985,15 @@ public class Intent implements Parcelable, Cloneable {
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_INSERT = "android.intent.action.INSERT";
/**
+ * Activity Action: Create a new item in the given container, initializing it
+ * from the current contents of the clipboard.
+ * <p>Input: {@link #getData} is URI of the directory (vnd.android.cursor.dir/*)
+ * in which to place the data.
+ * <p>Output: URI of the new data that was created.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_PASTE = "android.intent.action.PASTE";
+ /**
* Activity Action: Delete the given data from its container.
* <p>Input: {@link #getData} is URI of data to be deleted.
* <p>Output: nothing.
diff --git a/core/java/android/content/res/AssetFileDescriptor.java b/core/java/android/content/res/AssetFileDescriptor.java
index a37e4e8cc3bf..ccb86050facd 100644
--- a/core/java/android/content/res/AssetFileDescriptor.java
+++ b/core/java/android/content/res/AssetFileDescriptor.java
@@ -129,8 +129,12 @@ public class AssetFileDescriptor implements Parcelable {
/**
* Checks whether this file descriptor is for a memory file.
*/
- private boolean isMemoryFile() throws IOException {
- return MemoryFile.isMemoryFile(mFd.getFileDescriptor());
+ private boolean isMemoryFile() {
+ try {
+ return MemoryFile.isMemoryFile(mFd.getFileDescriptor());
+ } catch (IOException e) {
+ return false;
+ }
}
/**
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index ba1b3a9b8a3b..812af5a1d950 100755
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1333,7 +1333,7 @@ public class Resources {
height = mMetrics.widthPixels;
}
int keyboardHidden = mConfiguration.keyboardHidden;
- if (keyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO
+ if (keyboardHidden == Configuration.KEYBOARDHIDDEN_NO
&& mConfiguration.hardKeyboardHidden
== Configuration.HARDKEYBOARDHIDDEN_YES) {
keyboardHidden = Configuration.KEYBOARDHIDDEN_SOFT;
diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java
index 9ac45d81105a..c07c3c61e196 100644
--- a/core/java/android/database/DatabaseUtils.java
+++ b/core/java/android/database/DatabaseUtils.java
@@ -1203,4 +1203,18 @@ public class DatabaseUtils {
}
return STATEMENT_OTHER;
}
+
+ /**
+ * Appends one set of selection args to another. This is useful when adding a selection
+ * argument to a user provided set.
+ */
+ public static String[] appendSelectionArgs(String[] originalValues, String[] newValues) {
+ if (originalValues == null || originalValues.length == 0) {
+ return newValues;
+ }
+ String[] result = new String[originalValues.length + newValues.length ];
+ System.arraycopy(originalValues, 0, result, 0, originalValues.length);
+ System.arraycopy(newValues, 0, result, originalValues.length, newValues.length);
+ return result;
+ }
}
diff --git a/core/java/android/net/DownloadManager.java b/core/java/android/net/DownloadManager.java
index 1d88c18ea511..e69c3241350b 100644
--- a/core/java/android/net/DownloadManager.java
+++ b/core/java/android/net/DownloadManager.java
@@ -241,12 +241,6 @@ public class DownloadManager {
*/
public static class Request {
/**
- * Bit flag for {@link #setShowNotification} indicating a notification should be created
- * while the download is running.
- */
- public static final int NOTIFICATION_WHEN_RUNNING = 1;
-
- /**
* Bit flag for {@link #setAllowedNetworkTypes} corresponding to
* {@link ConnectivityManager#TYPE_MOBILE}.
*/
@@ -269,7 +263,7 @@ public class DownloadManager {
private Map<String, String> mRequestHeaders = new HashMap<String, String>();
private String mTitle;
private String mDescription;
- private int mNotificationFlags = 0;
+ private boolean mShowNotification = true;
private String mMediaType;
private boolean mRoamingAllowed = true;
private int mAllowedNetworkTypes = ~0; // default to all network types allowed
@@ -344,15 +338,20 @@ public class DownloadManager {
}
/**
- * Control system notifications posted by the download manager for this download. If
- * enabled, the download manager posts notifications about downloads through the system
- * {@link android.app.NotificationManager}. By default, no notification is shown.
+ * Control whether a system notification is posted by the download manager while this
+ * download is running. If enabled, the download manager posts notifications about downloads
+ * through the system {@link android.app.NotificationManager}. By default, a notification is
+ * shown.
*
- * @param flags any combination of the NOTIFICATION_* bit flags
+ * If set to false, this requires the permission
+ * android.permission.DOWNLOAD_WITHOUT_NOTIFICATION.
+ *
+ * @param show whether the download manager should show a notification for this download.
* @return this object
+ * @hide
*/
- public Request setShowNotification(int flags) {
- mNotificationFlags = flags;
+ public Request setShowRunningNotification(boolean show) {
+ mShowNotification = show;
return this;
}
@@ -404,11 +403,9 @@ public class DownloadManager {
putIfNonNull(values, Downloads.COLUMN_DESCRIPTION, mDescription);
putIfNonNull(values, Downloads.COLUMN_MIME_TYPE, mMediaType);
- int visibility = Downloads.VISIBILITY_HIDDEN;
- if ((mNotificationFlags & NOTIFICATION_WHEN_RUNNING) != 0) {
- visibility = Downloads.VISIBILITY_VISIBLE;
- }
- values.put(Downloads.COLUMN_VISIBILITY, visibility);
+ values.put(Downloads.COLUMN_VISIBILITY,
+ mShowNotification ? Downloads.VISIBILITY_VISIBLE
+ : Downloads.VISIBILITY_HIDDEN);
values.put(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES, mAllowedNetworkTypes);
values.put(Downloads.Impl.COLUMN_ALLOW_ROAMING, mRoamingAllowed);
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 11cd52613ad7..efbccd2d79ae 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -526,12 +526,6 @@ public class MobileDataStateTracker implements NetworkStateTracker {
return -1;
}
- /**
- * This is not supported.
- */
- public void interpretScanResultsAvailable() {
- }
-
@Override
public String toString() {
StringBuffer sb = new StringBuffer("Mobile data state: ");
diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java
index 44215e7bf452..82735e5f76c1 100644
--- a/core/java/android/net/NetworkStateTracker.java
+++ b/core/java/android/net/NetworkStateTracker.java
@@ -27,18 +27,17 @@ package android.net;
public interface NetworkStateTracker {
public static final int EVENT_STATE_CHANGED = 1;
- public static final int EVENT_SCAN_RESULTS_AVAILABLE = 2;
/**
* arg1: 1 to show, 0 to hide
* arg2: ID of the notification
* obj: Notification (if showing)
*/
- public static final int EVENT_NOTIFICATION_CHANGED = 3;
- public static final int EVENT_CONFIGURATION_CHANGED = 4;
- public static final int EVENT_ROAMING_CHANGED = 5;
- public static final int EVENT_NETWORK_SUBTYPE_CHANGED = 6;
- public static final int EVENT_RESTORE_DEFAULT_NETWORK = 7;
- public static final int EVENT_CLEAR_NET_TRANSITION_WAKELOCK = 8;
+ public static final int EVENT_NOTIFICATION_CHANGED = 2;
+ public static final int EVENT_CONFIGURATION_CHANGED = 3;
+ public static final int EVENT_ROAMING_CHANGED = 4;
+ public static final int EVENT_NETWORK_SUBTYPE_CHANGED = 5;
+ public static final int EVENT_RESTORE_DEFAULT_NETWORK = 6;
+ public static final int EVENT_CLEAR_NET_TRANSITION_WAKELOCK = 7;
/**
* Fetch NetworkInfo for the network
@@ -147,10 +146,4 @@ public interface NetworkStateTracker {
*/
public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid);
- /**
- * Interprets scan results. This will be called at a safe time for
- * processing, and from a safe thread.
- */
- public void interpretScanResultsAvailable();
-
}
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 47faaba8d0c5..63adcd09e519 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -1568,7 +1568,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
throw new UnsupportedOperationException(NOT_HIERARCHICAL);
}
if (key == null) {
- throw new NullPointerException("key");
+ throw new NullPointerException("key");
}
final String query = getEncodedQuery();
@@ -1608,6 +1608,24 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
return null;
}
+ /**
+ * Searches the query string for the first value with the given key and interprets it
+ * as a boolean value. "false" and "0" are interpreted as <code>false</code>, everything
+ * else is interpreted as <code>true</code>.
+ *
+ * @param key which will be decoded
+ * @param defaultValue the default value to return if there is no query parameter for key
+ * @return the boolean interpretation of the query parameter key
+ */
+ public boolean getBooleanQueryParameter(String key, boolean defaultValue) {
+ String flag = getQueryParameter(key);
+ if (flag == null) {
+ return defaultValue;
+ }
+ flag = flag.toLowerCase();
+ return (!"false".equals(flag) && !"0".equals(flag));
+ }
+
/** Identifies a null parcelled Uri. */
private static final int NULL_TYPE_ID = 0;
diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java
index 832ce84f52ef..aadacabe5e3d 100644
--- a/core/java/android/os/AsyncTask.java
+++ b/core/java/android/os/AsyncTask.java
@@ -175,6 +175,11 @@ public abstract class AsyncTask<Params, Progress, Result> {
FINISHED,
}
+ /** @hide Used to force static handler to be created. */
+ public static void init() {
+ sHandler.getLooper();
+ }
+
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*/
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index 69b35404a61b..a9d7342ec374 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -180,6 +180,11 @@ public class Looper {
return mThread;
}
+ /** @hide */
+ public MessageQueue getQueue() {
+ return mQueue;
+ }
+
public void dump(Printer pw, String prefix) {
pw.println(prefix + this);
pw.println(prefix + "mRun=" + mRun);
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 9d213b344dc8..d853f1375680 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -134,6 +134,25 @@ public class ParcelFileDescriptor implements Parcelable {
private static native FileDescriptor getFileDescriptorFromSocket(Socket socket);
/**
+ * Create two ParcelFileDescriptors structured as a data pipe. The first
+ * ParcelFileDescriptor in the returned array is the read side; the second
+ * is the write side.
+ */
+ public static ParcelFileDescriptor[] createPipe() throws IOException {
+ FileDescriptor[] fds = new FileDescriptor[2];
+ int res = createPipeNative(fds);
+ if (res == 0) {
+ ParcelFileDescriptor[] pfds = new ParcelFileDescriptor[2];
+ pfds[0] = new ParcelFileDescriptor(fds[0]);
+ pfds[1] = new ParcelFileDescriptor(fds[1]);
+ return pfds;
+ }
+ throw new IOException("Unable to create pipe: errno=" + -res);
+ }
+
+ private static native int createPipeNative(FileDescriptor[] outFds);
+
+ /**
* Retrieve the actual FileDescriptor associated with this object.
*
* @return Returns the FileDescriptor associated with this object.
diff --git a/core/java/android/os/storage/IMountService.aidl b/core/java/android/os/storage/IMountService.aidl
index ca7efe780743..5c69214c32c1 100644
--- a/core/java/android/os/storage/IMountService.aidl
+++ b/core/java/android/os/storage/IMountService.aidl
@@ -19,6 +19,7 @@ package android.os.storage;
import android.os.storage.IMountServiceListener;
import android.os.storage.IMountShutdownObserver;
+import android.os.storage.IObbActionListener;
/** WARNING! Update IMountService.h and IMountService.cpp if you change this file.
* In particular, the ordering of the methods below must match the
@@ -156,14 +157,20 @@ interface IMountService
/**
* Mounts an Opaque Binary Blob (OBB) with the specified decryption key and only
* allows the calling process's UID access to the contents.
+ *
+ * MountService will call back to the supplied IObbActionListener to inform
+ * it of the terminal state of the call.
*/
- int mountObb(String filename, String key);
+ void mountObb(String filename, String key, IObbActionListener token);
/**
* Unmounts an Opaque Binary Blob (OBB). When the force flag is specified, any
* program using it will be forcibly killed to unmount the image.
+ *
+ * MountService will call back to the supplied IObbActionListener to inform
+ * it of the terminal state of the call.
*/
- int unmountObb(String filename, boolean force);
+ void unmountObb(String filename, boolean force, IObbActionListener token);
/**
* Checks whether the specified Opaque Binary Blob (OBB) is mounted somewhere.
diff --git a/core/java/android/os/storage/IObbActionListener.aidl b/core/java/android/os/storage/IObbActionListener.aidl
new file mode 100644
index 000000000000..78d7a9ed391c
--- /dev/null
+++ b/core/java/android/os/storage/IObbActionListener.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.storage;
+
+/**
+ * Callback class for receiving events from MountService about
+ * Opaque Binary Blobs (OBBs).
+ *
+ * @hide - Applications should use android.os.storage.StorageManager
+ * to interact with OBBs.
+ */
+interface IObbActionListener {
+ /**
+ * Return from an OBB action result.
+ *
+ * @param filename the path to the OBB the operation was performed on
+ * @param returnCode status of the operation
+ */
+ void onObbResult(String filename, String status);
+}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 61cdace618bf..7c9effadb940 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -16,28 +16,14 @@
package android.os.storage;
-import android.content.Context;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Looper;
-import android.os.Parcelable;
-import android.os.ParcelFileDescriptor;
-import android.os.Process;
-import android.os.RemoteException;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
+import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.storage.IMountService;
-import android.os.storage.IMountServiceListener;
import android.util.Log;
-import android.util.SparseArray;
-import java.io.FileDescriptor;
-import java.io.IOException;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
/**
* StorageManager is the interface to the systems storage service.
@@ -88,6 +74,17 @@ public class StorageManager
}
/**
+ * Binder listener for OBB action results.
+ */
+ private final ObbActionBinderListener mObbActionListener = new ObbActionBinderListener();
+ private class ObbActionBinderListener extends IObbActionListener.Stub {
+ @Override
+ public void onObbResult(String filename, String status) throws RemoteException {
+ Log.i(TAG, "filename = " + filename + ", result = " + status);
+ }
+ }
+
+ /**
* Private base class for messages sent between the callback thread
* and the target looper handler.
*/
@@ -290,12 +287,23 @@ public class StorageManager
}
/**
- * Mount an OBB file.
+ * Mount an Opaque Binary Blob (OBB) file. If a <code>key</code> is
+ * specified, it is supplied to the mounting process to be used in any
+ * encryption used in the OBB.
+ * <p>
+ * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
+ * file matches a package ID that is owned by the calling program's UID.
+ * That is, shared UID applications can obtain access to any other
+ * application's OBB that shares its UID.
+ *
+ * @param filename the path to the OBB file
+ * @param key decryption key
+ * @return whether the mount call was successfully queued or not
*/
public boolean mountObb(String filename, String key) {
try {
- return mMountService.mountObb(filename, key)
- == StorageResultCode.OperationSucceeded;
+ mMountService.mountObb(filename, key, mObbActionListener);
+ return true;
} catch (RemoteException e) {
Log.e(TAG, "Failed to mount OBB", e);
}
@@ -304,12 +312,24 @@ public class StorageManager
}
/**
- * Mount an OBB file.
+ * Unmount an Opaque Binary Blob (OBB) file. If the <code>force</code> flag
+ * is true, it will kill any application needed to unmount the given OBB.
+ * <p>
+ * <em>Note:</em> you can only mount OBB files for which the OBB tag on the
+ * file matches a package ID that is owned by the calling program's UID.
+ * That is, shared UID applications can obtain access to any other
+ * application's OBB that shares its UID.
+ *
+ * @param filename path to the OBB file
+ * @param force whether to kill any programs using this in order to unmount
+ * it
+ * @return whether the unmount call was successfully queued or not
+ * @throws IllegalArgumentException when OBB is not already mounted
*/
- public boolean unmountObb(String filename, boolean force) {
+ public boolean unmountObb(String filename, boolean force) throws IllegalArgumentException {
try {
- return mMountService.unmountObb(filename, force)
- == StorageResultCode.OperationSucceeded;
+ mMountService.unmountObb(filename, force, mObbActionListener);
+ return true;
} catch (RemoteException e) {
Log.e(TAG, "Failed to mount OBB", e);
}
@@ -317,7 +337,13 @@ public class StorageManager
return false;
}
- public boolean isObbMounted(String filename) {
+ /**
+ * Check whether an Opaque Binary Blob (OBB) is mounted or not.
+ *
+ * @param filename path to OBB image
+ * @return true if OBB is mounted; false if not mounted or on error
+ */
+ public boolean isObbMounted(String filename) throws IllegalArgumentException {
try {
return mMountService.isObbMounted(filename);
} catch (RemoteException e) {
@@ -328,13 +354,21 @@ public class StorageManager
}
/**
- * Check the mounted path of an OBB file.
+ * Check the mounted path of an Opaque Binary Blob (OBB) file. This will
+ * give you the path to where you can obtain access to the internals of the
+ * OBB.
+ *
+ * @param filename path to OBB image
+ * @return absolute path to mounted OBB image data or <code>null</code> if
+ * not mounted or exception encountered trying to read status
*/
public String getMountedObbPath(String filename) {
try {
return mMountService.getMountedObbPath(filename);
} catch (RemoteException e) {
Log.e(TAG, "Failed to find mounted path for OBB", e);
+ } catch (IllegalArgumentException e) {
+ Log.d(TAG, "Couldn't read OBB file", e);
}
return null;
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index e13c3e8e7b55..114f67dc7a83 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -125,6 +125,9 @@ public abstract class PreferenceActivity extends ListActivity implements
// Back will then return RESULT_CANCELED and Next RESULT_OK
private static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
+ // add a Skip button?
+ private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
+
// specify custom text for the Back or Next buttons, or cause a button to not appear
// at all by setting it to null
private static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
@@ -307,6 +310,13 @@ public abstract class PreferenceActivity extends ListActivity implements
finish();
}
});
+ Button skipButton = (Button)findViewById(com.android.internal.R.id.skip_button);
+ skipButton.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ setResult(RESULT_OK);
+ finish();
+ }
+ });
mNextButton = (Button)findViewById(com.android.internal.R.id.next_button);
mNextButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
@@ -334,6 +344,9 @@ public abstract class PreferenceActivity extends ListActivity implements
backButton.setText(buttonText);
}
}
+ if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
+ skipButton.setVisibility(View.VISIBLE);
+ }
}
}
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index 1b37107e1d60..c9b55121aa84 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -624,13 +624,19 @@ public final class Downloads {
"android.permission.SEND_DOWNLOAD_COMPLETED_INTENTS";
/**
- * The permission to downloads files to the cache partition that won't be automatically
+ * The permission to download files to the cache partition that won't be automatically
* purged when space is needed.
*/
public static final String PERMISSION_CACHE_NON_PURGEABLE =
"android.permission.DOWNLOAD_CACHE_NON_PURGEABLE";
/**
+ * The permission to download files without any system notification being shown.
+ */
+ public static final String PERMISSION_NO_NOTIFICATION =
+ "android.permission.DOWNLOAD_WITHOUT_NOTIFICATION";
+
+ /**
* The content:// URI for the data table in the provider
*/
public static final Uri CONTENT_URI =
diff --git a/core/java/android/provider/Mtp.java b/core/java/android/provider/Mtp.java
index 15f8666b0fe2..bc764acb2f64 100644
--- a/core/java/android/provider/Mtp.java
+++ b/core/java/android/provider/Mtp.java
@@ -308,6 +308,13 @@ public final class Mtp
public static final int FORMAT_ABSTRACT_CONTACT = 0xBB81;
public static final int FORMAT_VCARD_2 = 0xBB82;
+ // Object properties we support
+ public static final int PROPERTY_STORAGE_ID = 0xDC01;
+ public static final int PROPERTY_OBJECT_FORMAT = 0xDC02;
+ public static final int PROPERTY_OBJECT_SIZE = 0xDC04;
+ public static final int PROPERTY_OBJECT_FILE_NAME = 0xDC07;
+ public static final int PROPERTY_PARENT_OBJECT = 0xDC0B;
+
/**
* Object is not protected. It may be modified and deleted, and its properties
* may be modified.
diff --git a/core/java/android/util/Finalizers.java b/core/java/android/util/Finalizers.java
new file mode 100644
index 000000000000..671f2d496e63
--- /dev/null
+++ b/core/java/android/util/Finalizers.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import java.lang.ref.PhantomReference;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+
+/**
+ * This class can be used to implement reliable finalizers.
+ *
+ * @hide
+ */
+public final class Finalizers {
+ private static final String LOG_TAG = "Finalizers";
+
+ private static final Object[] sLock = new Object[0];
+ private static boolean sInit;
+ private static Reclaimer sReclaimer;
+
+ /**
+ * Subclass of PhantomReference used to reclaim resources.
+ */
+ public static abstract class ReclaimableReference<T> extends PhantomReference<T> {
+ public ReclaimableReference(T r, ReferenceQueue<Object> q) {
+ super(r, q);
+ }
+
+ public abstract void reclaim();
+ }
+
+ /**
+ * Returns the queue used to reclaim ReclaimableReferences.
+ *
+ * @return A reference queue or null before initialization
+ */
+ public static ReferenceQueue<Object> getQueue() {
+ synchronized (sLock) {
+ if (!sInit) {
+ return null;
+ }
+ if (!sReclaimer.isRunning()) {
+ sReclaimer = new Reclaimer(sReclaimer.mQueue);
+ sReclaimer.start();
+ }
+ return sReclaimer.mQueue;
+ }
+ }
+
+ /**
+ * Invoked by Zygote. Don't touch!
+ */
+ public static void init() {
+ synchronized (sLock) {
+ if (!sInit && sReclaimer == null) {
+ sReclaimer = new Reclaimer();
+ sReclaimer.start();
+ sInit = true;
+ }
+ }
+ }
+
+ private static class Reclaimer extends Thread {
+ ReferenceQueue<Object> mQueue;
+
+ private volatile boolean mRunning = false;
+
+ Reclaimer() {
+ this(new ReferenceQueue<Object>());
+ }
+
+ Reclaimer(ReferenceQueue<Object> queue) {
+ super("Reclaimer");
+ setDaemon(true);
+ mQueue = queue;
+ }
+
+ @Override
+ public void start() {
+ mRunning = true;
+ super.start();
+ }
+
+ boolean isRunning() {
+ return mRunning;
+ }
+
+ @SuppressWarnings({"InfiniteLoopStatement"})
+ @Override
+ public void run() {
+ try {
+ while (true) {
+ try {
+ cleanUp(mQueue.remove());
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ }
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Reclaimer thread exiting: ", e);
+ } finally {
+ mRunning = false;
+ }
+ }
+
+ private void cleanUp(Reference<?> reference) {
+ do {
+ reference.clear();
+ ((ReclaimableReference<?>) reference).reclaim();
+ } while ((reference = mQueue.poll()) != null);
+ }
+ }
+}
diff --git a/core/java/android/util/JsonReader.java b/core/java/android/util/JsonReader.java
index e47be0d6d28c..3345bfab59fc 100644
--- a/core/java/android/util/JsonReader.java
+++ b/core/java/android/util/JsonReader.java
@@ -172,6 +172,9 @@ public final class JsonReader implements Closeable {
/** The input JSON. */
private final Reader in;
+ /** True to accept non-spec compliant JSON */
+ private boolean lenient = false;
+
/**
* Use a manual buffer to easily read and unread upcoming characters, and
* also so we can create strings without an intermediate StringBuilder.
@@ -207,8 +210,8 @@ public final class JsonReader implements Closeable {
/** The text of the next literal value. */
private String value;
- // TODO: make this parser strict and offer an optional lenient mode?
- // TODO: document how this reader is non-strict
+ /** True if we're currently handling a skipValue() call. */
+ private boolean skipping = false;
/**
* Creates a new instance that reads a JSON-encoded stream from {@code in}.
@@ -221,6 +224,31 @@ public final class JsonReader implements Closeable {
}
/**
+ * Configure this parser to be be liberal in what it accepts. By default,
+ * this parser is strict and only accepts JSON as specified by <a
+ * href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>. Setting the
+ * parser to lenient causes it to ignore the following syntax errors:
+ *
+ * <ul>
+ * <li>End of line comments starting with {@code //} or {@code #} and
+ * ending with a newline character.
+ * <li>C-style comments starting with {@code /*} and ending with
+ * {@code *}{@code /}. Such comments may not be nested.
+ * <li>Names that are unquoted or {@code 'single quoted'}.
+ * <li>Strings that are unquoted or {@code 'single quoted'}.
+ * <li>Array elements separated by {@code ;} instead of {@code ,}.
+ * <li>Unnecessary array separators. These are interpreted as if null
+ * was the omitted value.
+ * <li>Names and values separated by {@code =} or {@code =>} instead of
+ * {@code :}.
+ * <li>Name/value pairs separated by {@code ;} instead of {@code ,}.
+ * </ul>
+ */
+ public void setLenient(boolean lenient) {
+ this.lenient = lenient;
+ }
+
+ /**
* Consumes the next token from the JSON stream and asserts that it is the
* beginning of a new array.
*/
@@ -253,11 +281,12 @@ public final class JsonReader implements Closeable {
}
/**
- * Consumes {@code token}.
+ * Consumes {@code expected}.
*/
- private void expect(JsonToken token) throws IOException {
- if (quickPeek() != token) {
- throw new IllegalStateException("Expected " + token + " but was " + peek());
+ private void expect(JsonToken expected) throws IOException {
+ quickPeek();
+ if (token != expected) {
+ throw new IllegalStateException("Expected " + expected + " but was " + peek());
}
advance();
}
@@ -266,8 +295,8 @@ public final class JsonReader implements Closeable {
* Returns true if the current array or object has another element.
*/
public boolean hasNext() throws IOException {
- JsonToken peek = quickPeek();
- return peek != JsonToken.END_OBJECT && peek != JsonToken.END_ARRAY;
+ quickPeek();
+ return token != JsonToken.END_OBJECT && token != JsonToken.END_ARRAY;
}
/**
@@ -285,11 +314,8 @@ public final class JsonReader implements Closeable {
/**
* Ensures that a token is ready. After this call either {@code token} or
- * {@code value} will be non-null.
- *
- * @return the type of the next token, of {@code null} if it is unknown. For
- * a definitive result, use {@link #peek()} which decodes the token
- * type.
+ * {@code value} will be non-null. To ensure {@code token} has a definitive
+ * value, use {@link #peek()}
*/
private JsonToken quickPeek() throws IOException {
if (hasToken) {
@@ -347,7 +373,8 @@ public final class JsonReader implements Closeable {
* name.
*/
public String nextName() throws IOException {
- if (quickPeek() != JsonToken.NAME) {
+ quickPeek();
+ if (token != JsonToken.NAME) {
throw new IllegalStateException("Expected a name but was " + peek());
}
String result = name;
@@ -364,8 +391,8 @@ public final class JsonReader implements Closeable {
* this reader is closed.
*/
public String nextString() throws IOException {
- JsonToken peek = peek();
- if (value == null || (peek != JsonToken.STRING && peek != JsonToken.NUMBER)) {
+ peek();
+ if (value == null || (token != JsonToken.STRING && token != JsonToken.NUMBER)) {
throw new IllegalStateException("Expected a string but was " + peek());
}
@@ -382,8 +409,8 @@ public final class JsonReader implements Closeable {
* this reader is closed.
*/
public boolean nextBoolean() throws IOException {
- JsonToken peek = quickPeek();
- if (value == null || peek == JsonToken.STRING) {
+ quickPeek();
+ if (value == null || token == JsonToken.STRING) {
throw new IllegalStateException("Expected a boolean but was " + peek());
}
@@ -408,8 +435,8 @@ public final class JsonReader implements Closeable {
* reader is closed.
*/
public void nextNull() throws IOException {
- JsonToken peek = quickPeek();
- if (value == null || peek == JsonToken.STRING) {
+ quickPeek();
+ if (value == null || token == JsonToken.STRING) {
throw new IllegalStateException("Expected null but was " + peek());
}
@@ -536,17 +563,20 @@ public final class JsonReader implements Closeable {
* stream contains unrecognized or unhandled values.
*/
public void skipValue() throws IOException {
- // TODO: suppress string creation while elements are being skipped!
-
- int count = 0;
- do {
- JsonToken token = advance();
- if (token == JsonToken.BEGIN_ARRAY || token == JsonToken.BEGIN_OBJECT) {
- count++;
- } else if (token == JsonToken.END_ARRAY || token == JsonToken.END_OBJECT) {
- count--;
- }
- } while (count != 0);
+ skipping = true;
+ try {
+ int count = 0;
+ do {
+ JsonToken token = advance();
+ if (token == JsonToken.BEGIN_ARRAY || token == JsonToken.BEGIN_OBJECT) {
+ count++;
+ } else if (token == JsonToken.END_ARRAY || token == JsonToken.END_OBJECT) {
+ count--;
+ }
+ } while (count != 0);
+ } finally {
+ skipping = false;
+ }
}
private JsonScope peekStack() {
@@ -570,36 +600,43 @@ public final class JsonReader implements Closeable {
private JsonToken nextInArray(boolean firstElement) throws IOException {
if (firstElement) {
- switch (nextNonWhitespace()) {
- case ']':
- pop();
- hasToken = true;
- return token = JsonToken.END_ARRAY;
- case ',':
- case ';':
- /* a separator without a value first means "null". */
- // TODO: forbid this in strict mode
- hasToken = true;
- return token = JsonToken.NULL;
- default:
- replaceTop(JsonScope.NONEMPTY_ARRAY);
- pos--;
- }
+ replaceTop(JsonScope.NONEMPTY_ARRAY);
} else {
+ /* Look for a comma before each element after the first element. */
switch (nextNonWhitespace()) {
case ']':
pop();
hasToken = true;
return token = JsonToken.END_ARRAY;
- case ',':
case ';':
+ checkLenient(); // fall-through
+ case ',':
break;
default:
throw syntaxError("Unterminated array");
}
}
- return nextValue();
+ switch (nextNonWhitespace()) {
+ case ']':
+ if (firstElement) {
+ pop();
+ hasToken = true;
+ return token = JsonToken.END_ARRAY;
+ }
+ // fall-through to handle ",]"
+ case ';':
+ case ',':
+ /* In lenient mode, a 0-length literal means 'null' */
+ checkLenient();
+ pos--;
+ hasToken = true;
+ value = "null";
+ return token = JsonToken.NULL;
+ default:
+ pos--;
+ return nextValue();
+ }
}
private JsonToken nextInObject(boolean firstElement) throws IOException {
@@ -636,10 +673,12 @@ public final class JsonReader implements Closeable {
int quote = nextNonWhitespace();
switch (quote) {
case '\'':
+ checkLenient(); // fall-through
case '"':
name = nextString((char) quote);
break;
default:
+ checkLenient();
pos--;
name = nextLiteral();
if (name.isEmpty()) {
@@ -653,19 +692,21 @@ public final class JsonReader implements Closeable {
}
private JsonToken objectValue() throws IOException {
- // TODO: accept only ":" in strict mode
-
/*
- * Read the name/value separator. Usually a colon ':', an equals sign
- * '=', or an arrow "=>". The last two are bogus but we include them
- * because that's what org.json does.
+ * Read the name/value separator. Usually a colon ':'. In lenient mode
+ * we also accept an equals sign '=', or an arrow "=>".
*/
- int separator = nextNonWhitespace();
- if (separator != ':' && separator != '=') {
- throw syntaxError("Expected ':'");
- }
- if (separator == '=' && (pos < limit || fillBuffer(1)) && buffer[pos] == '>') {
- pos++;
+ switch (nextNonWhitespace()) {
+ case ':':
+ break;
+ case '=':
+ checkLenient();
+ if ((pos < limit || fillBuffer(1)) && buffer[pos] == '>') {
+ pos++;
+ }
+ break;
+ default:
+ throw syntaxError("Expected ':'");
}
replaceTop(JsonScope.NONEMPTY_OBJECT);
@@ -686,6 +727,7 @@ public final class JsonReader implements Closeable {
return token = JsonToken.BEGIN_ARRAY;
case '\'':
+ checkLenient(); // fall-through
case '"':
value = nextString((char) c);
hasToken = true;
@@ -722,8 +764,6 @@ public final class JsonReader implements Closeable {
}
private int nextNonWhitespace() throws IOException {
- // TODO: no comments in strict mode
-
while (pos < limit || fillBuffer(1)) {
int c = buffer[pos++];
switch (c) {
@@ -738,6 +778,7 @@ public final class JsonReader implements Closeable {
return c;
}
+ checkLenient();
char peek = buffer[pos];
switch (peek) {
case '*':
@@ -765,6 +806,7 @@ public final class JsonReader implements Closeable {
* specify this behaviour, but it's required to parse
* existing documents. See http://b/2571423.
*/
+ checkLenient();
skipToEndOfLine();
continue;
@@ -776,6 +818,12 @@ public final class JsonReader implements Closeable {
throw syntaxError("End of input");
}
+ private void checkLenient() throws IOException {
+ if (!lenient) {
+ throw syntaxError("Use JsonReader.setLenient(true) to accept malformed JSON");
+ }
+ }
+
/**
* Advances the position until after the next newline character. If the line
* is terminated by "\r\n", the '\n' must be consumed as whitespace by the
@@ -822,7 +870,9 @@ public final class JsonReader implements Closeable {
int c = buffer[pos++];
if (c == quote) {
- if (builder == null) {
+ if (skipping) {
+ return "skipped!";
+ } else if (builder == null) {
return new String(buffer, start, pos - start - 1);
} else {
builder.append(buffer, start, pos - start - 1);
@@ -853,9 +903,6 @@ public final class JsonReader implements Closeable {
* does not consume the delimiter character.
*/
private String nextLiteral() throws IOException {
- // TODO: use a much smaller set of permitted literal characters in strict mode;
- // these characters are derived from org.json's lenient mode
-
StringBuilder builder = null;
do {
/* the index of the first character not yet appended to the builder. */
@@ -863,24 +910,28 @@ public final class JsonReader implements Closeable {
while (pos < limit) {
int c = buffer[pos++];
switch (c) {
+ case '/':
+ case '\\':
+ case ';':
+ case '#':
+ case '=':
+ checkLenient(); // fall-through
+
case '{':
case '}':
case '[':
case ']':
- case '/':
- case '\\':
case ':':
- case '=':
case ',':
- case ';':
- case '#':
case ' ':
case '\t':
case '\f':
case '\r':
case '\n':
pos--;
- if (builder == null) {
+ if (skipping) {
+ return "skipped!";
+ } else if (builder == null) {
return new String(buffer, start, pos - start);
} else {
builder.append(buffer, start, pos - start);
@@ -965,7 +1016,7 @@ public final class JsonReader implements Closeable {
/**
* Assigns {@code nextToken} based on the value of {@code nextValue}.
*/
- private void decodeLiteral() {
+ private void decodeLiteral() throws IOException {
if (value.equalsIgnoreCase("null")) {
token = JsonToken.NULL;
} else if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) {
@@ -975,7 +1026,8 @@ public final class JsonReader implements Closeable {
Double.parseDouble(value); // this work could potentially be cached
token = JsonToken.NUMBER;
} catch (NumberFormatException ignored) {
- /* an unquoted string. This document is not well-formed! */
+ // this must be an unquoted string
+ checkLenient();
token = JsonToken.STRING;
}
}
diff --git a/core/java/android/util/JsonWriter.java b/core/java/android/util/JsonWriter.java
index 913b04784d98..fecc1c873771 100644
--- a/core/java/android/util/JsonWriter.java
+++ b/core/java/android/util/JsonWriter.java
@@ -16,10 +16,10 @@
package android.util;
+import java.io.Closeable;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
/**
@@ -117,7 +117,7 @@ import java.util.List;
* Instances of this class are not thread safe. Calls that would result in a
* malformed JSON string will fail with an {@link IllegalStateException}.
*/
-public final class JsonWriter {
+public final class JsonWriter implements Closeable {
/** The output data, containing at most one top-level array or object. */
private final Writer out;
@@ -151,23 +151,20 @@ public final class JsonWriter {
}
/**
- * Sets the number of spaces to indent each line in the encoded document.
- * If {@code indent == 0} the encoded document will be compact. If {@code
- * indent > 0}, the encoded document will be more human-readable.
+ * Sets the indentation string to be repeated for each level of indentation
+ * in the encoded document. If {@code indent.isEmpty()} the encoded document
+ * will be compact. Otherwise the encoded document will be more
+ * human-readable.
+ *
+ * @param indent a string containing only whitespace.
*/
- public void setIndentSpaces(int indent) {
- if (indent < 0) {
- throw new IllegalArgumentException("indent < 0");
- }
-
- if (indent > 0) {
- char[] indentChars = new char[indent];
- Arrays.fill(indentChars, ' ');
- this.indent = new String(indentChars);
- this.separator = ": ";
- } else {
+ public void setIndent(String indent) {
+ if (indent.isEmpty()) {
this.indent = null;
this.separator = ":";
+ } else {
+ this.indent = indent;
+ this.separator = ": ";
}
}
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 8e1338dda795..91dbe1ff8cb0 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -40,7 +40,6 @@ import javax.microedition.khronos.opengles.GL;
/**
* An implementation of Canvas on top of OpenGL ES 2.0.
*/
-@SuppressWarnings({"deprecation"})
class GLES20Canvas extends Canvas {
@SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
private final GL mGl;
@@ -56,6 +55,17 @@ class GLES20Canvas extends Canvas {
private final Rect mClipBounds = new Rect();
private DrawFilter mFilter;
+
+ ///////////////////////////////////////////////////////////////////////////
+ // JNI
+ ///////////////////////////////////////////////////////////////////////////
+
+ private static native boolean nIsAvailable();
+ private static boolean sIsAvailable = nIsAvailable();
+
+ static boolean isAvailable() {
+ return sIsAvailable;
+ }
///////////////////////////////////////////////////////////////////////////
// Constructors
@@ -91,11 +101,6 @@ class GLES20Canvas extends Canvas {
}
@Override
- public GL getGL() {
- throw new UnsupportedOperationException();
- }
-
- @Override
public void setBitmap(Bitmap bitmap) {
throw new UnsupportedOperationException();
}
@@ -522,11 +527,18 @@ class GLES20Canvas extends Canvas {
@Override
public void drawPath(Path path, Paint paint) {
boolean hasModifier = setupModifiers(paint);
- nDrawPath(mRenderer, path.mNativePath, paint.mNativePaint);
+ if (path.isSimplePath) {
+ if (path.rects != null) {
+ nDrawRects(mRenderer, path.rects.mNativeRegion, paint.mNativePaint);
+ }
+ } else {
+ nDrawPath(mRenderer, path.mNativePath, paint.mNativePaint);
+ }
if (hasModifier) nResetModifiers(mRenderer);
}
private native void nDrawPath(int renderer, int path, int paint);
+ private native void nDrawRects(int renderer, int region, int paint);
@Override
public void drawPicture(Picture picture) {
@@ -605,9 +617,13 @@ class GLES20Canvas extends Canvas {
if ((index | count | (index + count) | (text.length - index - count)) < 0) {
throw new IndexOutOfBoundsException();
}
+
boolean hasModifier = setupModifiers(paint);
- nDrawText(mRenderer, text, index, count, x, y, paint.mBidiFlags, paint.mNativePaint);
- if (hasModifier) nResetModifiers(mRenderer);
+ try {
+ nDrawText(mRenderer, text, index, count, x, y, paint.mBidiFlags, paint.mNativePaint);
+ } finally {
+ if (hasModifier) nResetModifiers(mRenderer);
+ }
}
private native void nDrawText(int renderer, char[] text, int index, int count, float x, float y,
@@ -616,20 +632,23 @@ class GLES20Canvas extends Canvas {
@Override
public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) {
boolean hasModifier = setupModifiers(paint);
- if (text instanceof String || text instanceof SpannedString ||
- text instanceof SpannableString) {
- nDrawText(mRenderer, text.toString(), start, end, x, y, paint.mBidiFlags,
- paint.mNativePaint);
- } else if (text instanceof GraphicsOperations) {
- ((GraphicsOperations) text).drawText(this, start, end, x, y,
- paint);
- } else {
- char[] buf = TemporaryBuffer.obtain(end - start);
- TextUtils.getChars(text, start, end, buf, 0);
- nDrawText(mRenderer, buf, 0, end - start, x, y, paint.mBidiFlags, paint.mNativePaint);
- TemporaryBuffer.recycle(buf);
+ try {
+ if (text instanceof String || text instanceof SpannedString ||
+ text instanceof SpannableString) {
+ nDrawText(mRenderer, text.toString(), start, end, x, y, paint.mBidiFlags,
+ paint.mNativePaint);
+ } else if (text instanceof GraphicsOperations) {
+ ((GraphicsOperations) text).drawText(this, start, end, x, y,
+ paint);
+ } else {
+ char[] buf = TemporaryBuffer.obtain(end - start);
+ TextUtils.getChars(text, start, end, buf, 0);
+ nDrawText(mRenderer, buf, 0, end - start, x, y, paint.mBidiFlags, paint.mNativePaint);
+ TemporaryBuffer.recycle(buf);
+ }
+ } finally {
+ if (hasModifier) nResetModifiers(mRenderer);
}
- if (hasModifier) nResetModifiers(mRenderer);
}
@Override
@@ -637,9 +656,13 @@ class GLES20Canvas extends Canvas {
if ((start | end | (end - start) | (text.length() - end)) < 0) {
throw new IndexOutOfBoundsException();
}
+
boolean hasModifier = setupModifiers(paint);
- nDrawText(mRenderer, text, start, end, x, y, paint.mBidiFlags, paint.mNativePaint);
- if (hasModifier) nResetModifiers(mRenderer);
+ try {
+ nDrawText(mRenderer, text, start, end, x, y, paint.mBidiFlags, paint.mNativePaint);
+ } finally {
+ if (hasModifier) nResetModifiers(mRenderer);
+ }
}
private native void nDrawText(int renderer, String text, int start, int end, float x, float y,
@@ -648,8 +671,12 @@ class GLES20Canvas extends Canvas {
@Override
public void drawText(String text, float x, float y, Paint paint) {
boolean hasModifier = setupModifiers(paint);
- nDrawText(mRenderer, text, 0, text.length(), x, y, paint.mBidiFlags, paint.mNativePaint);
- if (hasModifier) nResetModifiers(mRenderer);
+ try {
+ nDrawText(mRenderer, text, 0, text.length(), x, y, paint.mBidiFlags,
+ paint.mNativePaint);
+ } finally {
+ if (hasModifier) nResetModifiers(mRenderer);
+ }
}
@Override
@@ -666,15 +693,59 @@ class GLES20Canvas extends Canvas {
@Override
public void drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount,
float x, float y, int dir, Paint paint) {
- throw new UnsupportedOperationException();
+ if ((index | count | text.length - index - count) < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ if (dir != DIRECTION_LTR && dir != DIRECTION_RTL) {
+ throw new IllegalArgumentException("Unknown direction: " + dir);
+ }
+
+ boolean hasModifier = setupModifiers(paint);
+ try {
+ nDrawTextRun(mRenderer, text, index, count, contextIndex, contextCount, x, y, dir,
+ paint.mNativePaint);
+ } finally {
+ if (hasModifier) nResetModifiers(mRenderer);
+ }
}
+ private native void nDrawTextRun(int renderer, char[] text, int index, int count,
+ int contextIndex, int contextCount, float x, float y, int dir, int nativePaint);
+
@Override
public void drawTextRun(CharSequence text, int start, int end, int contextStart, int contextEnd,
float x, float y, int dir, Paint paint) {
- throw new UnsupportedOperationException();
+ if ((start | end | end - start | text.length() - end) < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ boolean hasModifier = setupModifiers(paint);
+ try {
+ int flags = dir == 0 ? 0 : 1;
+ if (text instanceof String || text instanceof SpannedString ||
+ text instanceof SpannableString) {
+ nDrawTextRun(mRenderer, text.toString(), start, end, contextStart,
+ contextEnd, x, y, flags, paint.mNativePaint);
+ } else if (text instanceof GraphicsOperations) {
+ ((GraphicsOperations) text).drawTextRun(this, start, end,
+ contextStart, contextEnd, x, y, flags, paint);
+ } else {
+ int contextLen = contextEnd - contextStart;
+ int len = end - start;
+ char[] buf = TemporaryBuffer.obtain(contextLen);
+ TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
+ nDrawTextRun(mRenderer, buf, start - contextStart, len, 0, contextLen,
+ x, y, flags, paint.mNativePaint);
+ TemporaryBuffer.recycle(buf);
+ }
+ } finally {
+ if (hasModifier) nResetModifiers(mRenderer);
+ }
}
+ private native void nDrawTextRun(int renderer, String text, int start, int end,
+ int contextStart, int contextEnd, float x, float y, int flags, int nativePaint);
+
@Override
public void drawVertices(VertexMode mode, int vertexCount, float[] verts, int vertOffset,
float[] texs, int texOffset, int[] colors, int colorOffset, short[] indices,
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 090a74372c1a..44bd6d47d678 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -28,22 +28,29 @@ import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
import javax.microedition.khronos.opengles.GL;
-import javax.microedition.khronos.opengles.GL11;
-
-import static javax.microedition.khronos.opengles.GL10.GL_COLOR_BUFFER_BIT;
-import static javax.microedition.khronos.opengles.GL10.GL_SCISSOR_TEST;
/**
* Interface for rendering a ViewRoot using hardware acceleration.
*
* @hide
*/
-abstract class HardwareRenderer {
+public abstract class HardwareRenderer {
private boolean mEnabled;
private boolean mRequested = true;
private static final String LOG_TAG = "HardwareRenderer";
/**
+ * Indicates whether hardware acceleration is available under any form for
+ * the view hierarchy.
+ *
+ * @return True if the view hierarchy can potentially be hardware accelerated,
+ * false otherwise
+ */
+ public static boolean isAvailable() {
+ return GLES20Canvas.isAvailable();
+ }
+
+ /**
* Destroys the hardware rendering context.
*/
abstract void destroy();
@@ -110,10 +117,8 @@ abstract class HardwareRenderer {
*/
static HardwareRenderer createGlRenderer(int glVersion, boolean translucent) {
switch (glVersion) {
- case 1:
- return new Gl10Renderer(translucent);
case 2:
- return new Gl20Renderer(translucent);
+ return Gl20Renderer.create(translucent);
}
throw new IllegalArgumentException("Unknown GL version: " + glVersion);
}
@@ -295,7 +300,6 @@ abstract class HardwareRenderer {
*/
if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
throw new RuntimeException("eglMakeCurrent failed");
-
}
return mEglContext.getGL();
@@ -369,6 +373,15 @@ abstract class HardwareRenderer {
attachInfo.mIgnoreDirtyState = true;
view.mPrivateFlags |= View.DRAWN;
+ // TODO: Don't check the current context when we have one per UI thread
+ // TODO: Use a threadlocal flag to know whether the surface has changed
+ if (mEgl.eglGetCurrentContext() != mEglContext ||
+ mEgl.eglGetCurrentSurface(EGL10.EGL_DRAW) != mEglSurface) {
+ if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+ throw new RuntimeException("eglMakeCurrent failed");
+ }
+ }
+
onPreDraw();
Canvas canvas = mCanvas;
@@ -520,43 +533,13 @@ abstract class HardwareRenderer {
@Override
void onPreDraw() {
mGlCanvas.onPreDraw();
- }
- }
-
- /**
- * Hardware renderer using OpenGL ES 1.0.
- */
- @SuppressWarnings({"deprecation"})
- static class Gl10Renderer extends GlRenderer {
- Gl10Renderer(boolean translucent) {
- super(1, translucent);
- }
-
- @Override
- Canvas createCanvas() {
- return new Canvas(mGl);
}
- @Override
- void destroy() {
- if (isEnabled()) {
- nativeAbandonGlCaches();
+ static HardwareRenderer create(boolean translucent) {
+ if (GLES20Canvas.isAvailable()) {
+ return new Gl20Renderer(translucent);
}
-
- super.destroy();
- }
-
- @Override
- void onPreDraw() {
- GL11 gl = (GL11) mGl;
- gl.glDisable(GL_SCISSOR_TEST);
- gl.glClearColor(0, 0, 0, 0);
- gl.glClear(GL_COLOR_BUFFER_BIT);
- gl.glEnable(GL_SCISSOR_TEST);
+ return null;
}
}
-
- // Inform Skia to just abandon its texture cache IDs doesn't call glDeleteTextures
- // Used only by the native Skia OpenGL ES 1.x implementation
- private static native void nativeAbandonGlCaches();
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index d6b92125490c..e86e3bf3bcba 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -28,6 +28,7 @@ import android.view.IWindowSession;
import android.view.KeyEvent;
import android.view.InputEvent;
import android.view.MotionEvent;
+import android.view.InputChannel;
/**
* System private interface to the window manager.
@@ -119,6 +120,7 @@ interface IWindowManager
int getKeycodeStateForDevice(int devid, int sw);
int getTrackballKeycodeState(int sw);
int getDPadKeycodeState(int sw);
+ InputChannel monitorInput(String inputChannelName);
// Report whether the hardware supports the given keys; returns true if successful
boolean hasKeys(in int[] keycodes, inout boolean[] keyExists);
diff --git a/core/java/android/view/InputQueue.java b/core/java/android/view/InputQueue.java
index 13d810431612..43c957adebb6 100644
--- a/core/java/android/view/InputQueue.java
+++ b/core/java/android/view/InputQueue.java
@@ -132,9 +132,9 @@ public final class InputQueue {
synchronized (sLock) {
FinishedCallback callback = sRecycleHead;
if (callback != null) {
- callback.mRecycleNext = null;
sRecycleHead = callback.mRecycleNext;
sRecycleCount -= 1;
+ callback.mRecycleNext = null;
} else {
callback = new FinishedCallback();
}
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 9223e17fb5a5..ed10e412ac51 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -156,7 +156,7 @@ public class KeyEvent extends InputEvent implements Parcelable {
// those new codes. This is intended to maintain a consistent
// set of key code definitions across all Android devices.
- private static final int LAST_KEYCODE = KEYCODE_SWITCH_CHARSET;
+ private static final int LAST_KEYCODE = KEYCODE_BUTTON_MODE;
/**
* @deprecated There are now more than MAX_KEYCODE keycodes.
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index ff34f4add39d..09995987e65d 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -312,7 +312,7 @@ public class ScaleGestureDetector {
* MotionEvent has no getRawX(int) method; simulate it pending future API approval.
*/
private static float getRawX(MotionEvent event, int pointerIndex) {
- float offset = event.getX() - event.getRawX();
+ float offset = event.getRawX() - event.getX();
return event.getX(pointerIndex) + offset;
}
@@ -320,7 +320,7 @@ public class ScaleGestureDetector {
* MotionEvent has no getRawY(int) method; simulate it pending future API approval.
*/
private static float getRawY(MotionEvent event, int pointerIndex) {
- float offset = event.getY() - event.getRawY();
+ float offset = event.getRawY() - event.getY();
return event.getY(pointerIndex) + offset;
}
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index 068e7b6efbc9..fb88c7135ba6 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -33,14 +33,15 @@ import android.util.PoolableManager;
* and {@link #getXVelocity()}.
*/
public final class VelocityTracker implements Poolable<VelocityTracker> {
- static final String TAG = "VelocityTracker";
- static final boolean DEBUG = false;
- static final boolean localLOGV = DEBUG || Config.LOGV;
+ private static final String TAG = "VelocityTracker";
+ private static final boolean DEBUG = false;
+ private static final boolean localLOGV = DEBUG || Config.LOGV;
- static final int NUM_PAST = 10;
- static final int MAX_AGE_MILLISECONDS = 200;
+ private static final int NUM_PAST = 10;
+ private static final int MAX_AGE_MILLISECONDS = 200;
+
+ private static final int POINTER_POOL_CAPACITY = 20;
- static final VelocityTracker[] mPool = new VelocityTracker[1];
private static final Pool<VelocityTracker> sPool = Pools.synchronizedPool(
Pools.finitePool(new PoolableManager<VelocityTracker>() {
public VelocityTracker newInstance() {
@@ -48,16 +49,19 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
}
public void onAcquired(VelocityTracker element) {
- element.clear();
}
public void onReleased(VelocityTracker element) {
+ element.clear();
}
}, 2));
- private static final int INITIAL_POINTERS = 5;
+ private static Pointer sRecycledPointerListHead;
+ private static int sRecycledPointerCount;
- private static final class PointerData {
+ private static final class Pointer {
+ public Pointer next;
+
public int id;
public float xVelocity;
public float yVelocity;
@@ -65,11 +69,13 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
public final float[] pastX = new float[NUM_PAST];
public final float[] pastY = new float[NUM_PAST];
public final long[] pastTime = new long[NUM_PAST]; // uses Long.MIN_VALUE as a sentinel
+
+ public int generation;
}
- private PointerData[] mPointers = new PointerData[INITIAL_POINTERS];
- private int mNumPointers;
+ private Pointer mPointerListHead; // sorted by id in increasing order
private int mLastTouchIndex;
+ private int mGeneration;
private VelocityTracker mNext;
@@ -115,7 +121,9 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
* Reset the velocity tracker back to its initial state.
*/
public void clear() {
- mNumPointers = 0;
+ releasePointerList(mPointerListHead);
+
+ mPointerListHead = null;
mLastTouchIndex = 0;
}
@@ -134,56 +142,62 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
final int lastTouchIndex = mLastTouchIndex;
final int nextTouchIndex = (lastTouchIndex + 1) % NUM_PAST;
final int finalTouchIndex = (nextTouchIndex + historySize) % NUM_PAST;
+ final int generation = mGeneration++;
- if (pointerCount < mNumPointers) {
- final PointerData[] pointers = mPointers;
- int i = mNumPointers;
- while (--i >= 0) {
- final PointerData pointerData = pointers[i];
- if (ev.findPointerIndex(pointerData.id) == -1) {
- // Pointer went up.
- // Shuffle pointers down to fill the hole. Place the old pointer data at
- // the end so we can recycle it if more pointers are added later.
- mNumPointers -= 1;
- final int remaining = mNumPointers - i;
- if (remaining != 0) {
- System.arraycopy(pointers, i + 1, pointers, i, remaining);
- pointers[mNumPointers] = pointerData;
- }
- }
- }
- }
-
+ mLastTouchIndex = finalTouchIndex;
+
+ // Update pointer data.
+ Pointer previousPointer = null;
for (int i = 0; i < pointerCount; i++){
final int pointerId = ev.getPointerId(i);
- PointerData pointerData = getPointerData(pointerId);
- if (pointerData == null) {
- // Pointer went down.
- // Add a new entry. Write a sentinel at the end of the pastTime trace so we
- // will be able to tell where the trace started.
- final PointerData[] oldPointers = mPointers;
- final int newPointerIndex = mNumPointers;
- if (newPointerIndex < oldPointers.length) {
- pointerData = oldPointers[newPointerIndex];
- if (pointerData == null) {
- pointerData = new PointerData();
- oldPointers[newPointerIndex] = pointerData;
+
+ // Find the pointer data for this pointer id.
+ // This loop is optimized for the common case where pointer ids in the event
+ // are in sorted order. However, we check for this case explicitly and
+ // perform a full linear scan from the start if needed.
+ Pointer nextPointer;
+ if (previousPointer == null || pointerId < previousPointer.id) {
+ previousPointer = null;
+ nextPointer = mPointerListHead;
+ } else {
+ nextPointer = previousPointer.next;
+ }
+
+ final Pointer pointer;
+ for (;;) {
+ if (nextPointer != null) {
+ final int nextPointerId = nextPointer.id;
+ if (nextPointerId == pointerId) {
+ pointer = nextPointer;
+ break;
+ }
+ if (nextPointerId < pointerId) {
+ nextPointer = nextPointer.next;
+ continue;
}
+ }
+
+ // Pointer went down. Add it to the list.
+ // Write a sentinel at the end of the pastTime trace so we will be able to
+ // tell when the trace started.
+ pointer = obtainPointer();
+ pointer.id = pointerId;
+ pointer.pastTime[lastTouchIndex] = Long.MIN_VALUE;
+ pointer.next = nextPointer;
+ if (previousPointer == null) {
+ mPointerListHead = pointer;
} else {
- final PointerData[] newPointers = new PointerData[newPointerIndex * 2];
- System.arraycopy(oldPointers, 0, newPointers, 0, newPointerIndex);
- mPointers = newPointers;
- pointerData = new PointerData();
- newPointers[newPointerIndex] = pointerData;
+ previousPointer.next = pointer;
}
- pointerData.id = pointerId;
- pointerData.pastTime[lastTouchIndex] = Long.MIN_VALUE;
- mNumPointers += 1;
+ break;
}
- final float[] pastX = pointerData.pastX;
- final float[] pastY = pointerData.pastY;
- final long[] pastTime = pointerData.pastTime;
+ pointer.generation = generation;
+ previousPointer = pointer;
+
+ final float[] pastX = pointer.pastX;
+ final float[] pastY = pointer.pastY;
+ final long[] pastTime = pointer.pastTime;
for (int j = 0; j < historySize; j++) {
final int touchIndex = (nextTouchIndex + j) % NUM_PAST;
@@ -196,7 +210,23 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
pastTime[finalTouchIndex] = ev.getEventTime();
}
- mLastTouchIndex = finalTouchIndex;
+ // Find removed pointers.
+ previousPointer = null;
+ for (Pointer pointer = mPointerListHead; pointer != null; ) {
+ final Pointer nextPointer = pointer.next;
+ if (pointer.generation != generation) {
+ // Pointer went up. Remove it from the list.
+ if (previousPointer == null) {
+ mPointerListHead = nextPointer;
+ } else {
+ previousPointer.next = nextPointer;
+ }
+ releasePointer(pointer);
+ } else {
+ previousPointer = pointer;
+ }
+ pointer = nextPointer;
+ }
}
/**
@@ -223,13 +253,10 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
* must be positive.
*/
public void computeCurrentVelocity(int units, float maxVelocity) {
- final int numPointers = mNumPointers;
- final PointerData[] pointers = mPointers;
final int lastTouchIndex = mLastTouchIndex;
- for (int p = 0; p < numPointers; p++) {
- final PointerData pointerData = pointers[p];
- final long[] pastTime = pointerData.pastTime;
+ for (Pointer pointer = mPointerListHead; pointer != null; pointer = pointer.next) {
+ final long[] pastTime = pointer.pastTime;
// Search backwards in time for oldest acceptable time.
// Stop at the beginning of the trace as indicated by the sentinel time Long.MIN_VALUE.
@@ -253,8 +280,8 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
}
// Kind-of stupid.
- final float[] pastX = pointerData.pastX;
- final float[] pastY = pointerData.pastY;
+ final float[] pastX = pointer.pastX;
+ final float[] pastY = pointer.pastY;
final float oldestX = pastX[oldestTouchIndex];
final float oldestY = pastY[oldestTouchIndex];
@@ -290,11 +317,11 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
accumY = maxVelocity;
}
- pointerData.xVelocity = accumX;
- pointerData.yVelocity = accumY;
+ pointer.xVelocity = accumX;
+ pointer.yVelocity = accumY;
if (localLOGV) {
- Log.v(TAG, "[" + p + "] Pointer " + pointerData.id
+ Log.v(TAG, "Pointer " + pointer.id
+ ": Y velocity=" + accumX +" X velocity=" + accumY + " N=" + numTouches);
}
}
@@ -307,8 +334,8 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
* @return The previously computed X velocity.
*/
public float getXVelocity() {
- PointerData pointerData = getPointerData(0);
- return pointerData != null ? pointerData.xVelocity : 0;
+ Pointer pointer = getPointer(0);
+ return pointer != null ? pointer.xVelocity : 0;
}
/**
@@ -318,8 +345,8 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
* @return The previously computed Y velocity.
*/
public float getYVelocity() {
- PointerData pointerData = getPointerData(0);
- return pointerData != null ? pointerData.yVelocity : 0;
+ Pointer pointer = getPointer(0);
+ return pointer != null ? pointer.yVelocity : 0;
}
/**
@@ -330,8 +357,8 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
* @return The previously computed X velocity.
*/
public float getXVelocity(int id) {
- PointerData pointerData = getPointerData(id);
- return pointerData != null ? pointerData.xVelocity : 0;
+ Pointer pointer = getPointer(id);
+ return pointer != null ? pointer.xVelocity : 0;
}
/**
@@ -342,19 +369,68 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
* @return The previously computed Y velocity.
*/
public float getYVelocity(int id) {
- PointerData pointerData = getPointerData(id);
- return pointerData != null ? pointerData.yVelocity : 0;
+ Pointer pointer = getPointer(id);
+ return pointer != null ? pointer.yVelocity : 0;
}
- private final PointerData getPointerData(int id) {
- final PointerData[] pointers = mPointers;
- final int numPointers = mNumPointers;
- for (int p = 0; p < numPointers; p++) {
- PointerData pointerData = pointers[p];
- if (pointerData.id == id) {
- return pointerData;
+ private final Pointer getPointer(int id) {
+ for (Pointer pointer = mPointerListHead; pointer != null; pointer = pointer.next) {
+ if (pointer.id == id) {
+ return pointer;
}
}
return null;
}
+
+ private static final Pointer obtainPointer() {
+ synchronized (sPool) {
+ if (sRecycledPointerCount != 0) {
+ Pointer element = sRecycledPointerListHead;
+ sRecycledPointerCount -= 1;
+ sRecycledPointerListHead = element.next;
+ element.next = null;
+ return element;
+ }
+ }
+ return new Pointer();
+ }
+
+ private static final void releasePointer(Pointer pointer) {
+ synchronized (sPool) {
+ if (sRecycledPointerCount < POINTER_POOL_CAPACITY) {
+ pointer.next = sRecycledPointerListHead;
+ sRecycledPointerCount += 1;
+ sRecycledPointerListHead = pointer;
+ }
+ }
+ }
+
+ private static final void releasePointerList(Pointer pointer) {
+ if (pointer != null) {
+ synchronized (sPool) {
+ int count = sRecycledPointerCount;
+ if (count >= POINTER_POOL_CAPACITY) {
+ return;
+ }
+
+ Pointer tail = pointer;
+ for (;;) {
+ count += 1;
+ if (count >= POINTER_POOL_CAPACITY) {
+ break;
+ }
+
+ Pointer next = tail.next;
+ if (next == null) {
+ break;
+ }
+ tail = next;
+ }
+
+ tail.next = sRecycledPointerListHead;
+ sRecycledPointerCount = count;
+ sRecycledPointerListHead = pointer;
+ }
+ }
+ }
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 7c644e41b946..570793bda6e0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16,6 +16,7 @@
package android.view;
+import android.graphics.Camera;
import com.android.internal.R;
import com.android.internal.view.menu.MenuBuilder;
@@ -1374,14 +1375,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* Width as measured during measure pass.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "measurement")
protected int mMeasuredWidth;
/**
* Height as measured during measure pass.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "measurement")
protected int mMeasuredHeight;
/**
@@ -1436,8 +1437,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
static final int MEASURED_DIMENSION_SET = 0x00000800;
/** {@hide} */
static final int FORCE_LAYOUT = 0x00001000;
-
- private static final int LAYOUT_REQUIRED = 0x00002000;
+ /** {@hide} */
+ static final int LAYOUT_REQUIRED = 0x00002000;
private static final int PRESSED = 0x00004000;
@@ -1537,6 +1538,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
private static final int AWAKEN_SCROLL_BARS_ON_ATTACH = 0x08000000;
/**
+ * Indicates that pivotX or pivotY were explicitly set and we should not assume the center
+ * for transform operations
+ *
+ * @hide
+ */
+ private static final int PIVOT_EXPLICITLY_SET = 0x10000000;
+
+ /**
* The parent this view is attached to.
* {@hide}
*
@@ -1627,6 +1636,42 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
private boolean mMatrixIsIdentity = true;
/**
+ * The Camera object is used to compute a 3D matrix when rotationX or rotationY are set.
+ */
+ private Camera mCamera = null;
+
+ /**
+ * This matrix is used when computing the matrix for 3D rotations.
+ */
+ private Matrix matrix3D = null;
+
+ /**
+ * These prev values are used to recalculate a centered pivot point when necessary. The
+ * pivot point is only used in matrix operations (when rotation, scale, or translation are
+ * set), so thes values are only used then as well.
+ */
+ private int mPrevWidth = -1;
+ private int mPrevHeight = -1;
+
+ /**
+ * Convenience value to check for float values that are close enough to zero to be considered
+ * zero.
+ */
+ private static float NONZERO_EPSILON = .001f;
+
+ /**
+ * The degrees rotation around the vertical axis through the pivot point.
+ */
+ @ViewDebug.ExportedProperty
+ private float mRotationY = 0f;
+
+ /**
+ * The degrees rotation around the horizontal axis through the pivot point.
+ */
+ @ViewDebug.ExportedProperty
+ private float mRotationX = 0f;
+
+ /**
* The degrees rotation around the pivot point.
*/
@ViewDebug.ExportedProperty
@@ -1684,28 +1729,28 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* to the left edge of this view.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
protected int mLeft;
/**
* The distance in pixels from the left edge of this view's parent
* to the right edge of this view.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
protected int mRight;
/**
* The distance in pixels from the top edge of this view's parent
* to the top edge of this view.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
protected int mTop;
/**
* The distance in pixels from the top edge of this view's parent
* to the bottom edge of this view.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
protected int mBottom;
/**
@@ -1713,14 +1758,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* horizontally.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "scrolling")
protected int mScrollX;
/**
* The offset, in pixels, by which the content of this view is scrolled
* vertically.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "scrolling")
protected int mScrollY;
/**
@@ -1728,28 +1773,28 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* left edge of this view and the left edge of its content.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "padding")
protected int mPaddingLeft;
/**
* The right padding in pixels, that is the distance in pixels between the
* right edge of this view and the right edge of its content.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "padding")
protected int mPaddingRight;
/**
* The top padding in pixels, that is the distance in pixels between the
* top edge of this view and the top edge of its content.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "padding")
protected int mPaddingTop;
/**
* The bottom padding in pixels, that is the distance in pixels between the
* bottom edge of this view and the bottom edge of its content.
* {@hide}
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "padding")
protected int mPaddingBottom;
/**
@@ -1760,13 +1805,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
/**
* Cache the paddingRight set by the user to append to the scrollbar's size.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "padding")
int mUserPaddingRight;
/**
* Cache the paddingBottom set by the user to append to the scrollbar's size.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "padding")
int mUserPaddingBottom;
/**
@@ -1828,8 +1873,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
private int[] mDrawableState = null;
- private SoftReference<Bitmap> mDrawingCache;
- private SoftReference<Bitmap> mUnscaledDrawingCache;
+ private Bitmap mDrawingCache;
+ private Bitmap mUnscaledDrawingCache;
/**
* When this view has focus and the next focus is {@link #FOCUS_LEFT},
@@ -1873,14 +1918,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* The minimum height of the view. We'll try our best to have the height
* of this view to at least this amount.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "measurement")
private int mMinHeight;
/**
* The minimum width of the view. We'll try our best to have the width
* of this view to at least this amount.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "measurement")
private int mMinWidth;
/**
@@ -2722,7 +2767,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
*
* @return True if this view has or contains focus, false otherwise.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "focus")
public boolean hasFocus() {
return (mPrivateFlags & FOCUSED) != 0;
}
@@ -2900,7 +2945,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
*
* @return True if this view has focus, false otherwise.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "focus")
public boolean isFocused() {
return (mPrivateFlags & FOCUSED) != 0;
}
@@ -3311,7 +3356,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
*
* @return true if this view has nothing to draw, false otherwise
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "drawing")
public boolean willNotDraw() {
return (mViewFlags & DRAW_MASK) == WILL_NOT_DRAW;
}
@@ -3334,7 +3379,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
*
* @return true if this view does not cache its drawing, false otherwise
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "drawing")
public boolean willNotCacheDrawing() {
return (mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING;
}
@@ -3509,7 +3554,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* @return True if this view can take focus, or false otherwise.
* @attr ref android.R.styleable#View_focusable
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "focus")
public final boolean isFocusable() {
return FOCUSABLE == (mViewFlags & FOCUSABLE_MASK);
}
@@ -4820,7 +4865,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
*
* @return The width of your view, in pixels.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public final int getWidth() {
return mRight - mLeft;
}
@@ -4830,7 +4875,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
*
* @return The height of your view, in pixels.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public final int getHeight() {
return mBottom - mTop;
}
@@ -4888,6 +4933,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
}
/**
+ * Utility function to determine if the value is far enough away from zero to be
+ * considered non-zero.
+ * @param value A floating point value to check for zero-ness
+ * @return whether the passed-in value is far enough away from zero to be considered non-zero
+ */
+ private static boolean nonzero(float value) {
+ return (value < -NONZERO_EPSILON || value > NONZERO_EPSILON);
+ }
+
+ /**
* Recomputes the transform matrix if necessary.
*
* @return True if the transform matrix is the identity matrix, false otherwise.
@@ -4896,10 +4951,34 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
if (mMatrixDirty) {
// transform-related properties have changed since the last time someone
// asked for the matrix; recalculate it with the current values
+
+ // Figure out if we need to update the pivot point
+ if ((mPrivateFlags & PIVOT_EXPLICITLY_SET) == 0) {
+ if ((mRight - mLeft) != mPrevWidth && (mBottom - mTop) != mPrevHeight) {
+ mPrevWidth = mRight - mLeft;
+ mPrevHeight = mBottom - mTop;
+ mPivotX = (float) mPrevWidth / 2f;
+ mPivotY = (float) mPrevHeight / 2f;
+ }
+ }
mMatrix.reset();
mMatrix.setTranslate(mTranslationX, mTranslationY);
mMatrix.preRotate(mRotation, mPivotX, mPivotY);
mMatrix.preScale(mScaleX, mScaleY, mPivotX, mPivotY);
+ if (nonzero(mRotationX) || nonzero(mRotationY)) {
+ if (mCamera == null) {
+ mCamera = new Camera();
+ matrix3D = new Matrix();
+ }
+ mCamera.save();
+ mCamera.rotateX(mRotationX);
+ mCamera.rotateY(mRotationY);
+ mCamera.getMatrix(matrix3D);
+ matrix3D.preTranslate(-mPivotX, -mPivotY);
+ matrix3D.postTranslate(mPivotX, mPivotY);
+ mMatrix.postConcat(matrix3D);
+ mCamera.restore();
+ }
mMatrixDirty = false;
mMatrixIsIdentity = mMatrix.isIdentity();
mInverseMatrixDirty = true;
@@ -4955,6 +5034,64 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
}
/**
+ * The degrees that the view is rotated around the vertical axis through the pivot point.
+ *
+ * @see #getPivotX()
+ * @see #getPivotY()
+ * @return The degrees of Y rotation.
+ */
+ public float getRotationY() {
+ return mRotationY;
+ }
+
+ /**
+ * Sets the degrees that the view is rotated around the vertical axis through pivot point.
+ *
+ * @param rotationY The degrees of Y rotation.
+ * @see #getPivotX()
+ * @see #getPivotY()
+ */
+ public void setRotationY(float rotationY) {
+ if (mRotationY != rotationY) {
+ // Double-invalidation is necessary to capture view's old and new areas
+ invalidate();
+ mRotationY = rotationY;
+ mMatrixDirty = true;
+ mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+ invalidate();
+ }
+ }
+
+ /**
+ * The degrees that the view is rotated around the horizontal axis through the pivot point.
+ *
+ * @see #getPivotX()
+ * @see #getPivotY()
+ * @return The degrees of X rotation.
+ */
+ public float getRotationX() {
+ return mRotationX;
+ }
+
+ /**
+ * Sets the degrees that the view is rotated around the horizontal axis through pivot point.
+ *
+ * @param rotationX The degrees of X rotation.
+ * @see #getPivotX()
+ * @see #getPivotY()
+ */
+ public void setRotationX(float rotationX) {
+ if (mRotationX != rotationX) {
+ // Double-invalidation is necessary to capture view's old and new areas
+ invalidate();
+ mRotationX = rotationX;
+ mMatrixDirty = true;
+ mPrivateFlags |= DRAWN; // force another invalidation with the new orientation
+ invalidate();
+ }
+ }
+
+ /**
* The amount that the view is scaled in x around the pivot point, as a proportion of
* the view's unscaled width. A value of 1, the default, means that no scaling is applied.
*
@@ -5035,6 +5172,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
/**
* Sets the x location of the point around which the view is
* {@link #setRotation(float) rotated} and {@link #setScaleX(float) scaled}.
+ * By default, the pivot point is centered on the object.
+ * Setting this property disables this behavior and causes the view to use only the
+ * explicitly set pivotX and pivotY values.
*
* @param pivotX The x location of the pivot point.
* @see #getRotation()
@@ -5043,6 +5183,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* @see #getPivotY()
*/
public void setPivotX(float pivotX) {
+ mPrivateFlags |= PIVOT_EXPLICITLY_SET;
if (mPivotX != pivotX) {
// Double-invalidation is necessary to capture view's old and new areas
invalidate();
@@ -5069,7 +5210,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
/**
* Sets the y location of the point around which the view is {@link #setRotation(float) rotated}
- * and {@link #setScaleY(float) scaled}.
+ * and {@link #setScaleY(float) scaled}. By default, the pivot point is centered on the object.
+ * Setting this property disables this behavior and causes the view to use only the
+ * explicitly set pivotX and pivotY values.
*
* @param pivotY The y location of the pivot point.
* @see #getRotation()
@@ -5078,6 +5221,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* @see #getPivotY()
*/
public void setPivotY(float pivotY) {
+ mPrivateFlags |= PIVOT_EXPLICITLY_SET;
if (mPivotY != pivotY) {
// Double-invalidation is necessary to capture view's old and new areas
invalidate();
@@ -5312,15 +5456,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* is still within the view.
*/
private boolean pointInView(float localX, float localY, float slop) {
- if (!hasIdentityMatrix() && mAttachInfo != null) {
- // non-identity matrix: transform the point into the view's coordinates
- final float[] localXY = mAttachInfo.mTmpTransformLocation;
- localXY[0] = localX;
- localXY[1] = localY;
- getInverseMatrix().mapPoints(localXY);
- localX = localXY[0];
- localY = localXY[1];
- }
return localX > -slop && localY > -slop && localX < ((mRight - mLeft) + slop) &&
localY < ((mBottom - mTop) + slop);
}
@@ -5781,7 +5916,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
*
* @return True if this View is guaranteed to be fully opaque, false otherwise.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "drawing")
public boolean isOpaque() {
return (mPrivateFlags & OPAQUE_MASK) == OPAQUE_MASK;
}
@@ -6866,7 +7001,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* @see #setDrawingCacheEnabled(boolean)
* @see #getDrawingCache()
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "drawing")
public boolean isDrawingCacheEnabled() {
return (mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED;
}
@@ -6916,8 +7051,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
if ((mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED) {
buildDrawingCache(autoScale);
}
- return autoScale ? (mDrawingCache == null ? null : mDrawingCache.get()) :
- (mUnscaledDrawingCache == null ? null : mUnscaledDrawingCache.get());
+ return autoScale ? mDrawingCache : mUnscaledDrawingCache;
}
/**
@@ -6932,13 +7066,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
*/
public void destroyDrawingCache() {
if (mDrawingCache != null) {
- final Bitmap bitmap = mDrawingCache.get();
- if (bitmap != null) bitmap.recycle();
+ mDrawingCache.recycle();
mDrawingCache = null;
}
if (mUnscaledDrawingCache != null) {
- final Bitmap bitmap = mUnscaledDrawingCache.get();
- if (bitmap != null) bitmap.recycle();
+ mUnscaledDrawingCache.recycle();
mUnscaledDrawingCache = null;
}
}
@@ -6999,8 +7131,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
*/
public void buildDrawingCache(boolean autoScale) {
if ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || (autoScale ?
- (mDrawingCache == null || mDrawingCache.get() == null) :
- (mUnscaledDrawingCache == null || mUnscaledDrawingCache.get() == null))) {
+ mDrawingCache == null : mUnscaledDrawingCache == null)) {
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.BUILD_CACHE);
@@ -7033,8 +7164,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
}
boolean clear = true;
- Bitmap bitmap = autoScale ? (mDrawingCache == null ? null : mDrawingCache.get()) :
- (mUnscaledDrawingCache == null ? null : mUnscaledDrawingCache.get());
+ Bitmap bitmap = autoScale ? mDrawingCache : mUnscaledDrawingCache;
if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) {
Bitmap.Config quality;
@@ -7066,9 +7196,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
bitmap = Bitmap.createBitmap(width, height, quality);
bitmap.setDensity(getResources().getDisplayMetrics().densityDpi);
if (autoScale) {
- mDrawingCache = new SoftReference<Bitmap>(bitmap);
+ mDrawingCache = bitmap;
} else {
- mUnscaledDrawingCache = new SoftReference<Bitmap>(bitmap);
+ mUnscaledDrawingCache = bitmap;
}
if (opaque && translucentWindow) bitmap.setHasAlpha(false);
} catch (OutOfMemoryError e) {
@@ -8735,7 +8865,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* @return the offset of the baseline within the widget's bounds or -1
* if baseline alignment is not supported
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public int getBaseline() {
return -1;
}
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 5dd45f9df03c..2ca08ea8be29 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -255,6 +255,14 @@ public class ViewDebug {
* @see #deepExport()
*/
String prefix() default "";
+
+ /**
+ * Specifies the category the property falls into, such as measurement,
+ * layout, drawing, etc.
+ *
+ * @return the category as String
+ */
+ String category() default "";
}
/**
@@ -934,65 +942,76 @@ public class ViewDebug {
private static void profileViewAndChildren(final View view, BufferedWriter out)
throws IOException {
- final long durationMeasure = profileViewOperation(view, new ViewOperation<Void>() {
- public Void[] pre() {
- forceLayout(view);
- return null;
- }
-
- private void forceLayout(View view) {
- view.forceLayout();
- if (view instanceof ViewGroup) {
- ViewGroup group = (ViewGroup) view;
- final int count = group.getChildCount();
- for (int i = 0; i < count; i++) {
- forceLayout(group.getChildAt(i));
- }
- }
- }
+ profileViewAndChildren(view, out, true);
+ }
- public void run(Void... data) {
- view.measure(view.mOldWidthMeasureSpec, view.mOldHeightMeasureSpec);
- }
+ private static void profileViewAndChildren(final View view, BufferedWriter out, boolean root)
+ throws IOException {
- public void post(Void... data) {
- }
- });
+ long durationMeasure =
+ (root || (view.mPrivateFlags & View.MEASURED_DIMENSION_SET) != 0) ? profileViewOperation(
+ view, new ViewOperation<Void>() {
+ public Void[] pre() {
+ forceLayout(view);
+ return null;
+ }
- final long durationLayout = profileViewOperation(view, new ViewOperation<Void>() {
- public Void[] pre() {
- return null;
- }
+ private void forceLayout(View view) {
+ view.forceLayout();
+ if (view instanceof ViewGroup) {
+ ViewGroup group = (ViewGroup) view;
+ final int count = group.getChildCount();
+ for (int i = 0; i < count; i++) {
+ forceLayout(group.getChildAt(i));
+ }
+ }
+ }
- public void run(Void... data) {
- view.layout(view.mLeft, view.mTop, view.mRight, view.mBottom);
- }
+ public void run(Void... data) {
+ view.measure(view.mOldWidthMeasureSpec, view.mOldHeightMeasureSpec);
+ }
- public void post(Void... data) {
- }
- });
+ public void post(Void... data) {
+ }
+ })
+ : 0;
+ long durationLayout =
+ (root || (view.mPrivateFlags & View.LAYOUT_REQUIRED) != 0) ? profileViewOperation(
+ view, new ViewOperation<Void>() {
+ public Void[] pre() {
+ return null;
+ }
- final long durationDraw = profileViewOperation(view, new ViewOperation<Object>() {
- public Object[] pre() {
- final DisplayMetrics metrics = view.getResources().getDisplayMetrics();
- final Bitmap bitmap =
- Bitmap.createBitmap(metrics.widthPixels, metrics.heightPixels,
- Bitmap.Config.RGB_565);
- final Canvas canvas = new Canvas(bitmap);
- return new Object[] {
- bitmap, canvas
- };
- }
+ public void run(Void... data) {
+ view.layout(view.mLeft, view.mTop, view.mRight, view.mBottom);
+ }
- public void run(Object... data) {
- view.draw((Canvas) data[1]);
- }
+ public void post(Void... data) {
+ }
+ }) : 0;
+ long durationDraw =
+ (root || (view.mPrivateFlags & View.DRAWN) != 0) ? profileViewOperation(view,
+ new ViewOperation<Object>() {
+ public Object[] pre() {
+ final DisplayMetrics metrics =
+ view.getResources().getDisplayMetrics();
+ final Bitmap bitmap =
+ Bitmap.createBitmap(metrics.widthPixels,
+ metrics.heightPixels, Bitmap.Config.RGB_565);
+ final Canvas canvas = new Canvas(bitmap);
+ return new Object[] {
+ bitmap, canvas
+ };
+ }
- public void post(Object... data) {
- ((Bitmap) data[0]).recycle();
- }
- });
+ public void run(Object... data) {
+ view.draw((Canvas) data[1]);
+ }
+ public void post(Object... data) {
+ ((Bitmap) data[0]).recycle();
+ }
+ }) : 0;
out.write(String.valueOf(durationMeasure));
out.write(' ');
out.write(String.valueOf(durationLayout));
@@ -1003,7 +1022,7 @@ public class ViewDebug {
ViewGroup group = (ViewGroup) view;
final int count = group.getChildCount();
for (int i = 0; i < count; i++) {
- profileViewAndChildren(group.getChildAt(i), out);
+ profileViewAndChildren(group.getChildAt(i), out, false);
}
}
}
@@ -1033,7 +1052,10 @@ public class ViewDebug {
});
try {
- latch.await(CAPTURE_TIMEOUT, TimeUnit.MILLISECONDS);
+ if (!latch.await(CAPTURE_TIMEOUT, TimeUnit.MILLISECONDS)) {
+ Log.w("View", "Could not complete the profiling of the view " + view);
+ return -1;
+ }
} catch (InterruptedException e) {
Log.w("View", "Could not complete the profiling of the view " + view);
Thread.currentThread().interrupt();
@@ -1354,9 +1376,12 @@ public class ViewDebug {
// TODO: This should happen on the UI thread
Object methodValue = method.invoke(view, (Object[]) null);
final Class<?> returnType = method.getReturnType();
+ final ExportedProperty property = sAnnotations.get(method);
+ String categoryPrefix =
+ property.category().length() != 0 ? property.category() + ":" : "";
if (returnType == int.class) {
- final ExportedProperty property = sAnnotations.get(method);
+
if (property.resolveId() && context != null) {
final int id = (Integer) methodValue;
methodValue = resolveId(context, id);
@@ -1364,7 +1389,8 @@ public class ViewDebug {
final FlagToString[] flagsMapping = property.flagMapping();
if (flagsMapping.length > 0) {
final int intValue = (Integer) methodValue;
- final String valuePrefix = prefix + method.getName() + '_';
+ final String valuePrefix =
+ categoryPrefix + prefix + method.getName() + '_';
exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix);
}
@@ -1388,21 +1414,22 @@ public class ViewDebug {
}
}
} else if (returnType == int[].class) {
- final ExportedProperty property = sAnnotations.get(method);
final int[] array = (int[]) methodValue;
- final String valuePrefix = prefix + method.getName() + '_';
+ final String valuePrefix = categoryPrefix + prefix + method.getName() + '_';
final String suffix = "()";
exportUnrolledArray(context, out, property, array, valuePrefix, suffix);
+
+ // Probably want to return here, same as for fields.
+ return;
} else if (!returnType.isPrimitive()) {
- final ExportedProperty property = sAnnotations.get(method);
if (property.deepExport()) {
dumpViewProperties(context, methodValue, out, prefix + property.prefix());
continue;
}
}
- writeEntry(out, prefix, method.getName(), "()", methodValue);
+ writeEntry(out, categoryPrefix + prefix, method.getName(), "()", methodValue);
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
}
@@ -1422,9 +1449,12 @@ public class ViewDebug {
try {
Object fieldValue = null;
final Class<?> type = field.getType();
+ final ExportedProperty property = sAnnotations.get(field);
+ String categoryPrefix =
+ property.category().length() != 0 ? property.category() + ":" : "";
if (type == int.class) {
- final ExportedProperty property = sAnnotations.get(field);
+
if (property.resolveId() && context != null) {
final int id = field.getInt(view);
fieldValue = resolveId(context, id);
@@ -1432,7 +1462,8 @@ public class ViewDebug {
final FlagToString[] flagsMapping = property.flagMapping();
if (flagsMapping.length > 0) {
final int intValue = field.getInt(view);
- final String valuePrefix = prefix + field.getName() + '_';
+ final String valuePrefix =
+ categoryPrefix + prefix + field.getName() + '_';
exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix);
}
@@ -1454,9 +1485,8 @@ public class ViewDebug {
}
}
} else if (type == int[].class) {
- final ExportedProperty property = sAnnotations.get(field);
final int[] array = (int[]) field.get(view);
- final String valuePrefix = prefix + field.getName() + '_';
+ final String valuePrefix = categoryPrefix + prefix + field.getName() + '_';
final String suffix = "";
exportUnrolledArray(context, out, property, array, valuePrefix, suffix);
@@ -1464,10 +1494,9 @@ public class ViewDebug {
// We exit here!
return;
} else if (!type.isPrimitive()) {
- final ExportedProperty property = sAnnotations.get(field);
if (property.deepExport()) {
- dumpViewProperties(context, field.get(view), out,
- prefix + property.prefix());
+ dumpViewProperties(context, field.get(view), out, prefix
+ + property.prefix());
continue;
}
}
@@ -1476,7 +1505,7 @@ public class ViewDebug {
fieldValue = field.get(view);
}
- writeEntry(out, prefix, field.getName(), "", fieldValue);
+ writeEntry(out, categoryPrefix + prefix, field.getName(), "", fieldValue);
} catch (IllegalAccessException e) {
}
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 9da56378684c..e2f9c1513238 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -377,7 +377,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* @return one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
* {@link #FOCUS_BLOCK_DESCENDANTS}.
*/
- @ViewDebug.ExportedProperty(mapping = {
+ @ViewDebug.ExportedProperty(category = "focus", mapping = {
@ViewDebug.IntToString(from = FOCUS_BEFORE_DESCENDANTS, to = "FOCUS_BEFORE_DESCENDANTS"),
@ViewDebug.IntToString(from = FOCUS_AFTER_DESCENDANTS, to = "FOCUS_AFTER_DESCENDANTS"),
@ViewDebug.IntToString(from = FOCUS_BLOCK_DESCENDANTS, to = "FOCUS_BLOCK_DESCENDANTS")
@@ -905,19 +905,16 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
// Calculate the offset point into the target's local coordinates
- float xc;
- float yc;
- if (target.hasIdentityMatrix() || mAttachInfo == null) {
- xc = scrolledXFloat - (float) target.mLeft;
- yc = scrolledYFloat - (float) target.mTop;
- } else {
+ float xc = scrolledXFloat - (float) target.mLeft;
+ float yc = scrolledYFloat - (float) target.mTop;
+ if (!target.hasIdentityMatrix() && mAttachInfo != null) {
// non-identity matrix: transform the point into the view's coordinates
final float[] localXY = mAttachInfo.mTmpTransformLocation;
- localXY[0] = scrolledXFloat;
- localXY[1] = scrolledYFloat;
+ localXY[0] = xc;
+ localXY[1] = yc;
target.getInverseMatrix().mapPoints(localXY);
- xc = localXY[0] - (float) target.mLeft;
- yc = localXY[1] - (float) target.mTop;
+ xc = localXY[0];
+ yc = localXY[1];
}
// if have a target, see if we're allowed to and want to intercept its
@@ -2835,7 +2832,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* @see #setChildrenDrawnWithCacheEnabled(boolean)
* @see View#setDrawingCacheEnabled(boolean)
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "drawing")
public boolean isAlwaysDrawnWithCacheEnabled() {
return (mGroupFlags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE;
}
@@ -2870,7 +2867,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* @see #setAlwaysDrawnWithCacheEnabled(boolean)
* @see #setChildrenDrawnWithCacheEnabled(boolean)
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "drawing")
protected boolean isChildrenDrawnWithCacheEnabled() {
return (mGroupFlags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE;
}
@@ -2902,7 +2899,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* @see #setChildrenDrawingOrderEnabled(boolean)
* @see #getChildDrawingOrder(int, int)
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "drawing")
protected boolean isChildrenDrawingOrderEnabled() {
return (mGroupFlags & FLAG_USE_CHILD_DRAWING_ORDER) == FLAG_USE_CHILD_DRAWING_ORDER;
}
@@ -2939,7 +2936,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
* and {@link #PERSISTENT_ALL_CACHES}
*/
- @ViewDebug.ExportedProperty(mapping = {
+ @ViewDebug.ExportedProperty(category = "drawing", mapping = {
@ViewDebug.IntToString(from = PERSISTENT_NO_CACHE, to = "NONE"),
@ViewDebug.IntToString(from = PERSISTENT_ANIMATION_CACHE, to = "ANIMATION"),
@ViewDebug.IntToString(from = PERSISTENT_SCROLLING_CACHE, to = "SCROLLING"),
@@ -3572,7 +3569,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* constants FILL_PARENT (replaced by MATCH_PARENT ,
* in API Level 8) or WRAP_CONTENT. or an exact size.
*/
- @ViewDebug.ExportedProperty(mapping = {
+ @ViewDebug.ExportedProperty(category = "layout", mapping = {
@ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
@ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
})
@@ -3583,7 +3580,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* constants FILL_PARENT (replaced by MATCH_PARENT ,
* in API Level 8) or WRAP_CONTENT. or an exact size.
*/
- @ViewDebug.ExportedProperty(mapping = {
+ @ViewDebug.ExportedProperty(category = "layout", mapping = {
@ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
@ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
})
@@ -3708,25 +3705,25 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
/**
* The left margin in pixels of the child.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public int leftMargin;
/**
* The top margin in pixels of the child.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public int topMargin;
/**
* The right margin in pixels of the child.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public int rightMargin;
/**
* The bottom margin in pixels of the child.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public int bottomMargin;
/**
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 8abbf58f0118..faa478309fc4 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -33,6 +33,7 @@ import android.util.Config;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.EventLog;
+import android.util.Slog;
import android.util.SparseArray;
import android.view.View.MeasureSpec;
import android.view.accessibility.AccessibilityEvent;
@@ -1719,7 +1720,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
if (LOCAL_LOGV) Log.v(
TAG, "Dispatching key "
+ msg.obj + " to " + mView);
- deliverKeyEvent((KeyEvent)msg.obj, true);
+ deliverKeyEvent((KeyEvent)msg.obj, msg.arg1 != 0);
break;
case DISPATCH_POINTER: {
MotionEvent event = (MotionEvent) msg.obj;
@@ -1860,9 +1861,14 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
}
private void finishKeyEvent(KeyEvent event) {
+ if (LOCAL_LOGV) Log.v(TAG, "Telling window manager key is finished");
+
if (mFinishedCallback != null) {
mFinishedCallback.run();
mFinishedCallback = null;
+ } else {
+ Slog.w(TAG, "Attempted to tell the input queue that the current key event "
+ + "is finished but there is no key event actually in progress.");
}
}
@@ -2321,8 +2327,6 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
boolean handled = mView == null || mView.dispatchKeyEventPreIme(event);
if (handled) {
if (sendDone) {
- if (LOCAL_LOGV) Log.v(
- TAG, "Telling window manager key is finished");
finishKeyEvent(event);
}
return;
@@ -2353,8 +2357,6 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
if (!handled) {
deliverKeyEventToViewHierarchy(event, sendDone);
} else if (sendDone) {
- if (LOCAL_LOGV) Log.v(
- TAG, "Telling window manager key is finished");
finishKeyEvent(event);
} else {
Log.w(TAG, "handleFinishedEvent(seq=" + seq
@@ -2428,8 +2430,6 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
} finally {
if (sendDone) {
- if (LOCAL_LOGV) Log.v(
- TAG, "Telling window manager key is finished");
finishKeyEvent(event);
}
// Let the exception fall through -- the looper will catch
@@ -2613,9 +2613,14 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
private final InputHandler mInputHandler = new InputHandler() {
public void handleKey(KeyEvent event, Runnable finishedCallback) {
+ if (mFinishedCallback != null) {
+ Slog.w(TAG, "Received a new key event from the input queue but there is "
+ + "already an unfinished key event in progress.");
+ }
+
mFinishedCallback = finishedCallback;
- dispatchKey(event);
+ dispatchKey(event, true);
}
public void handleMotion(MotionEvent event, Runnable finishedCallback) {
@@ -2626,9 +2631,13 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
};
public void dispatchKey(KeyEvent event) {
- if (event.getAction() == KeyEvent.ACTION_DOWN) {
- //noinspection ConstantConditions
- if (false && event.getKeyCode() == KeyEvent.KEYCODE_CAMERA) {
+ dispatchKey(event, false);
+ }
+
+ private void dispatchKey(KeyEvent event, boolean sendDone) {
+ //noinspection ConstantConditions
+ if (false && event.getAction() == KeyEvent.ACTION_DOWN) {
+ if (event.getKeyCode() == KeyEvent.KEYCODE_CAMERA) {
if (DBG) Log.d("keydisp", "===================================================");
if (DBG) Log.d("keydisp", "Focused view Hierarchy is:");
@@ -2640,6 +2649,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
Message msg = obtainMessage(DISPATCH_KEY);
msg.obj = event;
+ msg.arg1 = sendDone ? 1 : 0;
if (LOCAL_LOGV) Log.v(
TAG, "sending key " + event + " to " + mView);
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index f32ff77a8391..36f6bb2466de 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -64,14 +64,24 @@ public abstract class Window {
*/
public static final int FEATURE_ACTION_BAR = 8;
/**
+ * Flag for requesting an Action Bar that overlays window content.
+ * Normally an Action Bar will sit in the space above window content, but if this
+ * feature is requested along with {@link #FEATURE_ACTION_BAR} it will be layered over
+ * the window content itself. This is useful if you would like your app to have more control
+ * over how the Action Bar is displayed, such as letting application content scroll beneath
+ * an Action Bar with a transparent background or otherwise displaying a transparent/translucent
+ * Action Bar over application content.
+ */
+ public static final int FEATURE_ACTION_BAR_OVERLAY = 9;
+ /**
* Flag for specifying the behavior of action modes when an Action Bar is not present.
* If overlay is enabled, the action mode UI will be allowed to cover existing window content.
*/
- public static final int FEATURE_ACTION_MODE_OVERLAY = 9;
+ public static final int FEATURE_ACTION_MODE_OVERLAY = 10;
/**
* Flag for requesting this window to be hardware accelerated, if possible.
*/
- public static final int FEATURE_HARDWARE_ACCELERATED = 10;
+ public static final int FEATURE_HARDWARE_ACCELERATED = 11;
/** Flag for setting the progress bar's visibility to VISIBLE */
public static final int PROGRESS_VISIBILITY_ON = -1;
/** Flag for setting the progress bar's visibility to GONE */
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 33757f023ec3..659f9cd673c6 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -779,11 +779,6 @@ public interface WindowManagerPolicy {
*/
public void enableScreenAfterBoot();
- /**
- * Called every time the window manager is dispatching a pointer event.
- */
- public void dispatchedPointerEventLw(MotionEvent ev, int targetX, int targetY);
-
public void setCurrentOrientationLw(int newOrientation);
/**
diff --git a/core/java/android/view/WindowOrientationListener.java b/core/java/android/view/WindowOrientationListener.java
index 25df1f41b18d..fed55dcb10a3 100755
--- a/core/java/android/view/WindowOrientationListener.java
+++ b/core/java/android/view/WindowOrientationListener.java
@@ -68,7 +68,7 @@ public abstract class WindowOrientationListener {
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
if (mSensor != null) {
// Create listener only if sensors do exist
- mSensorEventListener = new SensorEventListenerImpl();
+ mSensorEventListener = new SensorEventListenerImpl(this);
}
}
@@ -109,8 +109,35 @@ public abstract class WindowOrientationListener {
}
return -1;
}
-
- class SensorEventListenerImpl implements SensorEventListener {
+
+ /**
+ * This class filters the raw accelerometer data and tries to detect actual changes in
+ * orientation. This is a very ill-defined problem so there are a lot of tweakable parameters,
+ * but here's the outline:
+ *
+ * - Convert the acceleromter vector from cartesian to spherical coordinates. Since we're
+ * dealing with rotation of the device, this is the sensible coordinate system to work in. The
+ * zenith direction is the Z-axis, i.e. the direction the screen is facing. The radial distance
+ * is referred to as the magnitude below. The elevation angle is referred to as the "tilt"
+ * below. The azimuth angle is referred to as the "orientation" below (and the azimuth axis is
+ * the Y-axis). See http://en.wikipedia.org/wiki/Spherical_coordinate_system for reference.
+ *
+ * - Low-pass filter the tilt and orientation angles to avoid "twitchy" behavior.
+ *
+ * - When the orientation angle reaches a certain threshold, transition to the corresponding
+ * orientation. These thresholds have some hysteresis built-in to avoid oscillation.
+ *
+ * - Use the magnitude to judge the accuracy of the data. Under ideal conditions, the magnitude
+ * should equal to that of gravity. When it differs significantly, we know the device is under
+ * external acceleration and we can't trust the data.
+ *
+ * - Use the tilt angle to judge the accuracy of orientation data. When the tilt angle is high
+ * in magnitude, we distrust the orientation data, because when the device is nearly flat, small
+ * physical movements produce large changes in orientation angle.
+ *
+ * Details are explained below.
+ */
+ static class SensorEventListenerImpl implements SensorEventListener {
// We work with all angles in degrees in this class.
private static final float RADIANS_TO_DEGREES = (float) (180 / Math.PI);
@@ -125,54 +152,50 @@ public abstract class WindowOrientationListener {
private static final int ROTATION_90 = 1;
private static final int ROTATION_270 = 2;
- // Current orientation state
- private int mRotation = ROTATION_0;
-
// Mapping our internal aliases into actual Surface rotation values
- private final int[] SURFACE_ROTATIONS = new int[] {Surface.ROTATION_0, Surface.ROTATION_90,
- Surface.ROTATION_270};
+ private static final int[] SURFACE_ROTATIONS = new int[] {
+ Surface.ROTATION_0, Surface.ROTATION_90, Surface.ROTATION_270};
// Threshold ranges of orientation angle to transition into other orientation states.
// The first list is for transitions from ROTATION_0, the next for ROTATION_90, etc.
// ROTATE_TO defines the orientation each threshold range transitions to, and must be kept
// in sync with this.
- // The thresholds are nearly regular -- we generally transition about the halfway point
- // between two states with a swing of 30 degrees for hysteresis. For ROTATION_180,
- // however, we enforce stricter thresholds, pushing the thresholds 15 degrees closer to 180.
- private final int[][][] THRESHOLDS = new int[][][] {
+ // We generally transition about the halfway point between two states with a swing of 30
+ // degrees for hysteresis.
+ private static final int[][][] THRESHOLDS = new int[][][] {
{{60, 180}, {180, 300}},
+ {{0, 30}, {195, 315}, {315, 360}},
{{0, 45}, {45, 165}, {330, 360}},
- {{0, 30}, {195, 315}, {315, 360}}
};
// See THRESHOLDS
- private final int[][] ROTATE_TO = new int[][] {
- {ROTATION_270, ROTATION_90},
+ private static final int[][] ROTATE_TO = new int[][] {
+ {ROTATION_90, ROTATION_270},
{ROTATION_0, ROTATION_270, ROTATION_0},
- {ROTATION_0, ROTATION_90, ROTATION_0}
+ {ROTATION_0, ROTATION_90, ROTATION_0},
};
- // Maximum absolute tilt angle at which to consider orientation changes. Beyond this (i.e.
- // when screen is facing the sky or ground), we refuse to make any orientation changes.
- private static final int MAX_TILT = 65;
+ // Maximum absolute tilt angle at which to consider orientation data. Beyond this (i.e.
+ // when screen is facing the sky or ground), we completely ignore orientation data.
+ private static final int MAX_TILT = 75;
// Additional limits on tilt angle to transition to each new orientation. We ignore all
- // vectors with tilt beyond MAX_TILT, but we can set stricter limits on transition to a
+ // data with tilt beyond MAX_TILT, but we can set stricter limits on transitions to a
// particular orientation here.
- private final int[] MAX_TRANSITION_TILT = new int[] {MAX_TILT, MAX_TILT, MAX_TILT};
+ private static final int[] MAX_TRANSITION_TILT = new int[] {MAX_TILT, 65, 65};
// Between this tilt angle and MAX_TILT, we'll allow orientation changes, but we'll filter
// with a higher time constant, making us less sensitive to change. This primarily helps
// prevent momentary orientation changes when placing a device on a table from the side (or
// picking one up).
- private static final int PARTIAL_TILT = 45;
+ private static final int PARTIAL_TILT = 50;
// Maximum allowable deviation of the magnitude of the sensor vector from that of gravity,
// in m/s^2. Beyond this, we assume the phone is under external forces and we can't trust
// the sensor data. However, under constantly vibrating conditions (think car mount), we
// still want to pick up changes, so rather than ignore the data, we filter it with a very
// high time constant.
- private static final int MAX_DEVIATION_FROM_GRAVITY = 1;
+ private static final float MAX_DEVIATION_FROM_GRAVITY = 1.5f;
// Actual sampling period corresponding to SensorManager.SENSOR_DELAY_NORMAL. There's no
// way to get this information from SensorManager.
@@ -185,28 +208,46 @@ public abstract class WindowOrientationListener {
// background.
// When device is near-vertical (screen approximately facing the horizon)
- private static final int DEFAULT_TIME_CONSTANT_MS = 200;
+ private static final int DEFAULT_TIME_CONSTANT_MS = 50;
// When device is partially tilted towards the sky or ground
- private static final int TILTED_TIME_CONSTANT_MS = 600;
+ private static final int TILTED_TIME_CONSTANT_MS = 300;
// When device is under external acceleration, i.e. not just gravity. We heavily distrust
// such readings.
- private static final int ACCELERATING_TIME_CONSTANT_MS = 5000;
+ private static final int ACCELERATING_TIME_CONSTANT_MS = 2000;
private static final float DEFAULT_LOWPASS_ALPHA =
- (float) SAMPLING_PERIOD_MS / (DEFAULT_TIME_CONSTANT_MS + SAMPLING_PERIOD_MS);
+ computeLowpassAlpha(DEFAULT_TIME_CONSTANT_MS);
private static final float TILTED_LOWPASS_ALPHA =
- (float) SAMPLING_PERIOD_MS / (TILTED_TIME_CONSTANT_MS + SAMPLING_PERIOD_MS);
+ computeLowpassAlpha(TILTED_TIME_CONSTANT_MS);
private static final float ACCELERATING_LOWPASS_ALPHA =
- (float) SAMPLING_PERIOD_MS / (ACCELERATING_TIME_CONSTANT_MS + SAMPLING_PERIOD_MS);
+ computeLowpassAlpha(ACCELERATING_TIME_CONSTANT_MS);
+
+ private WindowOrientationListener mOrientationListener;
+ private int mRotation = ROTATION_0; // Current orientation state
+ private float mTiltAngle = 0; // low-pass filtered
+ private float mOrientationAngle = 0; // low-pass filtered
- // The low-pass filtered accelerometer data
- private float[] mFilteredVector = new float[] {0, 0, 0};
+ /*
+ * Each "distrust" counter represents our current level of distrust in the data based on
+ * a certain signal. For each data point that is deemed unreliable based on that signal,
+ * the counter increases; otherwise, the counter decreases. Exact rules vary.
+ */
+ private int mAccelerationDistrust = 0; // based on magnitude != gravity
+ private int mTiltDistrust = 0; // based on tilt close to +/- 90 degrees
+
+ public SensorEventListenerImpl(WindowOrientationListener orientationListener) {
+ mOrientationListener = orientationListener;
+ }
+
+ private static float computeLowpassAlpha(int timeConstantMs) {
+ return (float) SAMPLING_PERIOD_MS / (timeConstantMs + SAMPLING_PERIOD_MS);
+ }
int getCurrentRotation() {
return SURFACE_ROTATIONS[mRotation];
}
- private void calculateNewRotation(int orientation, int tiltAngle) {
+ private void calculateNewRotation(float orientation, float tiltAngle) {
if (localLOGV) Log.i(TAG, orientation + ", " + tiltAngle + ", " + mRotation);
int thresholdRanges[][] = THRESHOLDS[mRotation];
int row = -1;
@@ -226,7 +267,7 @@ public abstract class WindowOrientationListener {
if (localLOGV) Log.i(TAG, " new rotation = " + rotation);
mRotation = rotation;
- onOrientationChanged(SURFACE_ROTATIONS[rotation]);
+ mOrientationListener.onOrientationChanged(getCurrentRotation());
}
private float lowpassFilter(float newValue, float oldValue, float alpha) {
@@ -238,11 +279,11 @@ public abstract class WindowOrientationListener {
}
/**
- * Absolute angle between upVector and the x-y plane (the plane of the screen), in [0, 90].
- * 90 degrees = screen facing the sky or ground.
+ * Angle between upVector and the x-y plane (the plane of the screen), in [-90, 90].
+ * +/- 90 degrees = screen facing the sky or ground.
*/
private float tiltAngle(float z, float magnitude) {
- return Math.abs((float) Math.asin(z / magnitude) * RADIANS_TO_DEGREES);
+ return (float) Math.asin(z / magnitude) * RADIANS_TO_DEGREES;
}
public void onSensorChanged(SensorEvent event) {
@@ -253,34 +294,124 @@ public abstract class WindowOrientationListener {
float z = event.values[_DATA_Z];
float magnitude = vectorMagnitude(x, y, z);
float deviation = Math.abs(magnitude - SensorManager.STANDARD_GRAVITY);
- float tiltAngle = tiltAngle(z, magnitude);
+ handleAccelerationDistrust(deviation);
+
+ // only filter tilt when we're accelerating
+ float alpha = 1;
+ if (mAccelerationDistrust > 0) {
+ alpha = ACCELERATING_LOWPASS_ALPHA;
+ }
+ float newTiltAngle = tiltAngle(z, magnitude);
+ mTiltAngle = lowpassFilter(newTiltAngle, mTiltAngle, alpha);
+
+ float absoluteTilt = Math.abs(mTiltAngle);
+ if (checkFullyTilted(absoluteTilt)) {
+ return; // when fully tilted, ignore orientation entirely
+ }
+
+ float newOrientationAngle = computeNewOrientation(x, y);
+ filterOrientation(absoluteTilt, newOrientationAngle);
+ calculateNewRotation(mOrientationAngle, absoluteTilt);
+ }
+
+ /**
+ * When accelerating, increment distrust; otherwise, decrement distrust. The idea is that
+ * if a single jolt happens among otherwise good data, we should keep trusting the good
+ * data. On the other hand, if a series of many bad readings comes in (as if the phone is
+ * being rapidly shaken), we should wait until things "settle down", i.e. we get a string
+ * of good readings.
+ *
+ * @param deviation absolute difference between the current magnitude and gravity
+ */
+ private void handleAccelerationDistrust(float deviation) {
+ if (deviation > MAX_DEVIATION_FROM_GRAVITY) {
+ if (mAccelerationDistrust < 5) {
+ mAccelerationDistrust++;
+ }
+ } else if (mAccelerationDistrust > 0) {
+ mAccelerationDistrust--;
+ }
+ }
+
+ /**
+ * Check if the phone is tilted towards the sky or ground and handle that appropriately.
+ * When fully tilted, we automatically push the tilt up to a fixed value; otherwise we
+ * decrement it. The idea is to distrust the first few readings after the phone gets
+ * un-tilted, no matter what, i.e. preventing an accidental transition when the phone is
+ * picked up from a table.
+ *
+ * We also reset the orientation angle to the center of the current screen orientation.
+ * Since there is no real orientation of the phone, we want to ignore the most recent sensor
+ * data and reset it to this value to avoid a premature transition when the phone starts to
+ * get un-tilted.
+ *
+ * @param absoluteTilt the absolute value of the current tilt angle
+ * @return true if the phone is fully tilted
+ */
+ private boolean checkFullyTilted(float absoluteTilt) {
+ boolean fullyTilted = absoluteTilt > MAX_TILT;
+ if (fullyTilted) {
+ if (mRotation == ROTATION_0) {
+ mOrientationAngle = 0;
+ } else if (mRotation == ROTATION_90) {
+ mOrientationAngle = 90;
+ } else { // ROTATION_270
+ mOrientationAngle = 270;
+ }
+
+ if (mTiltDistrust < 3) {
+ mTiltDistrust = 3;
+ }
+ } else if (mTiltDistrust > 0) {
+ mTiltDistrust--;
+ }
+ return fullyTilted;
+ }
+
+ /**
+ * Angle between the x-y projection of upVector and the +y-axis, increasing
+ * clockwise.
+ * 0 degrees = speaker end towards the sky
+ * 90 degrees = right edge of device towards the sky
+ */
+ private float computeNewOrientation(float x, float y) {
+ float orientationAngle = (float) -Math.atan2(-x, y) * RADIANS_TO_DEGREES;
+ // atan2 returns [-180, 180]; normalize to [0, 360]
+ if (orientationAngle < 0) {
+ orientationAngle += 360;
+ }
+ return orientationAngle;
+ }
+
+ /**
+ * Compute a new filtered orientation angle.
+ */
+ private void filterOrientation(float absoluteTilt, float orientationAngle) {
float alpha = DEFAULT_LOWPASS_ALPHA;
- if (tiltAngle > MAX_TILT) {
- return;
- } else if (deviation > MAX_DEVIATION_FROM_GRAVITY) {
+ if (mTiltDistrust > 0 || mAccelerationDistrust > 1) {
+ // when fully tilted, or under more than a transient acceleration, distrust heavily
alpha = ACCELERATING_LOWPASS_ALPHA;
- } else if (tiltAngle > PARTIAL_TILT) {
+ } else if (absoluteTilt > PARTIAL_TILT || mAccelerationDistrust == 1) {
+ // when tilted partway, or under transient acceleration, distrust lightly
alpha = TILTED_LOWPASS_ALPHA;
}
- x = mFilteredVector[0] = lowpassFilter(x, mFilteredVector[0], alpha);
- y = mFilteredVector[1] = lowpassFilter(y, mFilteredVector[1], alpha);
- z = mFilteredVector[2] = lowpassFilter(z, mFilteredVector[2], alpha);
- magnitude = vectorMagnitude(x, y, z);
- tiltAngle = tiltAngle(z, magnitude);
-
- // Angle between the x-y projection of upVector and the +y-axis, increasing
- // counter-clockwise.
- // 0 degrees = speaker end towards the sky
- // 90 degrees = left edge of device towards the sky
- float orientationAngle = (float) Math.atan2(-x, y) * RADIANS_TO_DEGREES;
- int orientation = Math.round(orientationAngle);
- // atan2 returns (-180, 180]; normalize to [0, 360)
- if (orientation < 0) {
- orientation += 360;
+ // since we're lowpass filtering a value with periodic boundary conditions, we need to
+ // adjust the new value to filter in the right direction...
+ float deltaOrientation = orientationAngle - mOrientationAngle;
+ if (deltaOrientation > 180) {
+ orientationAngle -= 360;
+ } else if (deltaOrientation < -180) {
+ orientationAngle += 360;
+ }
+ mOrientationAngle = lowpassFilter(orientationAngle, mOrientationAngle, alpha);
+ // ...and then adjust back to ensure we're in the range [0, 360]
+ if (mOrientationAngle > 360) {
+ mOrientationAngle -= 360;
+ } else if (mOrientationAngle < 0) {
+ mOrientationAngle += 360;
}
- calculateNewRotation(orientation, Math.round(tiltAngle));
}
public void onAccuracyChanged(Sensor sensor, int accuracy) {
diff --git a/core/java/android/webkit/DeviceOrientationManager.java b/core/java/android/webkit/DeviceOrientationManager.java
new file mode 100644
index 000000000000..f65dccf124ff
--- /dev/null
+++ b/core/java/android/webkit/DeviceOrientationManager.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+/**
+ * This class is simply a container for the methods used to configure WebKit's
+ * mock DeviceOrientationClient for use in LayoutTests.
+ *
+ * This could be part of WebViewCore, but have moved it to its own class to
+ * avoid bloat there.
+ * @hide
+ */
+public final class DeviceOrientationManager {
+ private WebViewCore mWebViewCore;
+
+ public DeviceOrientationManager(WebViewCore webViewCore) {
+ mWebViewCore = webViewCore;
+ }
+
+ /**
+ * Sets whether the Page for this WebViewCore should use a mock DeviceOrientation
+ * client.
+ */
+ public void useMock() {
+ assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
+ nativeUseMock(mWebViewCore);
+ }
+
+ /**
+ * Set the position for the mock DeviceOrientation service for this WebViewCore.
+ */
+ public void setMockOrientation(boolean canProvideAlpha, double alpha, boolean canProvideBeta,
+ double beta, boolean canProvideGamma, double gamma) {
+ assert WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName());
+ nativeSetMockOrientation(mWebViewCore, canProvideAlpha, alpha, canProvideBeta, beta,
+ canProvideGamma, gamma);
+ }
+
+ // Native functions
+ private static native void nativeUseMock(WebViewCore webViewCore);
+ private static native void nativeSetMockOrientation(WebViewCore webViewCore,
+ boolean canProvideAlpha, double alpha, boolean canProvideBeta, double beta,
+ boolean canProvideGamma, double gamma);
+}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index d1b0902d8e86..eb363c7ca225 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -3747,6 +3747,26 @@ public class WebView extends AbsoluteLayout
}
/**
+ * Called by DRT on UI thread, need to proxy to WebCore thread.
+ *
+ * @hide debug only
+ */
+ public void useMockDeviceOrientation() {
+ mWebViewCore.sendMessage(EventHub.USE_MOCK_DEVICE_ORIENTATION);
+ }
+
+ /**
+ * Called by DRT on WebCore thread.
+ *
+ * @hide debug only
+ */
+ public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+ boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
+ mWebViewCore.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
+ canProvideGamma, gamma);
+ }
+
+ /**
* Dump the V8 counters to standard output.
* Note that you need a build with V8 and WEBCORE_INSTRUMENTATION set to
* true. Otherwise, this will do nothing.
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 3c28c943ca8e..1e7e6c00434f 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -33,6 +33,7 @@ import android.util.SparseBooleanArray;
import android.view.KeyEvent;
import android.view.SurfaceView;
import android.view.View;
+import android.webkit.DeviceOrientationManager;
import java.util.ArrayList;
import java.util.Collection;
@@ -116,6 +117,8 @@ final class WebViewCore {
private int mWebkitScrollX = 0;
private int mWebkitScrollY = 0;
+ private DeviceOrientationManager mDeviceOrientationManager = new DeviceOrientationManager(this);
+
// The thread name used to identify the WebCore thread and for use in
// debugging other classes that require operation within the WebCore thread.
/* package */ static final String THREAD_NAME = "WebViewCoreThread";
@@ -878,6 +881,8 @@ final class WebViewCore {
// accessibility support
static final int MODIFY_SELECTION = 190;
+ static final int USE_MOCK_DEVICE_ORIENTATION = 191;
+
// private message ids
private static final int DESTROY = 200;
@@ -1409,6 +1414,10 @@ final class WebViewCore {
WebView.SET_TOUCH_HIGHLIGHT_RECTS, null)
.sendToTarget();
break;
+
+ case USE_MOCK_DEVICE_ORIENTATION:
+ useMockDeviceOrientation();
+ break;
}
}
};
@@ -2481,6 +2490,16 @@ final class WebViewCore {
hMode, vMode).sendToTarget();
}
+ private void useMockDeviceOrientation() {
+ mDeviceOrientationManager.useMock();
+ }
+
+ public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+ boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
+ mDeviceOrientationManager.setMockOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
+ canProvideGamma, gamma);
+ }
+
private native void nativePause();
private native void nativeResume();
private native void nativeFreeMemory();
diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java
index d75d4212e048..d7b4452ef66f 100644
--- a/core/java/android/webkit/WebViewDatabase.java
+++ b/core/java/android/webkit/WebViewDatabase.java
@@ -772,6 +772,9 @@ public class WebViewDatabase {
}
long getCacheTotalSize() {
+ if (mCacheDatabase == null) {
+ return 0;
+ }
long size = 0;
Cursor cursor = null;
final String query = "SELECT SUM(contentlength) as sum FROM cache";
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 372cc833ced6..e572d3dc65a4 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -3998,7 +3998,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
* View type for this view, as returned by
* {@link android.widget.Adapter#getItemViewType(int) }
*/
- @ViewDebug.ExportedProperty(mapping = {
+ @ViewDebug.ExportedProperty(category = "list", mapping = {
@ViewDebug.IntToString(from = ITEM_VIEW_TYPE_IGNORE, to = "ITEM_VIEW_TYPE_IGNORE"),
@ViewDebug.IntToString(from = ITEM_VIEW_TYPE_HEADER_OR_FOOTER, to = "ITEM_VIEW_TYPE_HEADER_OR_FOOTER")
})
@@ -4010,7 +4010,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
* been added to the list view and whether they should be treated as
* recycled views or not.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "list")
boolean recycledHeaderFooter;
/**
@@ -4021,7 +4021,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
* view to be attached to the window rather than just attached to the
* parent.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "list")
boolean forceAdd;
public LayoutParams(Context c, AttributeSet attrs) {
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index fe6d91ac9d63..10a87295ccf5 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -56,7 +56,7 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup {
/**
* The position of the first child displayed
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "scrolling")
int mFirstPosition = 0;
/**
@@ -141,7 +141,7 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup {
* The position within the adapter's data set of the item to select
* during the next layout.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "list")
int mNextSelectedPosition = INVALID_POSITION;
/**
@@ -152,7 +152,7 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup {
/**
* The position within the adapter's data set of the currently selected item.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "list")
int mSelectedPosition = INVALID_POSITION;
/**
@@ -168,7 +168,7 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup {
/**
* The number of items in the current adapter.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "list")
int mItemCount;
/**
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index a6d51702c403..2b723c9b4e4b 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -16,14 +16,22 @@
package android.widget;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+
+import android.animation.PropertyAnimator;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
+import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
@@ -35,23 +43,100 @@ import android.view.animation.AnimationUtils;
* @attr ref android.R.styleable#AdapterViewAnimator_outAnimation
* @attr ref android.R.styleable#AdapterViewAnimator_animateFirstView
*/
-public class AdapterViewAnimator extends AdapterView<Adapter> implements RemoteViewsAdapter.RemoteAdapterConnectionCallback{
+public abstract class AdapterViewAnimator extends AdapterView<Adapter>
+ implements RemoteViewsAdapter.RemoteAdapterConnectionCallback{
private static final String TAG = "RemoteViewAnimator";
+ /**
+ * The index of the current child, which appears anywhere from the beginning
+ * to the end of the current set of children, as specified by {@link #mActiveOffset}
+ */
int mWhichChild = 0;
- boolean mFirstTime = true;
+
+ /**
+ * Whether or not the first view(s) should be animated in
+ */
boolean mAnimateFirstTime = true;
+ /**
+ * Represents where the in the current window of
+ * views the current <code>mDisplayedChild</code> sits
+ */
+ int mActiveOffset = 0;
+
+ /**
+ * The number of views that the {@link AdapterViewAnimator} keeps as children at any
+ * given time (not counting views that are pending removal, see {@link #mPreviousViews}).
+ */
+ int mNumActiveViews = 1;
+
+ /**
+ * Array of the children of the {@link AdapterViewAnimator}. This array
+ * is accessed in a circular fashion
+ */
+ View[] mActiveViews;
+
+ /**
+ * List of views pending removal from the {@link AdapterViewAnimator}
+ */
+ ArrayList<View> mPreviousViews;
+
+ /**
+ * The index, relative to the adapter, of the beginning of the window of views
+ */
+ int mCurrentWindowStart = 0;
+
+ /**
+ * The index, relative to the adapter, of the end of the window of views
+ */
+ int mCurrentWindowEnd = -1;
+
+ /**
+ * The same as {@link #mCurrentWindowStart}, except when the we have bounded
+ * {@link #mCurrentWindowStart} to be non-negative
+ */
+ int mCurrentWindowStartUnbounded = 0;
+
+ /**
+ * Indicates whether to treat the adapter to be a circular structure, ie.
+ * the view before 0 is considered to be <code>mAdapter.getCount() - 1</code>
+ *
+ * TODO: this doesn't do anything yet
+ *
+ */
+ boolean mCycleViews = false;
+
+ /**
+ * Handler to post events to the main thread
+ */
+ Handler mMainQueue;
+
+ /**
+ * Listens for data changes from the adapter
+ */
AdapterDataSetObserver mDataSetObserver;
- View mPreviousView;
- View mCurrentView;
+ /**
+ * The {@link Adapter} for this {@link AdapterViewAnimator}
+ */
+ Adapter mAdapter;
+ /**
+ * The {@link RemoteViewsAdapter} for this {@link AdapterViewAnimator}
+ */
+ RemoteViewsAdapter mRemoteViewsAdapter;
+
+ /**
+ * Specifies whether this is the first time the animator is showing views
+ */
+ boolean mFirstTime = true;
+
+ /**
+ * TODO: Animation stuff is still in flux, waiting on the new framework to settle a bit.
+ */
Animation mInAnimation;
Animation mOutAnimation;
- Adapter mAdapter;
- RemoteViewsAdapter mRemoteViewsAdapter;
- private Handler mMainQueue;
+ private ArrayList<View> mViewsToBringToFront;
public AdapterViewAnimator(Context context) {
super(context);
@@ -61,8 +146,10 @@ public class AdapterViewAnimator extends AdapterView<Adapter> implements RemoteV
public AdapterViewAnimator(Context context, AttributeSet attrs) {
super(context, attrs);
- TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.ViewAnimator);
- int resource = a.getResourceId(com.android.internal.R.styleable.ViewAnimator_inAnimation, 0);
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.ViewAnimator);
+ int resource = a.getResourceId(
+ com.android.internal.R.styleable.ViewAnimator_inAnimation, 0);
if (resource > 0) {
setInAnimation(context, resource);
}
@@ -72,7 +159,8 @@ public class AdapterViewAnimator extends AdapterView<Adapter> implements RemoteV
setOutAnimation(context, resource);
}
- boolean flag = a.getBoolean(com.android.internal.R.styleable.ViewAnimator_animateFirstView, true);
+ boolean flag = a.getBoolean(
+ com.android.internal.R.styleable.ViewAnimator_animateFirstView, true);
setAnimateFirstView(flag);
a.recycle();
@@ -85,6 +173,54 @@ public class AdapterViewAnimator extends AdapterView<Adapter> implements RemoteV
*/
private void initViewAnimator(Context context, AttributeSet attrs) {
mMainQueue = new Handler(Looper.myLooper());
+ mActiveViews = new View[mNumActiveViews];
+ mPreviousViews = new ArrayList<View>();
+ mViewsToBringToFront = new ArrayList<View>();
+ }
+
+ /**
+ * This method is used by subclasses to configure the animator to display the
+ * desired number of views, and specify the offset
+ *
+ * @param numVisibleViews The number of views the animator keeps in the {@link ViewGroup}
+ * @param activeOffset This parameter specifies where the current index ({@link mWhichChild})
+ * sits within the window. For example if activeOffset is 1, and numVisibleViews is 3,
+ * and {@link setDisplayedChild} is called with 10, then the effective window will be
+ * the indexes 9, 10, and 11. In the same example, if activeOffset were 0, then the
+ * window would instead contain indexes 10, 11 and 12.
+ */
+ void configureViewAnimator(int numVisibleViews, int activeOffset) {
+ if (activeOffset > numVisibleViews - 1) {
+ // Throw an exception here.
+ }
+ mNumActiveViews = numVisibleViews;
+ mActiveOffset = activeOffset;
+ mActiveViews = new View[mNumActiveViews];
+ mPreviousViews.clear();
+ removeAllViewsInLayout();
+ mCurrentWindowStart = 0;
+ mCurrentWindowEnd = -1;
+ }
+
+ /**
+ * This class should be overridden by subclasses to customize view transitions within
+ * the set of visible views
+ *
+ * @param fromIndex The relative index within the window that the view was in, -1 if it wasn't
+ * in the window
+ * @param toIndex The relative index within the window that the view is going to, -1 if it is
+ * being removed
+ * @param view The view that is being animated
+ */
+ void animateViewForTransition(int fromIndex, int toIndex, View view) {
+ PropertyAnimator pa;
+ if (fromIndex == -1) {
+ pa = new PropertyAnimator(400, view, "alpha", 0.0f, 1.0f);
+ pa.start();
+ } else if (toIndex == -1) {
+ pa = new PropertyAnimator(400, view, "alpha", 1.0f, 0.0f);
+ pa.start();
+ }
}
/**
@@ -114,18 +250,28 @@ public class AdapterViewAnimator extends AdapterView<Adapter> implements RemoteV
/**
* Return default inAnimation. To be overriden by subclasses.
*/
- public Animation getDefaultInAnimation() {
+ Animation getDefaultInAnimation() {
return null;
}
/**
- * Return default outAnimation. To be overriden by subclasses.
+ * Return default outAnimation. To be overridden by subclasses.
*/
- public Animation getDefaultOutAnimation() {
+ Animation getDefaultOutAnimation() {
return null;
}
/**
+ * To be overridden by subclasses. This method applies a view / index specific
+ * transform to the child view.
+ *
+ * @param child
+ * @param relativeIndex
+ */
+ void applyTransformForChildAtIndex(View child, int relativeIndex) {
+ }
+
+ /**
* Returns the index of the currently displayed child view.
*/
public int getDisplayedChild() {
@@ -160,70 +306,137 @@ public class AdapterViewAnimator extends AdapterView<Adapter> implements RemoteV
showOnly(childIndex, animate, false);
}
- private LayoutParams makeLayoutParams() {
- int width = mMeasuredWidth - mPaddingLeft - mPaddingRight;
- int height = mMeasuredHeight - mPaddingTop - mPaddingBottom;
- return new LayoutParams(width, height);
+ private int modulo(int pos, int size) {
+ return (size + (pos % size)) % size;
}
- protected void showOnly(int childIndex, boolean animate, boolean onLayout) {
- if (mAdapter != null) {
- // The previous view should be removed from the ViewGroup
- if (mPreviousView != null) {
- mPreviousView.clearAnimation();
+ /**
+ * Get the view at this index relative to the current window's start
+ *
+ * @param relativeIndex Position relative to the current window's start
+ * @return View at this index, null if the index is outside the bounds
+ */
+ View getViewAtRelativeIndex(int relativeIndex) {
+ if (relativeIndex >= 0 && relativeIndex <= mNumActiveViews - 1) {
+ int index = mCurrentWindowStartUnbounded + relativeIndex;
+ return mActiveViews[modulo(index, mNumActiveViews)];
+ }
+ return null;
+ }
- // TODO: this is where we would store the the view for
- // recycling
- removeViewInLayout(mPreviousView);
- }
+ private LayoutParams createOrReuseLayoutParams(View v) {
+ final LayoutParams currentLp = (LayoutParams) v.getLayoutParams();
+ if (currentLp instanceof LayoutParams) {
+ return currentLp;
+ }
+ return new LayoutParams(v);
+ }
- // If the current view is still being animated, we should
- // force the animation to end
- if (mCurrentView != null) {
- mCurrentView.clearAnimation();
- }
+ void showOnly(int childIndex, boolean animate, boolean onLayout) {
+ if (mAdapter == null) return;
- // load the new mCurrentView from our adapter
- mPreviousView = mCurrentView;
- mCurrentView = mAdapter.getView(childIndex, null, this);
- if (mPreviousView != mCurrentView) {
- addViewInLayout(mCurrentView, 0, makeLayoutParams(), true);
- mCurrentView.bringToFront();
+ for (int i = 0; i < mPreviousViews.size(); i++) {
+ View viewToRemove = mPreviousViews.get(i);
+ viewToRemove.clearAnimation();
+ // applyTransformForChildAtIndex here just allows for any cleanup
+ // associated with this view that may need to be done by a subclass
+ applyTransformForChildAtIndex(viewToRemove, -1);
+ removeViewInLayout(viewToRemove);
+ }
+ mPreviousViews.clear();
+ int newWindowStartUnbounded = childIndex - mActiveOffset;
+ int newWindowEndUnbounded = newWindowStartUnbounded + mNumActiveViews - 1;
+ int newWindowStart = Math.max(0, newWindowStartUnbounded);
+ int newWindowEnd = Math.min(mAdapter.getCount(), newWindowEndUnbounded);
+
+ // This section clears out any items that are in our mActiveViews list
+ // but are outside the effective bounds of our window (this is becomes an issue
+ // at the extremities of the list, eg. where newWindowStartUnbounded < 0 or
+ // newWindowEndUnbounded > mAdapter.getCount() - 1
+ for (int i = newWindowStartUnbounded; i < newWindowEndUnbounded; i++) {
+ if (i < newWindowStart || i > newWindowEnd) {
+ int index = modulo(i, mNumActiveViews);
+ if (mActiveViews[index] != null) {
+ View previousView = mActiveViews[index];
+ mPreviousViews.add(previousView);
+ int previousViewRelativeIndex = modulo(index - mCurrentWindowStart,
+ mNumActiveViews);
+ animateViewForTransition(previousViewRelativeIndex, -1, previousView);
+ }
}
+ }
+ // If the window has changed
+ if (! (newWindowStart == mCurrentWindowStart && newWindowEnd == mCurrentWindowEnd)) {
+ // Run through the indices in the new range
+ for (int i = newWindowStart; i <= newWindowEnd; i++) {
+
+ int oldRelativeIndex = i - mCurrentWindowStartUnbounded;
+ int newRelativeIndex = i - newWindowStartUnbounded;
+ int index = modulo(i, mNumActiveViews);
+
+ // If this item is in the current window, great, we just need to apply
+ // the transform for it's new relative position in the window, and animate
+ // between it's current and new relative positions
+ if (i >= mCurrentWindowStart && i <= mCurrentWindowEnd) {
+ View view = mActiveViews[index];
+ applyTransformForChildAtIndex(view, newRelativeIndex);
+ animateViewForTransition(oldRelativeIndex, newRelativeIndex, view);
+
+ // Otherwise this view is new, so first we have to displace the view that's
+ // taking the new view's place within our cache (a circular array)
+ } else {
+ if (mActiveViews[index] != null) {
+ View previousView = mActiveViews[index];
+ mPreviousViews.add(previousView);
+ int previousViewRelativeIndex = modulo(index - mCurrentWindowStart,
+ mNumActiveViews);
+ animateViewForTransition(previousViewRelativeIndex, -1, previousView);
+
+ if (mCurrentWindowStart > newWindowStart) {
+ mViewsToBringToFront.add(previousView);
+ }
+ }
-
- // Animate as necessary
- if (mPreviousView != null && mPreviousView != mCurrentView) {
- if (animate && mOutAnimation != null) {
- mPreviousView.startAnimation(mOutAnimation);
+ // We've cleared a spot for the new view. Get it from the adapter, add it
+ // and apply any transform / animation
+ View newView = mAdapter.getView(i, null, this);
+ if (newView != null) {
+ mActiveViews[index] = newView;
+ addViewInLayout(newView, -1, createOrReuseLayoutParams(newView));
+ applyTransformForChildAtIndex(newView, newRelativeIndex);
+ animateViewForTransition(-1, newRelativeIndex, newView);
+ }
}
- // This line results in the view becoming invisible *after*
- // the above animation is complete, or, if there is no animation
- // then it becomes invisble immediately
- mPreviousView.setVisibility(View.GONE);
+ mActiveViews[index].bringToFront();
}
- if (mCurrentView != null && animate && mInAnimation != null) {
- mCurrentView.startAnimation(mInAnimation);
+ for (int i = 0; i < mViewsToBringToFront.size(); i++) {
+ View v = mViewsToBringToFront.get(i);
+ v.bringToFront();
}
+ mViewsToBringToFront.clear();
- mFirstTime = false;
- if (!onLayout) {
- requestLayout();
- invalidate();
- } else {
- // If the Adapter tries to layout the current view when we get it using getView above
- // the layout will end up being ignored since we are currently laying out, so
- // we post a delayed requestLayout and invalidate
- mMainQueue.post(new Runnable() {
- @Override
- public void run() {
- mCurrentView.requestLayout();
- mCurrentView.invalidate();
- }
- });
- }
+ mCurrentWindowStart = newWindowStart;
+ mCurrentWindowEnd = newWindowEnd;
+ mCurrentWindowStartUnbounded = newWindowStartUnbounded;
+ }
+
+ mFirstTime = false;
+ if (!onLayout) {
+ requestLayout();
+ invalidate();
+ } else {
+ // If the Adapter tries to layout the current view when we get it using getView
+ // above the layout will end up being ignored since we are currently laying out, so
+ // we post a delayed requestLayout and invalidate
+ mMainQueue.post(new Runnable() {
+ @Override
+ public void run() {
+ requestLayout();
+ invalidate();
+ }
+ });
}
}
@@ -247,10 +460,11 @@ public class AdapterViewAnimator extends AdapterView<Adapter> implements RemoteV
int childRight = mPaddingLeft + child.getMeasuredWidth();
int childBottom = mPaddingTop + child.getMeasuredHeight();
+ LayoutParams lp = (LayoutParams) child.getLayoutParams();
- child.layout(mPaddingLeft, mPaddingTop, childRight, childBottom);
+ child.layout(mPaddingLeft + lp.horizontalOffset, mPaddingTop + lp.verticalOffset,
+ childRight + lp.horizontalOffset, childBottom + lp.verticalOffset);
}
-
mDataChanged = false;
}
@@ -261,8 +475,6 @@ public class AdapterViewAnimator extends AdapterView<Adapter> implements RemoteV
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
- Log.v(TAG, "onMeasure");
-
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
@@ -278,7 +490,6 @@ public class AdapterViewAnimator extends AdapterView<Adapter> implements RemoteV
child.measure(childWidthMeasureSpec, childheightMeasureSpec);
}
-
setMeasuredDimension(widthSpecSize, heightSpecSize);
}
@@ -302,7 +513,7 @@ public class AdapterViewAnimator extends AdapterView<Adapter> implements RemoteV
* @see #getDisplayedChild()
*/
public View getCurrentView() {
- return mCurrentView;
+ return getViewAtRelativeIndex(mActiveOffset);
}
/**
@@ -402,22 +613,25 @@ public class AdapterViewAnimator extends AdapterView<Adapter> implements RemoteV
@Override
public void setAdapter(Adapter adapter) {
+ if (mAdapter != null && mDataSetObserver != null) {
+ mAdapter.unregisterDataSetObserver(mDataSetObserver);
+ }
+
mAdapter = adapter;
if (mAdapter != null) {
- if (mDataSetObserver != null) {
- mAdapter.unregisterDataSetObserver(mDataSetObserver);
- }
-
mDataSetObserver = new AdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
}
+ setFocusable(true);
}
/**
- * Sets up this AdapterViewAnimator to use a remote views adapter which connects to a RemoteViewsService
- * through the specified intent.
- * @param intent the intent used to identify the RemoteViewsService for the adapter to connect to.
+ * Sets up this AdapterViewAnimator to use a remote views adapter which connects to a
+ * RemoteViewsService through the specified intent.
+ *
+ * @param intent the intent used to identify the RemoteViewsService for the adapter to
+ * connect to.
*/
@android.view.RemotableViewMethod
public void setRemoteViewsAdapter(Intent intent) {
@@ -431,7 +645,7 @@ public class AdapterViewAnimator extends AdapterView<Adapter> implements RemoteV
@Override
public View getSelectedView() {
- return mCurrentView;
+ return getViewAtRelativeIndex(mActiveOffset);
}
/**
@@ -452,4 +666,61 @@ public class AdapterViewAnimator extends AdapterView<Adapter> implements RemoteV
setAdapter(mRemoteViewsAdapter);
}
}
+
+ static class LayoutParams extends ViewGroup.LayoutParams {
+ int horizontalOffset;
+ int verticalOffset;
+ View mView;
+
+ LayoutParams(View view) {
+ super(0, 0);
+ horizontalOffset = 0;
+ verticalOffset = 0;
+ mView = view;
+ }
+
+ LayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
+ horizontalOffset = 0;
+ verticalOffset = 0;
+ }
+
+ void setHorizontalOffset(int newHorizontalOffset) {
+ horizontalOffset = newHorizontalOffset;
+ if (mView != null) {
+ mView.requestLayout();
+ mView.invalidate();
+ }
+ }
+
+ private Rect parentRect = new Rect();
+ void invalidateGlobalRegion(View v, Rect r) {
+ View p = v;
+ boolean firstPass = true;
+ parentRect.set(0, 0, 0, 0);
+ while (p.getParent() != null && p.getParent() instanceof View
+ && !parentRect.contains(r)) {
+ if (!firstPass) r.offset(p.getLeft() - p.getScrollX(), p.getTop() - p.getScrollY());
+ firstPass = false;
+ p = (View) p.getParent();
+ parentRect.set(p.getLeft() - p.getScrollX(), p.getTop() - p.getScrollY(),
+ p.getRight() - p.getScrollX(), p.getBottom() - p.getScrollY());
+ }
+ p.invalidate(r.left, r.top, r.right, r.bottom);
+ }
+
+ private Rect invalidateRect = new Rect();
+ // This is public so that PropertyAnimator can access it
+ public void setVerticalOffset(int newVerticalOffset) {
+ int offsetDelta = newVerticalOffset - verticalOffset;
+ verticalOffset = newVerticalOffset;
+ if (mView != null) {
+ mView.requestLayout();
+ int top = Math.min(mView.getTop() + offsetDelta, mView.getTop());
+ int bottom = Math.max(mView.getBottom() + offsetDelta, mView.getBottom());
+ invalidateRect.set(mView.getLeft(), top, mView.getRight(), bottom);
+ invalidateGlobalRegion(mView, invalidateRect);
+ }
+ }
+ }
}
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index e27bb4fe2073..e4451800752b 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -46,27 +46,32 @@ import android.widget.RemoteViews.RemoteView;
*/
@RemoteView
public class FrameLayout extends ViewGroup {
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "measurement")
boolean mMeasureAllChildren = false;
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "drawing")
private Drawable mForeground;
- @ViewDebug.ExportedProperty
+
+ @ViewDebug.ExportedProperty(category = "padding")
private int mForegroundPaddingLeft = 0;
- @ViewDebug.ExportedProperty
+
+ @ViewDebug.ExportedProperty(category = "padding")
private int mForegroundPaddingTop = 0;
- @ViewDebug.ExportedProperty
+
+ @ViewDebug.ExportedProperty(category = "padding")
private int mForegroundPaddingRight = 0;
- @ViewDebug.ExportedProperty
+
+ @ViewDebug.ExportedProperty(category = "padding")
private int mForegroundPaddingBottom = 0;
private final Rect mSelfBounds = new Rect();
private final Rect mOverlayBounds = new Rect();
- @ViewDebug.ExportedProperty
+
+ @ViewDebug.ExportedProperty(category = "drawing")
private int mForegroundGravity = Gravity.FILL;
/** {@hide} */
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "drawing")
protected boolean mForegroundInPadding = true;
boolean mForegroundBoundsChanged = false;
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index 7254c3cfd359..53187bf01ebd 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -57,7 +57,7 @@ public class LinearLayout extends ViewGroup {
* Whether the children of this layout are baseline aligned. Only applicable
* if {@link #mOrientation} is horizontal.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
private boolean mBaselineAligned = true;
/**
@@ -67,7 +67,7 @@ public class LinearLayout extends ViewGroup {
* Note: this is orthogonal to {@link #mBaselineAligned}, which is concerned
* with whether the children of this layout are baseline aligned.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
private int mBaselineAlignedChildIndex = -1;
/**
@@ -75,12 +75,13 @@ public class LinearLayout extends ViewGroup {
* We'll calculate the baseline of this layout as we measure vertically; for
* horizontal linear layouts, the offset of 0 is appropriate.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "measurement")
private int mBaselineChildTop = 0;
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "measurement")
private int mOrientation;
- @ViewDebug.ExportedProperty(mapping = {
+
+ @ViewDebug.ExportedProperty(category = "measurement", mapping = {
@ViewDebug.IntToString(from = -1, to = "NONE"),
@ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"),
@ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"),
@@ -95,13 +96,14 @@ public class LinearLayout extends ViewGroup {
@ViewDebug.IntToString(from = Gravity.FILL, to = "FILL")
})
private int mGravity = Gravity.LEFT | Gravity.TOP;
- @ViewDebug.ExportedProperty
+
+ @ViewDebug.ExportedProperty(category = "measurement")
private int mTotalLength;
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
private float mWeightSum;
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
private boolean mUseLargestChild;
private int[] mMaxAscent;
@@ -1401,7 +1403,7 @@ public class LinearLayout extends ViewGroup {
* 0 if the view should not be stretched. Otherwise the extra pixels
* will be pro-rated among all views whose weight is greater than 0.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public float weight;
/**
@@ -1409,7 +1411,7 @@ public class LinearLayout extends ViewGroup {
*
* @see android.view.Gravity
*/
- @ViewDebug.ExportedProperty(mapping = {
+ @ViewDebug.ExportedProperty(category = "layout", mapping = {
@ViewDebug.IntToString(from = -1, to = "NONE"),
@ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"),
@ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"),
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index 5c34c2cd7b42..35e0603630cd 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -33,7 +33,7 @@ import android.view.View.OnTouchListener;
/**
* A ListPopupWindow anchors itself to a host view and displays a
- * list of choices. When one is selected, the popup is dismissed.
+ * list of choices.
*
* <p>ListPopupWindow contains a number of tricky behaviors surrounding
* positioning, scrolling parents to fit the dropdown, interacting
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 0bb41e584eed..0d0a1ba0eae6 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -1186,7 +1186,7 @@ public class ListView extends AbsListView {
* UNSPECIFIED/AT_MOST modes, false otherwise.
* @hide
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "list")
protected boolean recycleOnMeasure() {
return true;
}
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index d404ce70a9aa..b562942aaae4 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -122,7 +122,7 @@ public class PopupWindow {
private OnScrollChangedListener mOnScrollChangedListener =
new OnScrollChangedListener() {
public void onScrollChanged() {
- View anchor = mAnchor.get();
+ View anchor = mAnchor != null ? mAnchor.get() : null;
if (anchor != null && mPopupView != null) {
WindowManager.LayoutParams p = (WindowManager.LayoutParams)
mPopupView.getLayoutParams();
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 71f0c2f9320c..0f52fc81e7bb 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -336,7 +336,7 @@ public class ProgressBar extends View {
*
* @return true if the progress bar is in indeterminate mode
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "progress")
public synchronized boolean isIndeterminate() {
return mIndeterminate;
}
@@ -609,7 +609,7 @@ public class ProgressBar extends View {
* @see #setMax(int)
* @see #getMax()
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "progress")
public synchronized int getProgress() {
return mIndeterminate ? 0 : mProgress;
}
@@ -626,7 +626,7 @@ public class ProgressBar extends View {
* @see #setMax(int)
* @see #getMax()
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "progress")
public synchronized int getSecondaryProgress() {
return mIndeterminate ? 0 : mSecondaryProgress;
}
@@ -640,7 +640,7 @@ public class ProgressBar extends View {
* @see #getProgress()
* @see #getSecondaryProgress()
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "progress")
public synchronized int getMax() {
return mMax;
}
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 1aa1df30afa0..64cda49ade65 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -1011,7 +1011,7 @@ public class RelativeLayout extends ViewGroup {
* @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerVertical
*/
public static class LayoutParams extends ViewGroup.MarginLayoutParams {
- @ViewDebug.ExportedProperty(resolveId = true, indexMapping = {
+ @ViewDebug.ExportedProperty(category = "layout", resolveId = true, indexMapping = {
@ViewDebug.IntToString(from = ABOVE, to = "above"),
@ViewDebug.IntToString(from = ALIGN_BASELINE, to = "alignBaseline"),
@ViewDebug.IntToString(from = ALIGN_BOTTOM, to = "alignBottom"),
@@ -1040,7 +1040,7 @@ public class RelativeLayout extends ViewGroup {
* When true, uses the parent as the anchor if the anchor doesn't exist or if
* the anchor's visibility is GONE.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public boolean alignWithParent;
public LayoutParams(Context c, AttributeSet attrs) {
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 52635e85fa48..ebf5d6ee9977 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -433,6 +433,7 @@ public class RemoteViewsAdapter extends BaseAdapter {
int cacheIndex = getCacheIndex(position);
FrameLayout flipper = mViewCache[cacheIndex].flipper;
flipper.setVisibility(View.VISIBLE);
+ flipper.setAlpha(1.0f);
if (indexInfo == null) {
// hide the item view and show the loading view
diff --git a/core/java/android/widget/ResourceCursorAdapter.java b/core/java/android/widget/ResourceCursorAdapter.java
index c9c217a6f2df..aee411ee4076 100644
--- a/core/java/android/widget/ResourceCursorAdapter.java
+++ b/core/java/android/widget/ResourceCursorAdapter.java
@@ -36,9 +36,8 @@ public abstract class ResourceCursorAdapter extends CursorAdapter {
/**
* Constructor.
- *
- * @param context The context where the ListView associated with this
- * SimpleListItemFactory is running
+ *
+ * @param context The context where the ListView associated with this adapter is running
* @param layout resource identifier of a layout file that defines the views
* for this list item. Unless you override them later, this will
* define both the item views and the drop down views.
@@ -51,9 +50,8 @@ public abstract class ResourceCursorAdapter extends CursorAdapter {
/**
* Constructor.
- *
- * @param context The context where the ListView associated with this
- * SimpleListItemFactory is running
+ *
+ * @param context The context where the ListView associated with this adapter is running
* @param layout resource identifier of a layout file that defines the views
* for this list item. Unless you override them later, this will
* define both the item views and the drop down views.
@@ -69,6 +67,22 @@ public abstract class ResourceCursorAdapter extends CursorAdapter {
}
/**
+ * Constructor.
+ *
+ * @param context The context where the ListView associated with this adapter is running
+ * @param layout resource identifier of a layout file that defines the views
+ * for this list item. Unless you override them later, this will
+ * define both the item views and the drop down views.
+ * @param c The cursor from which to get the data.
+ * @param flags flags used to determine the behavior of the adapter
+ */
+ public ResourceCursorAdapter(Context context, int layout, Cursor c, int flags) {
+ super(context, c, flags);
+ mLayout = mDropDownLayout = layout;
+ mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ }
+
+ /**
* Inflates view(s) from the specified XML file.
*
* @see android.widget.CursorAdapter#newView(android.content.Context,
diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java
new file mode 100644
index 000000000000..4cd44d99a777
--- /dev/null
+++ b/core/java/android/widget/StackView.java
@@ -0,0 +1,487 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import java.util.WeakHashMap;
+
+import android.animation.PropertyAnimator;
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.widget.RemoteViews.RemoteView;
+
+@RemoteView
+/**
+ * A view that displays its children in a stack and allows users to discretely swipe
+ * through the children.
+ */
+public class StackView extends AdapterViewAnimator {
+ private final String TAG = "StackView";
+
+ /**
+ * Default animation parameters
+ */
+ private final int DEFAULT_ANIMATION_DURATION = 400;
+ private final int MINIMUM_ANIMATION_DURATION = 50;
+
+ /**
+ * These specify the different gesture states
+ */
+ private final int GESTURE_NONE = 0;
+ private final int GESTURE_SLIDE_UP = 1;
+ private final int GESTURE_SLIDE_DOWN = 2;
+
+ /**
+ * Specifies how far you need to swipe (up or down) before it
+ * will be consider a completed gesture when you lift your finger
+ */
+ private final float SWIPE_THRESHOLD_RATIO = 0.35f;
+ private final float SLIDE_UP_RATIO = 0.7f;
+
+ private final WeakHashMap<View, Float> mRotations = new WeakHashMap<View, Float>();
+ private final WeakHashMap<View, Integer>
+ mChildrenToApplyTransformsTo = new WeakHashMap<View, Integer>();
+
+ /**
+ * Sentinel value for no current active pointer.
+ * Used by {@link #mActivePointerId}.
+ */
+ private static final int INVALID_POINTER = -1;
+
+ /**
+ * These variables are all related to the current state of touch interaction
+ * with the stack
+ */
+ private boolean mGestureComplete = false;
+ private float mInitialY;
+ private float mInitialX;
+ private int mActivePointerId;
+ private int mYOffset = 0;
+ private int mYVelocity = 0;
+ private int mSwipeGestureType = GESTURE_NONE;
+ private int mViewHeight;
+ private int mSwipeThreshold;
+ private int mTouchSlop;
+ private int mMaximumVelocity;
+ private VelocityTracker mVelocityTracker;
+
+ private boolean mFirstLayoutHappened = false;
+
+ // TODO: temp hack to get this thing started
+ int mIndex = 5;
+
+ public StackView(Context context) {
+ super(context);
+ initStackView();
+ }
+
+ public StackView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initStackView();
+ }
+
+ private void initStackView() {
+ configureViewAnimator(4, 2);
+ setStaticTransformationsEnabled(true);
+ final ViewConfiguration configuration = ViewConfiguration.get(getContext());
+ mTouchSlop = configuration.getScaledTouchSlop();// + 5;
+ mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
+ mActivePointerId = INVALID_POINTER;
+ }
+
+ /**
+ * Animate the views between different relative indexes within the {@link AdapterViewAnimator}
+ */
+ void animateViewForTransition(int fromIndex, int toIndex, View view) {
+ if (fromIndex == -1 && toIndex == 0) {
+ // Fade item in
+ if (view.getAlpha() == 1) {
+ view.setAlpha(0);
+ }
+ PropertyAnimator fadeIn = new PropertyAnimator(DEFAULT_ANIMATION_DURATION,
+ view, "alpha", view.getAlpha(), 1.0f);
+ fadeIn.start();
+ } else if (fromIndex == mNumActiveViews - 1 && toIndex == mNumActiveViews - 2) {
+ // Slide item in
+ view.setVisibility(VISIBLE);
+ LayoutParams lp = (LayoutParams) view.getLayoutParams();
+
+ int largestDuration = (int) Math.round(
+ (lp.verticalOffset*1.0f/-mViewHeight)*DEFAULT_ANIMATION_DURATION);
+ int duration = largestDuration;
+ if (mYVelocity != 0) {
+ duration = 1000*(0 - lp.verticalOffset)/Math.abs(mYVelocity);
+ }
+
+ duration = Math.min(duration, largestDuration);
+ duration = Math.max(duration, MINIMUM_ANIMATION_DURATION);
+
+ PropertyAnimator slideDown = new PropertyAnimator(duration, lp,
+ "verticalOffset", lp.verticalOffset, 0);
+ slideDown.start();
+
+ PropertyAnimator fadeIn = new PropertyAnimator(duration, view,
+ "alpha", view.getAlpha(), 1.0f);
+ fadeIn.start();
+ } else if (fromIndex == mNumActiveViews - 2 && toIndex == mNumActiveViews - 1) {
+ // Slide item out
+ LayoutParams lp = (LayoutParams) view.getLayoutParams();
+
+ int largestDuration = (int) Math.round(
+ (1 - (lp.verticalOffset*1.0f/-mViewHeight))*DEFAULT_ANIMATION_DURATION);
+ int duration = largestDuration;
+ if (mYVelocity != 0) {
+ duration = 1000*(lp.verticalOffset + mViewHeight)/Math.abs(mYVelocity);
+ }
+
+ duration = Math.min(duration, largestDuration);
+ duration = Math.max(duration, MINIMUM_ANIMATION_DURATION);
+
+ PropertyAnimator slideUp = new PropertyAnimator(duration, lp,
+ "verticalOffset", lp.verticalOffset, -mViewHeight);
+ slideUp.start();
+
+ PropertyAnimator fadeOut = new PropertyAnimator(duration, view,
+ "alpha", view.getAlpha(), 0.0f);
+ fadeOut.start();
+ } else if (fromIndex == -1 && toIndex == mNumActiveViews - 1) {
+ // Make sure this view that is "waiting in the wings" is invisible
+ view.setAlpha(0.0f);
+ } else if (toIndex == -1) {
+ // Fade item out
+ PropertyAnimator fadeOut = new PropertyAnimator(DEFAULT_ANIMATION_DURATION,
+ view, "alpha", view.getAlpha(), 0);
+ fadeOut.start();
+ }
+ }
+
+ /**
+ * Apply any necessary tranforms for the child that is being added.
+ */
+ void applyTransformForChildAtIndex(View child, int relativeIndex) {
+ float rotation;
+
+ if (!mRotations.containsKey(child)) {
+ rotation = (float) (Math.random()*26 - 13);
+ mRotations.put(child, rotation);
+ } else {
+ rotation = mRotations.get(child);
+ }
+
+ // Child has been removed
+ if (relativeIndex == -1) {
+ if (mRotations.containsKey(child)) {
+ mRotations.remove(child);
+ }
+ if (mChildrenToApplyTransformsTo.containsKey(child)) {
+ mChildrenToApplyTransformsTo.remove(child);
+ }
+ }
+
+ // if this view is already in the layout, we need to
+ // wait until layout has finished in order to set the
+ // pivot point of the rotation (requiring getMeasuredWidth/Height())
+ mChildrenToApplyTransformsTo.put(child, relativeIndex);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+
+ if (!mChildrenToApplyTransformsTo.isEmpty()) {
+ for (View child: mChildrenToApplyTransformsTo.keySet()) {
+ if (mRotations.containsKey(child)) {
+ child.setPivotX(child.getMeasuredWidth()/2);
+ child.setPivotY(child.getMeasuredHeight()/2);
+ child.setRotation(mRotations.get(child));
+ }
+ }
+ mChildrenToApplyTransformsTo.clear();
+ }
+
+ if (!mFirstLayoutHappened) {
+ mViewHeight = (int) Math.round(SLIDE_UP_RATIO*getMeasuredHeight());
+ mSwipeThreshold = (int) Math.round(SWIPE_THRESHOLD_RATIO*mViewHeight);
+
+ // TODO: Right now this walks all the way up the view hierarchy and disables
+ // ClipChildren and ClipToPadding. We're probably going to want to reset
+ // these flags as well.
+ setClipChildren(false);
+ ViewGroup view = this;
+ while (view.getParent() != null && view.getParent() instanceof ViewGroup) {
+ view = (ViewGroup) view.getParent();
+ view.setClipChildren(false);
+ view.setClipToPadding(false);
+ }
+
+ mFirstLayoutHappened = true;
+ }
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ int action = ev.getAction();
+ switch(action & MotionEvent.ACTION_MASK) {
+
+ case MotionEvent.ACTION_DOWN: {
+ if (mActivePointerId == INVALID_POINTER) {
+ mInitialX = ev.getX();
+ mInitialY = ev.getY();
+ mActivePointerId = ev.getPointerId(0);
+ }
+ break;
+ }
+ case MotionEvent.ACTION_MOVE: {
+ int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (pointerIndex == INVALID_POINTER) {
+ // no data for our primary pointer, this shouldn't happen, log it
+ Log.d(TAG, "Error: No data for our primary pointer.");
+ return false;
+ }
+
+ float newY = ev.getY(pointerIndex);
+ float deltaY = newY - mInitialY;
+
+ if ((int) Math.abs(deltaY) > mTouchSlop && mSwipeGestureType == GESTURE_NONE) {
+ mSwipeGestureType = deltaY < 0 ? GESTURE_SLIDE_UP : GESTURE_SLIDE_DOWN;
+ mGestureComplete = false;
+ cancelLongPress();
+ requestDisallowInterceptTouchEvent(true);
+ }
+ break;
+ }
+ case MotionEvent.ACTION_POINTER_UP: {
+ onSecondaryPointerUp(ev);
+ break;
+ }
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL: {
+ mActivePointerId = INVALID_POINTER;
+ mSwipeGestureType = GESTURE_NONE;
+ mGestureComplete = true;
+ }
+ }
+
+ return mSwipeGestureType != GESTURE_NONE;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ int action = ev.getAction();
+ int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (pointerIndex == INVALID_POINTER) {
+ // no data for our primary pointer, this shouldn't happen, log it
+ Log.d(TAG, "Error: No data for our primary pointer.");
+ return false;
+ }
+
+ float newY = ev.getY(pointerIndex);
+ float deltaY = newY - mInitialY;
+
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ mVelocityTracker.addMovement(ev);
+
+ switch (action & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_MOVE: {
+ if ((int) Math.abs(deltaY) > mTouchSlop && mSwipeGestureType == GESTURE_NONE) {
+ mSwipeGestureType = deltaY < 0 ? GESTURE_SLIDE_UP : GESTURE_SLIDE_DOWN;
+ mGestureComplete = false;
+ cancelLongPress();
+ requestDisallowInterceptTouchEvent(true);
+ }
+
+ if (!mGestureComplete) {
+ if (mSwipeGestureType == GESTURE_SLIDE_DOWN) {
+ View v = getViewAtRelativeIndex(mNumActiveViews - 1);
+ if (v != null) {
+ // This view is present but hidden, make sure it's visible
+ // if they pull down
+ v.setVisibility(VISIBLE);
+
+ float r = (deltaY-mTouchSlop)*1.0f / (mSwipeThreshold);
+ mYOffset = Math.min(-mViewHeight + (int) Math.round(
+ r*mSwipeThreshold) - mTouchSlop, 0);
+ LayoutParams lp = (LayoutParams) v.getLayoutParams();
+ lp.setVerticalOffset(mYOffset);
+
+ float alpha = Math.max(0.0f, 1.0f - (1.0f*mYOffset/-mViewHeight));
+ alpha = Math.min(1.0f, alpha);
+ v.setAlpha(alpha);
+ }
+ return true;
+ } else if (mSwipeGestureType == GESTURE_SLIDE_UP) {
+ View v = getViewAtRelativeIndex(mNumActiveViews - 2);
+
+ if (v != null) {
+ float r = -(deltaY*1.0f + mTouchSlop) / (mSwipeThreshold);
+ mYOffset = Math.min((int) Math.round(r*-mSwipeThreshold), 0);
+ LayoutParams lp = (LayoutParams) v.getLayoutParams();
+ lp.setVerticalOffset(mYOffset);
+
+ float alpha = Math.max(0.0f, 1.0f - (1.0f*mYOffset/-mViewHeight));
+ alpha = Math.min(1.0f, alpha);
+ v.setAlpha(alpha);
+ }
+ return true;
+ }
+ }
+ break;
+ }
+ case MotionEvent.ACTION_UP: {
+ handlePointerUp(ev);
+ break;
+ }
+ case MotionEvent.ACTION_POINTER_UP: {
+ onSecondaryPointerUp(ev);
+ break;
+ }
+ case MotionEvent.ACTION_CANCEL: {
+ mActivePointerId = INVALID_POINTER;
+ mGestureComplete = true;
+ mSwipeGestureType = GESTURE_NONE;
+ mYOffset = 0;
+ break;
+ }
+ }
+ return true;
+ }
+
+ private final Rect touchRect = new Rect();
+ private void onSecondaryPointerUp(MotionEvent ev) {
+ final int activePointerIndex = ev.getActionIndex();
+ final int pointerId = ev.getPointerId(activePointerIndex);
+ if (pointerId == mActivePointerId) {
+
+ int activeViewIndex = (mSwipeGestureType == GESTURE_SLIDE_DOWN) ? mNumActiveViews - 1
+ : mNumActiveViews - 2;
+
+ View v = getViewAtRelativeIndex(activeViewIndex);
+ if (v == null) return;
+
+ // Our primary pointer has gone up -- let's see if we can find
+ // another pointer on the view. If so, then we should replace
+ // our primary pointer with this new pointer and adjust things
+ // so that the view doesn't jump
+ for (int index = 0; index < ev.getPointerCount(); index++) {
+ if (index != activePointerIndex) {
+
+ float x = ev.getX(index);
+ float y = ev.getY(index);
+
+ touchRect.set(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
+ if (touchRect.contains((int) Math.round(x), (int) Math.round(y))) {
+ float oldX = ev.getX(activePointerIndex);
+ float oldY = ev.getY(activePointerIndex);
+
+ // adjust our frame of reference to avoid a jump
+ mInitialY += (y - oldY);
+ mInitialX += (x - oldX);
+
+ mActivePointerId = ev.getPointerId(index);
+ if (mVelocityTracker != null) {
+ mVelocityTracker.clear();
+ }
+ // ok, we're good, we found a new pointer which is touching the active view
+ return;
+ }
+ }
+ }
+ // if we made it this far, it means we didn't find a satisfactory new pointer :(,
+ // so end the
+ handlePointerUp(ev);
+ }
+ }
+
+ private void handlePointerUp(MotionEvent ev) {
+ int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ float newY = ev.getY(pointerIndex);
+ int deltaY = (int) (newY - mInitialY);
+
+ mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+ mYVelocity = (int) mVelocityTracker.getYVelocity(mActivePointerId);
+
+ if (mVelocityTracker != null) {
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+
+ if (deltaY > mSwipeThreshold && mSwipeGestureType == GESTURE_SLIDE_DOWN &&
+ !mGestureComplete) {
+ // Swipe threshold exceeded, swipe down
+ showNext();
+ } else if (deltaY < -mSwipeThreshold && mSwipeGestureType == GESTURE_SLIDE_UP &&
+ !mGestureComplete) {
+ // Swipe threshold exceeded, swipe up
+ showPrevious();
+ } else if (mSwipeGestureType == GESTURE_SLIDE_UP && !mGestureComplete) {
+ // Didn't swipe up far enough, snap back down
+ View v = getViewAtRelativeIndex(mNumActiveViews - 2);
+ if (v != null) {
+ // Compute the animation duration based on how far they pulled it up
+ LayoutParams lp = (LayoutParams) v.getLayoutParams();
+ int duration = (int) Math.round(
+ lp.verticalOffset*1.0f/-mViewHeight*DEFAULT_ANIMATION_DURATION);
+ duration = Math.max(MINIMUM_ANIMATION_DURATION, duration);
+
+ // Animate back down
+ PropertyAnimator slideDown = new PropertyAnimator(duration, lp,
+ "verticalOffset", lp.verticalOffset, 0);
+ slideDown.start();
+ PropertyAnimator fadeIn = new PropertyAnimator(duration, v,
+ "alpha",v.getAlpha(), 1.0f);
+ fadeIn.start();
+ }
+ } else if (mSwipeGestureType == GESTURE_SLIDE_DOWN && !mGestureComplete) {
+ // Didn't swipe down far enough, snap back up
+ View v = getViewAtRelativeIndex(mNumActiveViews - 1);
+ if (v != null) {
+ // Compute the animation duration based on how far they pulled it down
+ LayoutParams lp = (LayoutParams) v.getLayoutParams();
+ int duration = (int) Math.round(
+ (1 - lp.verticalOffset*1.0f/-mViewHeight)*DEFAULT_ANIMATION_DURATION);
+ duration = Math.max(MINIMUM_ANIMATION_DURATION, duration);
+
+ // Animate back up
+ PropertyAnimator slideUp = new PropertyAnimator(duration, lp,
+ "verticalOffset", lp.verticalOffset, -mViewHeight);
+ slideUp.start();
+ PropertyAnimator fadeOut = new PropertyAnimator(duration, v,
+ "alpha",v.getAlpha(), 0.0f);
+ fadeOut.start();
+ }
+ }
+
+ mActivePointerId = INVALID_POINTER;
+ mGestureComplete = true;
+ mSwipeGestureType = GESTURE_NONE;
+ mYOffset = 0;
+ }
+
+ @Override
+ public void onRemoteAdapterConnected() {
+ super.onRemoteAdapterConnected();
+ setDisplayedChild(mIndex);
+ }
+}
diff --git a/core/java/android/widget/TableRow.java b/core/java/android/widget/TableRow.java
index 48d12df8eb16..b612004c5b77 100644
--- a/core/java/android/widget/TableRow.java
+++ b/core/java/android/widget/TableRow.java
@@ -387,13 +387,13 @@ public class TableRow extends LinearLayout {
/**
* <p>The column index of the cell represented by the widget.</p>
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public int column;
/**
* <p>The number of columns the widgets spans over.</p>
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "layout")
public int span;
private static final int LOCATION = 0;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 24f14dc8bdc7..b413aee8bbd7 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -21,7 +21,9 @@ import com.android.internal.widget.EditableInputConnection;
import org.xmlpull.v1.XmlPullParserException;
+import android.content.ClippedData;
import android.content.Context;
+import android.content.ClipboardManager;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -34,6 +36,7 @@ import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
+import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -42,7 +45,6 @@ import android.os.Parcelable;
import android.os.ResultReceiver;
import android.os.SystemClock;
import android.text.BoringLayout;
-import android.text.ClipboardManager;
import android.text.DynamicLayout;
import android.text.Editable;
import android.text.GetChars;
@@ -5816,7 +5818,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
/**
* Convenience for {@link Selection#getSelectionStart}.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "text")
public int getSelectionStart() {
return Selection.getSelectionStart(getText());
}
@@ -5824,7 +5826,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
/**
* Convenience for {@link Selection#getSelectionEnd}.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "text")
public int getSelectionEnd() {
return Selection.getSelectionEnd(getText());
}
@@ -7061,7 +7063,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
getSelectionStart() >= 0 &&
getSelectionEnd() >= 0 &&
((ClipboardManager)getContext().getSystemService(Context.CLIPBOARD_SERVICE)).
- hasText());
+ hasPrimaryClip());
}
/**
@@ -7270,7 +7272,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
int min = Math.max(0, Math.min(selStart, selEnd));
int max = Math.max(0, Math.max(selStart, selEnd));
- ClipboardManager clip = (ClipboardManager)getContext()
+ ClipboardManager clipboard = (ClipboardManager)getContext()
.getSystemService(Context.CLIPBOARD_SERVICE);
switch (id) {
@@ -7278,8 +7280,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
MetaKeyKeyListener.stopSelecting(this, (Spannable) mText);
URLSpan[] urls = ((Spanned) mText).getSpans(min, max, URLSpan.class);
- if (urls.length == 1) {
- clip.setText(urls[0].getURL());
+ if (urls.length >= 1) {
+ ClippedData clip = null;
+ for (int i=0; i<urls.length; i++) {
+ Uri uri = Uri.parse(urls[0].getURL());
+ ClippedData.Item item = new ClippedData.Item(uri);
+ if (clip == null) {
+ clip = new ClippedData(null, null, item);
+ } else {
+ clip.addItem(item);
+ }
+ }
+ clipboard.setPrimaryClip(clip);
}
return true;
@@ -7490,8 +7502,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return true;
}
- ClipboardManager clip = (ClipboardManager) getContext().
- getSystemService(Context.CLIPBOARD_SERVICE);
+ ClipboardManager clipboard = (ClipboardManager) getContext().
+ getSystemService(Context.CLIPBOARD_SERVICE);
int min = 0;
int max = mText.length();
@@ -7506,23 +7518,36 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
switch (item.getItemId()) {
case ID_PASTE:
- CharSequence paste = clip.getText();
-
- if (paste != null) {
- Selection.setSelection((Spannable) mText, max);
- ((Editable) mText).replace(min, max, paste);
+ ClippedData clip = clipboard.getPrimaryClip();
+ if (clip != null) {
+ boolean didfirst = false;
+ for (int i=0; i<clip.getItemCount(); i++) {
+ CharSequence paste = clip.getItem(i).coerceToText(getContext());
+ if (paste != null) {
+ if (!didfirst) {
+ Selection.setSelection((Spannable) mText, max);
+ ((Editable) mText).replace(min, max, paste);
+ } else {
+ ((Editable) mText).insert(getSelectionEnd(), "\n");
+ ((Editable) mText).insert(getSelectionEnd(), paste);
+ }
+ }
+ }
finishSelectionActionMode();
}
+
return true;
case ID_CUT:
- clip.setText(mTransformed.subSequence(min, max));
+ clipboard.setPrimaryClip(new ClippedData(null, null,
+ new ClippedData.Item(mTransformed.subSequence(min, max))));
((Editable) mText).delete(min, max);
finishSelectionActionMode();
return true;
case ID_COPY:
- clip.setText(mTransformed.subSequence(min, max));
+ clipboard.setPrimaryClip(new ClippedData(null, null,
+ new ClippedData.Item(mTransformed.subSequence(min, max))));
finishSelectionActionMode();
return true;
}
@@ -8037,7 +8062,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "text")
private CharSequence mText;
private CharSequence mTransformed;
private BufferType mBufferType = BufferType.NORMAL;
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index 281f32c83224..dd2ad6c764dd 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -24,8 +24,10 @@ import com.android.internal.widget.ActionBarView;
import android.app.ActionBar;
import android.app.Activity;
+import android.app.Dialog;
import android.app.Fragment;
import android.app.FragmentTransaction;
+import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.view.ActionMode;
@@ -54,7 +56,9 @@ public class ActionBarImpl extends ActionBar {
private static final int TAB_SWITCH_SHOW_HIDE = 0;
private static final int TAB_SWITCH_ADD_REMOVE = 1;
+ private Context mContext;
private Activity mActivity;
+ private Dialog mDialog;
private ViewAnimator mAnimatorView;
private ActionBarView mActionView;
@@ -88,8 +92,17 @@ public class ActionBarImpl extends ActionBar {
};
public ActionBarImpl(Activity activity) {
- final View decor = activity.getWindow().getDecorView();
mActivity = activity;
+ init(activity.getWindow().getDecorView());
+ }
+
+ public ActionBarImpl(Dialog dialog) {
+ mDialog = dialog;
+ init(dialog.getWindow().getDecorView());
+ }
+
+ private void init(View decor) {
+ mContext = decor.getContext();
mActionView = (ActionBarView) decor.findViewById(com.android.internal.R.id.action_bar);
mUpperContextView = (ActionBarContextView) decor.findViewById(
com.android.internal.R.id.action_context_bar);
@@ -109,23 +122,23 @@ public class ActionBarImpl extends ActionBar {
@Override
public void setStandardNavigationMode(int titleResId, int subtitleResId) {
- setStandardNavigationMode(mActivity.getString(titleResId),
- mActivity.getString(subtitleResId));
+ setStandardNavigationMode(mContext.getString(titleResId),
+ mContext.getString(subtitleResId));
}
@Override
public void setStandardNavigationMode(int titleResId) {
- setStandardNavigationMode(mActivity.getString(titleResId));
+ setStandardNavigationMode(mContext.getString(titleResId));
}
@Override
public void setTitle(int resId) {
- setTitle(mActivity.getString(resId));
+ setTitle(mContext.getString(resId));
}
@Override
public void setSubtitle(int resId) {
- setSubtitle(mActivity.getString(resId));
+ setSubtitle(mContext.getString(resId));
}
public void setCustomNavigationMode(View view) {
@@ -345,6 +358,10 @@ public class ActionBarImpl extends ActionBar {
@Override
public void setTabNavigationMode() {
+ if (mActivity == null) {
+ throw new IllegalStateException(
+ "Tab navigation mode cannot be used outside of an Activity");
+ }
mActionView.setNavigationMode(NAVIGATION_MODE_TABS);
}
@@ -380,6 +397,27 @@ public class ActionBarImpl extends ActionBar {
trans.commit();
}
+ @Override
+ public int getHeight() {
+ return mActionView.getHeight();
+ }
+
+ @Override
+ public void show() {
+ // TODO animate!
+ mAnimatorView.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void hide() {
+ // TODO animate!
+ mAnimatorView.setVisibility(View.GONE);
+ }
+
+ public boolean isShowing() {
+ return mAnimatorView.getVisibility() == View.VISIBLE;
+ }
+
/**
* @hide
*/
@@ -396,7 +434,7 @@ public class ActionBarImpl extends ActionBar {
@Override
public MenuInflater getMenuInflater() {
- return new MenuInflater(mActivity);
+ return new MenuInflater(mContext);
}
@Override
@@ -485,7 +523,7 @@ public class ActionBarImpl extends ActionBar {
return true;
}
- new MenuPopupHelper(mActivity, subMenu).show();
+ new MenuPopupHelper(mContext, subMenu).show();
return true;
}
@@ -493,6 +531,8 @@ public class ActionBarImpl extends ActionBar {
}
public void onMenuModeChange(MenuBuilder menu) {
+ invalidate();
+ mUpperContextView.showOverflowMenu();
}
}
diff --git a/core/java/com/android/internal/app/IMediaContainerService.aidl b/core/java/com/android/internal/app/IMediaContainerService.aidl
index 89649a98efa8..5d1f632797a7 100755
--- a/core/java/com/android/internal/app/IMediaContainerService.aidl
+++ b/core/java/com/android/internal/app/IMediaContainerService.aidl
@@ -19,6 +19,7 @@ package com.android.internal.app;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.content.pm.PackageInfoLite;
+import android.content.res.ObbInfo;
interface IMediaContainerService {
String copyResourceToContainer(in Uri packageURI,
@@ -28,4 +29,5 @@ interface IMediaContainerService {
in ParcelFileDescriptor outStream);
PackageInfoLite getMinimalPackageInfo(in Uri fileUri, int flags);
boolean checkFreeStorage(boolean external, in Uri fileUri);
+ ObbInfo getObbInfo(String filename);
}
diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java
new file mode 100644
index 000000000000..ce5959d1ea91
--- /dev/null
+++ b/core/java/com/android/internal/app/PlatLogoActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.ImageView;
+
+public class PlatLogoActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ ImageView content = new ImageView(this);
+ content.setImageResource(com.android.internal.R.drawable.platlogo);
+ content.setScaleType(ImageView.ScaleType.FIT_CENTER);
+
+ setContentView(content);
+ }
+}
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index 59600dcdc455..5767832b58d0 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -24,6 +24,7 @@ import android.os.IBinder;
import android.os.Process;
import android.os.SystemProperties;
import android.util.Config;
+import android.util.Finalizers;
import android.util.Log;
import android.util.Slog;
@@ -141,6 +142,12 @@ public class RuntimeInit {
Debug.enableEmulatorTraceOutput();
}
+ /**
+ * Initialize the thread used to reclaim resources without
+ * going through finalizers.
+ */
+ Finalizers.init();
+
initialized = true;
}
@@ -331,9 +338,6 @@ public class RuntimeInit {
}
}
- /** Counter used to prevent reentrancy in {@link #reportException}. */
- private static final AtomicInteger sInReportException = new AtomicInteger();
-
/**
* Set the object identifying this application/process, for reporting VM
* errors.
diff --git a/core/java/com/android/internal/view/StandaloneActionMode.java b/core/java/com/android/internal/view/StandaloneActionMode.java
index e6d6ba04d807..ab80c588f8fd 100644
--- a/core/java/com/android/internal/view/StandaloneActionMode.java
+++ b/core/java/com/android/internal/view/StandaloneActionMode.java
@@ -135,5 +135,7 @@ public class StandaloneActionMode extends ActionMode implements MenuBuilder.Call
}
public void onMenuModeChange(MenuBuilder menu) {
+ invalidate();
+ mContextView.showOverflowMenu();
}
}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java
index e2815368ecba..cb5f17980d99 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuView.java
@@ -20,10 +20,12 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.util.AttributeSet;
+import android.view.Gravity;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.LinearLayout;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
/**
@@ -39,6 +41,7 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo
private int mMaxItems;
private boolean mReserveOverflow;
private OverflowMenuButton mOverflowButton;
+ private WeakReference<MenuPopupHelper> mOverflowPopup;
public ActionMenuView(Context context) {
this(context, null);
@@ -80,6 +83,7 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo
if (p instanceof LayoutParams) {
LayoutParams lp = (LayoutParams) p;
return lp.leftMargin == mItemMargin && lp.rightMargin == mItemMargin &&
+ lp.gravity == Gravity.CENTER_VERTICAL &&
lp.width == LayoutParams.WRAP_CONTENT && lp.height == LayoutParams.WRAP_CONTENT;
}
return false;
@@ -91,6 +95,7 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo
LayoutParams.WRAP_CONTENT);
params.leftMargin = mItemMargin;
params.rightMargin = mItemMargin;
+ params.gravity = Gravity.CENTER_VERTICAL;
return params;
}
@@ -143,8 +148,32 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo
public boolean showOverflowMenu() {
if (mOverflowButton != null) {
- MenuPopupHelper popup = new MenuPopupHelper(getContext(), mMenu, mOverflowButton, true);
- popup.show();
+ final MenuPopupHelper popup =
+ new MenuPopupHelper(getContext(), mMenu, mOverflowButton, true);
+ // Post this for later; we might still need a layout for the anchor to be right.
+ post(new Runnable() {
+ public void run() {
+ popup.show();
+ }
+ });
+ mOverflowPopup = new WeakReference<MenuPopupHelper>(popup);
+ return true;
+ }
+ return false;
+ }
+
+ public boolean isOverflowMenuShowing() {
+ MenuPopupHelper popup = mOverflowPopup != null ? mOverflowPopup.get() : null;
+ if (popup != null) {
+ return popup.isShowing();
+ }
+ return false;
+ }
+
+ public boolean hideOverflowMenu() {
+ MenuPopupHelper popup = mOverflowPopup != null ? mOverflowPopup.get() : null;
+ if (popup != null) {
+ popup.dismiss();
return true;
}
return false;
@@ -162,7 +191,7 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo
final Resources res = context.getResources();
setClickable(true);
setFocusable(true);
- // TODO setTitle() to a localized string for accessibility
+ setContentDescription(res.getString(com.android.internal.R.string.more_item_label));
setImageDrawable(res.getDrawable(com.android.internal.R.drawable.ic_menu_more));
setVisibility(VISIBLE);
setEnabled(true);
@@ -174,7 +203,8 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo
return true;
}
- showOverflowMenu();
+ // Change to overflow mode
+ mMenu.getCallback().onMenuModeChange(mMenu);
return true;
}
}
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index ce0ec045dc0b..f52c93cf3537 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -92,7 +92,9 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
}
public void dismiss() {
- mPopup.dismiss();
+ if (isShowing()) {
+ mPopup.dismiss();
+ }
mPopup = null;
}
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index 5518b3edc317..6a476d00eb09 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -163,6 +163,27 @@ public class ActionBarContextView extends ViewGroup {
mMenuView = null;
}
+ public boolean showOverflowMenu() {
+ if (mMenuView != null) {
+ return mMenuView.showOverflowMenu();
+ }
+ return false;
+ }
+
+ public boolean hideOverflowMenu() {
+ if (mMenuView != null) {
+ return mMenuView.hideOverflowMenu();
+ }
+ return false;
+ }
+
+ public boolean isOverflowMenuShowing() {
+ if (mMenuView != null) {
+ return mMenuView.isOverflowMenuShowing();
+ }
+ return false;
+ }
+
@Override
protected LayoutParams generateDefaultLayoutParams() {
// Used by custom views if they don't supply layout params. Everything else
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index d703a2f7f730..73d3c9574a3c 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -118,7 +118,6 @@ public class ActionBarView extends ViewGroup {
super(context, attrs);
final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
- mContentHeight = (int) (CONTENT_HEIGHT_DIP * metrics.density + 0.5f);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ActionBar);
@@ -157,14 +156,18 @@ public class ActionBarView extends ViewGroup {
LayoutInflater inflater = LayoutInflater.from(context);
mCustomNavView = (View) inflater.inflate(customNavId, null);
mNavigationMode = ActionBar.NAVIGATION_MODE_CUSTOM;
+ addView(mCustomNavView);
}
+ final int padding = a.getDimensionPixelSize(R.styleable.ActionBar_padding,
+ (int) (CONTENT_PADDING_DIP * metrics.density + 0.5f));
+ setPadding(padding, padding, padding, padding);
+ mContentHeight = a.getDimensionPixelSize(R.styleable.ActionBar_height,
+ (int) (CONTENT_PADDING_DIP * metrics.density + 0.5f)) - padding * 2;
+
a.recycle();
// TODO: Set this in the theme
- int padding = (int) (CONTENT_PADDING_DIP * metrics.density + 0.5f);
- setPadding(padding, padding, padding, padding);
-
mSpacing = (int) (CONTENT_SPACING_DIP * metrics.density + 0.5f);
mActionSpacing = (int) (CONTENT_ACTION_SPACING_DIP * metrics.density + 0.5f);
@@ -209,6 +212,20 @@ public class ActionBarView extends ViewGroup {
return false;
}
+ public boolean hideOverflowMenu() {
+ if (mMenuView != null) {
+ return mMenuView.hideOverflowMenu();
+ }
+ return false;
+ }
+
+ public boolean isOverflowMenuShowing() {
+ if (mMenuView != null) {
+ return mMenuView.isOverflowMenuShowing();
+ }
+ return false;
+ }
+
public boolean isOverflowReserved() {
return mMenuView != null && mMenuView.isOverflowReserved();
}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 77c77f98eefa..860b5b743b7f 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -19,6 +19,10 @@ ifneq ($(USE_CUSTOM_RUNTIME_HEAP_MAX),)
LOCAL_CFLAGS += -DCUSTOM_RUNTIME_HEAP_MAX=$(USE_CUSTOM_RUNTIME_HEAP_MAX)
endif
+ifeq ($(USE_OPENGL_RENDERER),true)
+ LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER
+endif
+
LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
LOCAL_SRC_FILES:= \
@@ -47,7 +51,6 @@ LOCAL_SRC_FILES:= \
android_view_InputChannel.cpp \
android_view_InputQueue.cpp \
android_view_KeyEvent.cpp \
- android_view_HardwareRenderer.cpp \
android_view_GLES20Canvas.cpp \
android_view_MotionEvent.cpp \
android_text_AndroidCharacter.cpp \
@@ -136,7 +139,8 @@ LOCAL_SRC_FILES:= \
android_backup_BackupDataOutput.cpp \
android_backup_FileBackupHelperBase.cpp \
android_backup_BackupHelperDispatcher.cpp \
- android_content_res_ObbScanner.cpp
+ android_content_res_ObbScanner.cpp \
+ android_content_res_Configuration.cpp
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
@@ -170,7 +174,6 @@ LOCAL_SHARED_LIBRARIES := \
libbinder \
libnetutils \
libui \
- libhwui \
libgui \
libsurfaceflinger_client \
libcamera_client \
@@ -193,6 +196,10 @@ LOCAL_SHARED_LIBRARIES := \
libwpa_client \
libjpeg
+ifeq ($(USE_OPENGL_RENDERER),true)
+ LOCAL_SHARED_LIBRARIES += libhwui
+endif
+
ifeq ($(BOARD_HAVE_BLUETOOTH),true)
LOCAL_C_INCLUDES += \
external/dbus \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 1e6d219bcb10..2dd17bbf5954 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -115,7 +115,6 @@ extern int register_android_graphics_PixelFormat(JNIEnv* env);
extern int register_com_android_internal_graphics_NativeUtils(JNIEnv *env);
extern int register_android_view_Display(JNIEnv* env);
extern int register_android_view_GLES20Canvas(JNIEnv* env);
-extern int register_android_view_HardwareRenderer(JNIEnv* env);
extern int register_android_view_Surface(JNIEnv* env);
extern int register_android_view_ViewRoot(JNIEnv* env);
extern int register_android_database_CursorWindow(JNIEnv* env);
@@ -168,6 +167,7 @@ extern int register_android_view_InputQueue(JNIEnv* env);
extern int register_android_view_KeyEvent(JNIEnv* env);
extern int register_android_view_MotionEvent(JNIEnv* env);
extern int register_android_content_res_ObbScanner(JNIEnv* env);
+extern int register_android_content_res_Configuration(JNIEnv* env);
static AndroidRuntime* gCurRuntime = NULL;
@@ -1244,7 +1244,6 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_graphics_PixelFormat),
REG_JNI(register_android_graphics_Graphics),
REG_JNI(register_android_view_GLES20Canvas),
- REG_JNI(register_android_view_HardwareRenderer),
REG_JNI(register_android_view_Surface),
REG_JNI(register_android_view_ViewRoot),
REG_JNI(register_com_google_android_gles_jni_EGLImpl),
@@ -1333,6 +1332,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_view_MotionEvent),
REG_JNI(register_android_content_res_ObbScanner),
+ REG_JNI(register_android_content_res_Configuration),
};
/*
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index 558f5ffa478e..bf150a90ad6e 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -20,7 +20,6 @@
#include "SkCanvas.h"
#include "SkDevice.h"
-#include "SkGLCanvas.h"
#include "SkGraphics.h"
#include "SkImageRef_GlobalPool.h"
#include "SkPorterDuff.h"
@@ -67,13 +66,8 @@ public:
return bitmap ? new SkCanvas(*bitmap) : new SkCanvas;
}
- static SkCanvas* initGL(JNIEnv* env, jobject) {
- return new SkGLCanvas;
- }
-
static void freeCaches(JNIEnv* env, jobject) {
// these are called in no particular order
- SkGLCanvas::DeleteAllTextures();
SkImageRef_GlobalPool::SetRAMUsed(0);
SkGraphics::SetFontCacheUsed(0);
}
@@ -110,11 +104,6 @@ public:
return canvas->getDevice()->accessBitmap(false).height();
}
- static void setViewport(JNIEnv* env, jobject, SkCanvas* canvas,
- int width, int height) {
- canvas->setViewport(width, height);
- }
-
static void setBitmap(JNIEnv* env, jobject, SkCanvas* canvas,
SkBitmap* bitmap) {
canvas->setBitmapDevice(*bitmap);
@@ -880,12 +869,10 @@ public:
static JNINativeMethod gCanvasMethods[] = {
{"finalizer", "(I)V", (void*) SkCanvasGlue::finalizer},
{"initRaster","(I)I", (void*) SkCanvasGlue::initRaster},
- {"initGL","()I", (void*) SkCanvasGlue::initGL},
{"isOpaque","()Z", (void*) SkCanvasGlue::isOpaque},
{"getWidth","()I", (void*) SkCanvasGlue::getWidth},
{"getHeight","()I", (void*) SkCanvasGlue::getHeight},
{"native_setBitmap","(II)V", (void*) SkCanvasGlue::setBitmap},
- {"nativeSetViewport", "(III)V", (void*) SkCanvasGlue::setViewport},
{"save","()I", (void*) SkCanvasGlue::saveAll},
{"save","(I)I", (void*) SkCanvasGlue::save},
{"native_saveLayer","(ILandroid/graphics/RectF;II)I",
diff --git a/core/jni/android/graphics/ColorFilter.cpp b/core/jni/android/graphics/ColorFilter.cpp
index 848234f8129a..f3be8b0b9522 100644
--- a/core/jni/android/graphics/ColorFilter.cpp
+++ b/core/jni/android/graphics/ColorFilter.cpp
@@ -36,40 +36,25 @@ public:
obj->safeUnref();
}
- static SkColorFilter* CreatePorterDuffFilter(JNIEnv* env, jobject, jint srcColor,
- SkPorterDuff::Mode mode) {
- return SkColorFilter::CreateModeFilter(srcColor, SkPorterDuff::ToXfermodeMode(mode));
- }
-
static SkiaColorFilter* glCreatePorterDuffFilter(JNIEnv* env, jobject, jint srcColor,
SkPorterDuff::Mode mode) {
+#ifdef USE_OPENGL_RENDERER
return new SkiaBlendFilter(srcColor, SkPorterDuff::ToXfermodeMode(mode));
+#else
+ return NULL;
+#endif
}
- static SkColorFilter* CreateLightingFilter(JNIEnv* env, jobject, jint mul, jint add) {
- return SkColorFilter::CreateLightingFilter(mul, add);
- }
-
static SkiaColorFilter* glCreateLightingFilter(JNIEnv* env, jobject, jint mul, jint add) {
+#ifdef USE_OPENGL_RENDERER
return new SkiaLightingFilter(mul, add);
- }
-
- static SkColorFilter* CreateColorMatrixFilter(JNIEnv* env, jobject, jfloatArray jarray) {
- AutoJavaFloatArray autoArray(env, jarray, 20);
- const float* src = autoArray.ptr();
-
-#ifdef SK_SCALAR_IS_FIXED
- SkFixed array[20];
- for (int i = 0; i < 20; i++) {
- array[i] = SkFloatToScalar(src[i]);
- }
- return new SkColorMatrixFilter(array);
#else
- return new SkColorMatrixFilter(src);
+ return NULL;
#endif
}
static SkiaColorFilter* glCreateColorMatrixFilter(JNIEnv* env, jobject, jfloatArray jarray) {
+#ifdef USE_OPENGL_RENDERER
AutoJavaFloatArray autoArray(env, jarray, 20);
const float* src = autoArray.ptr();
@@ -86,6 +71,33 @@ public:
colorVector[3] = src[19];
return new SkiaColorMatrixFilter(colorMatrix, colorVector);
+#else
+ return NULL;
+#endif
+ }
+
+ static SkColorFilter* CreatePorterDuffFilter(JNIEnv* env, jobject, jint srcColor,
+ SkPorterDuff::Mode mode) {
+ return SkColorFilter::CreateModeFilter(srcColor, SkPorterDuff::ToXfermodeMode(mode));
+ }
+
+ static SkColorFilter* CreateLightingFilter(JNIEnv* env, jobject, jint mul, jint add) {
+ return SkColorFilter::CreateLightingFilter(mul, add);
+ }
+
+ static SkColorFilter* CreateColorMatrixFilter(JNIEnv* env, jobject, jfloatArray jarray) {
+ AutoJavaFloatArray autoArray(env, jarray, 20);
+ const float* src = autoArray.ptr();
+
+#ifdef SK_SCALAR_IS_FIXED
+ SkFixed array[20];
+ for (int i = 0; i < 20; i++) {
+ array[i] = SkFloatToScalar(src[i]);
+ }
+ return new SkColorMatrixFilter(array);
+#else
+ return new SkColorMatrixFilter(src);
+#endif
}
};
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index 34b4ab50d124..cb1c333762d9 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -71,7 +71,9 @@ static void Shader_setLocalMatrix(JNIEnv* env, jobject o, SkShader* shader, Skia
else {
shader->setLocalMatrix(*matrix);
}
+#ifdef USE_OPENGL_RENDERER
skiaShader->setMatrix(const_cast<SkMatrix*>(matrix));
+#endif
}
}
@@ -90,10 +92,14 @@ static SkShader* BitmapShader_constructor(JNIEnv* env, jobject o, const SkBitmap
static SkiaShader* BitmapShader_postConstructor(JNIEnv* env, jobject o, SkShader* shader,
SkBitmap* bitmap, int tileModeX, int tileModeY) {
+#ifdef USE_OPENGL_RENDERER
SkiaShader* skiaShader = new SkiaBitmapShader(bitmap, shader,
static_cast<SkShader::TileMode>(tileModeX), static_cast<SkShader::TileMode>(tileModeY),
NULL, (shader->getFlags() & SkShader::kOpaqueAlpha_Flag) == 0);
return skiaShader;
+#else
+ return NULL;
+#endif
}
///////////////////////////////////////////////////////////////////////////////////////////////
@@ -134,7 +140,7 @@ static SkShader* LinearGradient_create1(JNIEnv* env, jobject o,
static SkiaShader* LinearGradient_postCreate1(JNIEnv* env, jobject o, SkShader* shader,
float x0, float y0, float x1, float y1, jintArray colorArray,
jfloatArray posArray, int tileMode) {
-
+#ifdef USE_OPENGL_RENDERER
size_t count = env->GetArrayLength(colorArray);
const jint* colorValues = env->GetIntArrayElements(colorArray, NULL);
@@ -143,7 +149,9 @@ static SkiaShader* LinearGradient_postCreate1(JNIEnv* env, jobject o, SkShader*
storedBounds[2] = x1; storedBounds[3] = y1;
jfloat* storedPositions = new jfloat[count];
uint32_t* storedColors = new uint32_t[count];
- memcpy(storedColors, colorValues, count);
+ for (size_t i = 0; i < count; i++) {
+ storedColors[i] = static_cast<uint32_t>(colorValues[i]);
+ }
if (posArray) {
AutoJavaFloatArray autoPos(env, posArray, count);
@@ -162,10 +170,14 @@ static SkiaShader* LinearGradient_postCreate1(JNIEnv* env, jobject o, SkShader*
env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT);
return skiaShader;
+#else
+ return NULL;
+#endif
}
static SkiaShader* LinearGradient_postCreate2(JNIEnv* env, jobject o, SkShader* shader,
float x0, float y0, float x1, float y1, int color0, int color1, int tileMode) {
+#ifdef USE_OPENGL_RENDERER
float* storedBounds = new float[4];
storedBounds[0] = x0; storedBounds[1] = y0;
storedBounds[2] = x1; storedBounds[3] = y1;
@@ -175,14 +187,17 @@ static SkiaShader* LinearGradient_postCreate2(JNIEnv* env, jobject o, SkShader*
storedPositions[1] = 1.0f;
uint32_t* storedColors = new uint32_t[2];
- storedColors[0] = color0;
- storedColors[1] = color1;
+ storedColors[0] = static_cast<uint32_t>(color0);
+ storedColors[1] = static_cast<uint32_t>(color1);
SkiaShader* skiaShader = new SkiaLinearGradientShader(storedBounds, storedColors,
storedPositions, 2, shader, static_cast<SkShader::TileMode>(tileMode), NULL,
(shader->getFlags() & SkShader::kOpaqueAlpha_Flag) == 0);
return skiaShader;
+#else
+ return NULL;
+#endif
}
static SkShader* LinearGradient_create2(JNIEnv* env, jobject o,
@@ -315,22 +330,26 @@ static SkShader* ComposeShader_create2(JNIEnv* env, jobject o,
static SkiaShader* ComposeShader_postCreate2(JNIEnv* env, jobject o, SkShader* shader,
SkiaShader* shaderA, SkiaShader* shaderB, SkPorterDuff::Mode porterDuffMode) {
- SkAutoUnref au(SkPorterDuff::CreateXfermode(porterDuffMode));
- SkXfermode* mode = (SkXfermode*) au.get();
- SkXfermode::Mode skiaMode;
- if (!SkXfermode::IsMode(mode, &skiaMode)) {
- skiaMode = SkXfermode::kSrcOver_Mode;
- }
- return new SkiaComposeShader(shaderA, shaderB, skiaMode, shader);
+#ifdef USE_OPENGL_RENDERER
+ SkXfermode::Mode mode = SkPorterDuff::ToXfermodeMode(porterDuffMode);
+ return new SkiaComposeShader(shaderA, shaderB, mode, shader);
+#else
+ return NULL;
+#endif
}
static SkiaShader* ComposeShader_postCreate1(JNIEnv* env, jobject o, SkShader* shader,
SkiaShader* shaderA, SkiaShader* shaderB, SkXfermode* mode) {
+#ifdef USE_OPENGL_RENDERER
SkXfermode::Mode skiaMode;
if (!SkXfermode::IsMode(mode, &skiaMode)) {
+ // TODO: Support other modes
skiaMode = SkXfermode::kSrcOver_Mode;
}
return new SkiaComposeShader(shaderA, shaderB, skiaMode, shader);
+#else
+ return NULL;
+#endif
}
///////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/core/jni/android/graphics/TextLayout.cpp b/core/jni/android/graphics/TextLayout.cpp
index 716d96044e3e..147e1fa2e3a0 100644
--- a/core/jni/android/graphics/TextLayout.cpp
+++ b/core/jni/android/graphics/TextLayout.cpp
@@ -221,6 +221,18 @@ void TextLayout::handleText(SkPaint *paint, const jchar* text, jsize len,
}
}
+bool TextLayout::prepareRtlTextRun(const jchar* context, jsize start, jsize& count,
+ jsize contextCount, jchar* shaped) {
+ UErrorCode status = U_ZERO_ERROR;
+ count = shapeRtlText(context, start, count, contextCount, shaped, status);
+ if (U_SUCCESS(status)) {
+ return true;
+ } else {
+ LOG(LOG_WARN, "LAYOUT", "drawTextRun error %d\n", status);
+ }
+ return false;
+}
+
void TextLayout::drawTextRun(SkPaint* paint, const jchar* chars,
jint start, jint count, jint contextCount,
int dirFlags, jfloat x, jfloat y, SkCanvas* canvas) {
@@ -231,12 +243,8 @@ void TextLayout::drawTextRun(SkPaint* paint, const jchar* chars,
uint8_t rtl = dirFlags & 0x1;
if (rtl) {
SkAutoSTMalloc<80, jchar> buffer(contextCount);
- UErrorCode status = U_ZERO_ERROR;
- count = shapeRtlText(chars, start, count, contextCount, buffer.get(), status);
- if (U_SUCCESS(status)) {
+ if (prepareRtlTextRun(chars, start, count, contextCount, buffer.get())) {
canvas->drawText(buffer.get(), count << 1, x_, y_, *paint);
- } else {
- LOG(LOG_WARN, "LAYOUT", "drawTextRun error %d\n", status);
}
} else {
canvas->drawText(chars + start, count << 1, x_, y_, *paint);
diff --git a/core/jni/android/graphics/TextLayout.h b/core/jni/android/graphics/TextLayout.h
index 3d05e18f5e1c..8f666c095ccf 100644
--- a/core/jni/android/graphics/TextLayout.h
+++ b/core/jni/android/graphics/TextLayout.h
@@ -66,6 +66,9 @@ public:
static bool prepareText(SkPaint *paint, const jchar* text, jsize len, jint bidiFlags,
const jchar** outText, int32_t* outBytes, jchar** outBuffer);
+ static bool prepareRtlTextRun(const jchar* context, jsize start, jsize& count,
+ jsize contextCount, jchar* shaped);
+
private:
static bool needsLayout(const jchar* text, jint len, jint bidiFlags);
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 1feb3b3ecda9..0932473aa859 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -600,7 +600,7 @@ static bool mainWorkCallback(int fd, int events, void* data) {
static jint
loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jobject messageQueue,
jstring internalDataDir, jstring externalDataDir, int sdkVersion,
- jobject jAssetMgr)
+ jobject jAssetMgr, jbyteArray savedState)
{
LOG_TRACE("loadNativeCode_native");
@@ -666,7 +666,18 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jobject messageQ
code->assetManager = assetManagerForJavaObject(env, jAssetMgr);
- code->createActivityFunc(code, NULL, 0);
+ jbyte* rawSavedState = NULL;
+ jsize rawSavedSize = 0;
+ if (savedState != NULL) {
+ rawSavedState = env->GetByteArrayElements(savedState, NULL);
+ rawSavedSize = env->GetArrayLength(savedState);
+ }
+
+ code->createActivityFunc(code, rawSavedState, rawSavedSize);
+
+ if (rawSavedState != NULL) {
+ env->ReleaseByteArrayElements(savedState, rawSavedState, 0);
+ }
}
return (jint)code;
@@ -706,17 +717,31 @@ onResume_native(JNIEnv* env, jobject clazz, jint handle)
}
}
-static void
+static jbyteArray
onSaveInstanceState_native(JNIEnv* env, jobject clazz, jint handle)
{
LOG_TRACE("onSaveInstanceState_native");
+
+ jbyteArray array = NULL;
+
if (handle != 0) {
NativeCode* code = (NativeCode*)handle;
if (code->callbacks.onSaveInstanceState != NULL) {
size_t len = 0;
- code->callbacks.onSaveInstanceState(code, &len);
+ jbyte* state = (jbyte*)code->callbacks.onSaveInstanceState(code, &len);
+ if (len > 0) {
+ array = env->NewByteArray(len);
+ if (array != NULL) {
+ env->SetByteArrayRegion(array, 0, len, state);
+ }
+ }
+ if (state != NULL) {
+ free(state);
+ }
}
}
+
+ return array;
}
static void
@@ -744,6 +769,18 @@ onStop_native(JNIEnv* env, jobject clazz, jint handle)
}
static void
+onConfigurationChanged_native(JNIEnv* env, jobject clazz, jint handle)
+{
+ LOG_TRACE("onConfigurationChanged_native");
+ if (handle != 0) {
+ NativeCode* code = (NativeCode*)handle;
+ if (code->callbacks.onConfigurationChanged != NULL) {
+ code->callbacks.onConfigurationChanged(code);
+ }
+ }
+}
+
+static void
onLowMemory_native(JNIEnv* env, jobject clazz, jint handle)
{
LOG_TRACE("onLowMemory_native");
@@ -934,14 +971,15 @@ finishPreDispatchKeyEvent_native(JNIEnv* env, jobject clazz, jint handle,
}
static const JNINativeMethod g_methods[] = {
- { "loadNativeCode", "(Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;ILandroid/content/res/AssetManager;)I",
+ { "loadNativeCode", "(Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;ILandroid/content/res/AssetManager;[B)I",
(void*)loadNativeCode_native },
{ "unloadNativeCode", "(I)V", (void*)unloadNativeCode_native },
{ "onStartNative", "(I)V", (void*)onStart_native },
{ "onResumeNative", "(I)V", (void*)onResume_native },
- { "onSaveInstanceStateNative", "(I)V", (void*)onSaveInstanceState_native },
+ { "onSaveInstanceStateNative", "(I)[B", (void*)onSaveInstanceState_native },
{ "onPauseNative", "(I)V", (void*)onPause_native },
{ "onStopNative", "(I)V", (void*)onStop_native },
+ { "onConfigurationChangedNative", "(I)V", (void*)onConfigurationChanged_native },
{ "onLowMemoryNative", "(I)V", (void*)onLowMemory_native },
{ "onWindowFocusChangedNative", "(IZ)V", (void*)onWindowFocusChanged_native },
{ "onSurfaceCreatedNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceCreated_native },
diff --git a/core/jni/android_content_res_Configuration.cpp b/core/jni/android_content_res_Configuration.cpp
new file mode 100644
index 000000000000..28a43ab09f76
--- /dev/null
+++ b/core/jni/android_content_res_Configuration.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2010, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Configuration"
+
+#include <utils/Log.h>
+#include "utils/misc.h"
+
+#include "jni.h"
+#include <android_runtime/android_content_res_Configuration.h>
+#include "android_runtime/AndroidRuntime.h"
+
+namespace android {
+
+static struct {
+ jclass clazz;
+
+ jfieldID mcc;
+ jfieldID mnc;
+ jfieldID locale;
+ jfieldID screenLayout;
+ jfieldID touchscreen;
+ jfieldID keyboard;
+ jfieldID keyboardHidden;
+ jfieldID hardKeyboardHidden;
+ jfieldID navigation;
+ jfieldID navigationHidden;
+ jfieldID orientation;
+ jfieldID uiMode;
+} gConfigurationClassInfo;
+
+void android_Configuration_getFromJava(
+ JNIEnv* env, jobject clazz, struct AConfiguration* out) {
+ out->mcc = env->GetIntField(clazz, gConfigurationClassInfo.mcc);
+ out->mnc = env->GetIntField(clazz, gConfigurationClassInfo.mnc);
+ out->screenLayout = env->GetIntField(clazz, gConfigurationClassInfo.screenLayout);
+ out->touchscreen = env->GetIntField(clazz, gConfigurationClassInfo.touchscreen);
+ out->keyboard = env->GetIntField(clazz, gConfigurationClassInfo.keyboard);
+ out->navigation = env->GetIntField(clazz, gConfigurationClassInfo.navigation);
+
+ out->inputFlags = env->GetIntField(clazz, gConfigurationClassInfo.keyboardHidden);
+ int hardKeyboardHidden = env->GetIntField(clazz, gConfigurationClassInfo.hardKeyboardHidden);
+ if (out->inputFlags == ACONFIGURATION_KEYSHIDDEN_NO
+ && hardKeyboardHidden == 2) {
+ out->inputFlags = ACONFIGURATION_KEYSHIDDEN_SOFT;
+ }
+ out->inputFlags |= env->GetIntField(clazz, gConfigurationClassInfo.navigationHidden)
+ << ResTable_config::SHIFT_NAVHIDDEN;
+
+ out->orientation = env->GetIntField(clazz, gConfigurationClassInfo.orientation);
+ out->uiMode = env->GetIntField(clazz, gConfigurationClassInfo.uiMode);
+}
+
+/*
+ * JNI registration.
+ */
+static JNINativeMethod gMethods[] = {
+ /* name, signature, funcPtr */
+ //{ "getObbInfo_native", "(Ljava/lang/String;Landroid/content/res/ObbInfo;)Z",
+ // (void*) android_content_res_ObbScanner_getObbInfo },
+};
+
+#define FIND_CLASS(var, className) \
+ var = env->FindClass(className); \
+ LOG_FATAL_IF(! var, "Unable to find class " className); \
+ var = jclass(env->NewGlobalRef(var));
+
+#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
+ var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find field " fieldName);
+
+int register_android_content_res_Configuration(JNIEnv* env)
+{
+ FIND_CLASS(gConfigurationClassInfo.clazz, "android/content/res/Configuration");
+
+ GET_FIELD_ID(gConfigurationClassInfo.mcc, gConfigurationClassInfo.clazz,
+ "mcc", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.mnc, gConfigurationClassInfo.clazz,
+ "mnc", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.locale, gConfigurationClassInfo.clazz,
+ "locale", "Ljava/util/Locale;");
+ GET_FIELD_ID(gConfigurationClassInfo.screenLayout, gConfigurationClassInfo.clazz,
+ "screenLayout", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.touchscreen, gConfigurationClassInfo.clazz,
+ "touchscreen", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.keyboard, gConfigurationClassInfo.clazz,
+ "keyboard", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.keyboardHidden, gConfigurationClassInfo.clazz,
+ "keyboardHidden", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.hardKeyboardHidden, gConfigurationClassInfo.clazz,
+ "hardKeyboardHidden", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.navigation, gConfigurationClassInfo.clazz,
+ "navigation", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.navigationHidden, gConfigurationClassInfo.clazz,
+ "navigationHidden", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.orientation, gConfigurationClassInfo.clazz,
+ "orientation", "I");
+ GET_FIELD_ID(gConfigurationClassInfo.uiMode, gConfigurationClassInfo.clazz,
+ "uiMode", "I");
+
+ return AndroidRuntime::registerNativeMethods(env, "android/content/res/Configuration", gMethods,
+ NELEM(gMethods));
+}
+
+}; // namespace android
diff --git a/core/jni/android_os_ParcelFileDescriptor.cpp b/core/jni/android_os_ParcelFileDescriptor.cpp
index 848a57adea4d..eceef1c54620 100644
--- a/core/jni/android_os_ParcelFileDescriptor.cpp
+++ b/core/jni/android_os_ParcelFileDescriptor.cpp
@@ -66,6 +66,26 @@ static jobject android_os_ParcelFileDescriptor_getFileDescriptorFromSocket(JNIEn
return fileDescriptorClone;
}
+static int android_os_ParcelFileDescriptor_createPipeNative(JNIEnv* env,
+ jobject clazz, jobjectArray outFds)
+{
+ int fds[2];
+ if (pipe(fds) < 0) {
+ return -errno;
+ }
+
+ for (int i=0; i<2; i++) {
+ jobject fdObj = env->NewObject(gFileDescriptorOffsets.mClass,
+ gFileDescriptorOffsets.mConstructor);
+ if (fdObj != NULL) {
+ env->SetIntField(fdObj, gFileDescriptorOffsets.mDescriptor, fds[i]);
+ }
+ env->SetObjectArrayElement(outFds, i, fdObj);
+ }
+
+ return 0;
+}
+
static jint getFd(JNIEnv* env, jobject clazz)
{
jobject descriptor = env->GetObjectField(clazz, gParcelFileDescriptorOffsets.mFileDescriptor);
@@ -109,6 +129,8 @@ static jlong android_os_ParcelFileDescriptor_seekTo(JNIEnv* env,
static const JNINativeMethod gParcelFileDescriptorMethods[] = {
{"getFileDescriptorFromSocket", "(Ljava/net/Socket;)Ljava/io/FileDescriptor;",
(void*)android_os_ParcelFileDescriptor_getFileDescriptorFromSocket},
+ {"createPipeNative", "([Ljava/io/FileDescriptor;)I",
+ (void*)android_os_ParcelFileDescriptor_createPipeNative},
{"getStatSize", "()J",
(void*)android_os_ParcelFileDescriptor_getStatSize},
{"seekTo", "(J)J",
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 9fd2b8674f4a..9f94af9e30ce 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -27,6 +27,7 @@
#include <SkPaint.h>
#include <SkRegion.h>
#include <SkScalerContext.h>
+#include <SkTemplates.h>
#include <SkXfermode.h>
#include <OpenGLRenderer.h>
@@ -41,6 +42,13 @@ namespace android {
using namespace uirenderer;
+/**
+ * Note: OpenGLRenderer JNI layer is generated and compiled only on supported
+ * devices. This means all the logic must be compiled only when the
+ * preprocessor variable USE_OPENGL_RENDERER is defined.
+ */
+#ifdef USE_OPENGL_RENDERER
+
// ----------------------------------------------------------------------------
// Java APIs
// ----------------------------------------------------------------------------
@@ -228,6 +236,16 @@ static void android_view_GLES20Canvas_drawRect(JNIEnv* env, jobject canvas,
renderer->drawRect(left, top, right, bottom, paint);
}
+static void android_view_GLES20Canvas_drawRects(JNIEnv* env, jobject canvas,
+ OpenGLRenderer* renderer, SkRegion* region, SkPaint* paint) {
+ SkRegion::Iterator it(*region);
+ while (!it.done()) {
+ const SkIRect& r = it.rect();
+ renderer->drawRect(r.fLeft, r.fTop, r.fRight, r.fBottom, paint);
+ it.next();
+ }
+}
+
static void android_view_GLES20Canvas_drawPath(JNIEnv* env, jobject canvas,
OpenGLRenderer* renderer, SkPath* path, SkPaint* paint) {
renderer->drawPath(path, paint);
@@ -268,6 +286,23 @@ static void renderText(OpenGLRenderer* renderer, const jchar* text, int count,
}
}
+static void renderTextRun(OpenGLRenderer* renderer, const jchar* text,
+ jint start, jint count, jint contextCount, jfloat x, jfloat y,
+ int flags, SkPaint* paint) {
+ uint8_t rtl = flags & 0x1;
+ if (rtl) {
+ SkAutoSTMalloc<80, jchar> buffer(contextCount);
+ jchar* shaped = buffer.get();
+ if (TextLayout::prepareRtlTextRun(text, start, count, contextCount, shaped)) {
+ renderer->drawText((const char*) shaped, count << 1, count, x, y, paint);
+ } else {
+ LOGW("drawTextRun error");
+ }
+ } else {
+ renderer->drawText((const char*) (text + start), count << 1, count, x, y, paint);
+ }
+}
+
static void android_view_GLES20Canvas_drawTextArray(JNIEnv* env, jobject canvas,
OpenGLRenderer* renderer, jcharArray text, int index, int count,
jfloat x, jfloat y, int flags, SkPaint* paint) {
@@ -284,6 +319,42 @@ static void android_view_GLES20Canvas_drawText(JNIEnv* env, jobject canvas,
env->ReleaseStringChars(text, textArray);
}
+static void android_view_GLES20Canvas_drawTextRunArray(JNIEnv* env, jobject canvas,
+ OpenGLRenderer* renderer, jcharArray text, int index, int count,
+ int contextIndex, int contextCount, jfloat x, jfloat y, int dirFlags,
+ SkPaint* paint) {
+ jchar* textArray = env->GetCharArrayElements(text, NULL);
+ renderTextRun(renderer, textArray + contextIndex, index - contextIndex,
+ count, contextCount, x, y, dirFlags, paint);
+ env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
+ }
+
+static void android_view_GLES20Canvas_drawTextRun(JNIEnv* env, jobject canvas,
+ OpenGLRenderer* renderer, jstring text, int start, int end,
+ int contextStart, int contextEnd, jfloat x, jfloat y, int dirFlags,
+ SkPaint* paint) {
+ const jchar* textArray = env->GetStringChars(text, NULL);
+ jint count = end - start;
+ jint contextCount = contextEnd - contextStart;
+ renderTextRun(renderer, textArray + contextStart, start - contextStart,
+ count, contextCount, x, y, dirFlags, paint);
+ env->ReleaseStringChars(text, textArray);
+}
+
+#endif // USE_OPENGL_RENDERER
+
+// ----------------------------------------------------------------------------
+// Common
+// ----------------------------------------------------------------------------
+
+static jboolean android_view_GLES20Canvas_isAvailable(JNIEnv* env, jobject clazz) {
+#ifdef USE_OPENGL_RENDERER
+ return JNI_TRUE;
+#else
+ return JNI_FALSE;
+#endif
+}
+
// ----------------------------------------------------------------------------
// JNI Glue
// ----------------------------------------------------------------------------
@@ -291,59 +362,73 @@ static void android_view_GLES20Canvas_drawText(JNIEnv* env, jobject canvas,
const char* const kClassPathName = "android/view/GLES20Canvas";
static JNINativeMethod gMethods[] = {
- { "nCreateRenderer", "()I", (void*) android_view_GLES20Canvas_createRenderer },
- { "nDestroyRenderer", "(I)V", (void*) android_view_GLES20Canvas_destroyRenderer },
- { "nSetViewport", "(III)V", (void*) android_view_GLES20Canvas_setViewport },
- { "nPrepare", "(I)V", (void*) android_view_GLES20Canvas_prepare },
-
- { "nSave", "(II)I", (void*) android_view_GLES20Canvas_save },
- { "nRestore", "(I)V", (void*) android_view_GLES20Canvas_restore },
- { "nRestoreToCount", "(II)V", (void*) android_view_GLES20Canvas_restoreToCount },
- { "nGetSaveCount", "(I)I", (void*) android_view_GLES20Canvas_getSaveCount },
-
- { "nSaveLayer", "(IFFFFII)I", (void*) android_view_GLES20Canvas_saveLayer },
- { "nSaveLayerAlpha", "(IFFFFII)I", (void*) android_view_GLES20Canvas_saveLayerAlpha },
-
- { "nQuickReject", "(IFFFFI)Z", (void*) android_view_GLES20Canvas_quickReject },
- { "nClipRect", "(IFFFFI)Z", (void*) android_view_GLES20Canvas_clipRectF },
- { "nClipRect", "(IIIIII)Z", (void*) android_view_GLES20Canvas_clipRect },
-
- { "nTranslate", "(IFF)V", (void*) android_view_GLES20Canvas_translate },
- { "nRotate", "(IF)V", (void*) android_view_GLES20Canvas_rotate },
- { "nScale", "(IFF)V", (void*) android_view_GLES20Canvas_scale },
-
- { "nSetMatrix", "(II)V", (void*) android_view_GLES20Canvas_setMatrix },
- { "nGetMatrix", "(II)V", (void*) android_view_GLES20Canvas_getMatrix },
- { "nConcatMatrix", "(II)V", (void*) android_view_GLES20Canvas_concatMatrix },
-
- { "nDrawBitmap", "(IIFFI)V", (void*) android_view_GLES20Canvas_drawBitmap },
- { "nDrawBitmap", "(IIFFFFFFFFI)V", (void*) android_view_GLES20Canvas_drawBitmapRect },
- { "nDrawBitmap", "(IIII)V", (void*) android_view_GLES20Canvas_drawBitmapMatrix },
- { "nDrawPatch", "(II[BFFFFI)V", (void*) android_view_GLES20Canvas_drawPatch },
- { "nDrawColor", "(III)V", (void*) android_view_GLES20Canvas_drawColor },
- { "nDrawRect", "(IFFFFI)V", (void*) android_view_GLES20Canvas_drawRect },
- { "nDrawPath", "(III)V", (void*) android_view_GLES20Canvas_drawPath },
-
- { "nResetModifiers", "(I)V", (void*) android_view_GLES20Canvas_resetModifiers },
- { "nSetupShader", "(II)V", (void*) android_view_GLES20Canvas_setupShader },
- { "nSetupColorFilter", "(II)V", (void*) android_view_GLES20Canvas_setupColorFilter },
-
- { "nDrawText", "(I[CIIFFII)V", (void*) android_view_GLES20Canvas_drawTextArray },
- { "nDrawText", "(ILjava/lang/String;IIFFII)V",
+ { "nIsAvailable", "()Z", (void*) android_view_GLES20Canvas_isAvailable },
+
+#ifdef USE_OPENGL_RENDERER
+ { "nCreateRenderer", "()I", (void*) android_view_GLES20Canvas_createRenderer },
+ { "nDestroyRenderer", "(I)V", (void*) android_view_GLES20Canvas_destroyRenderer },
+ { "nSetViewport", "(III)V", (void*) android_view_GLES20Canvas_setViewport },
+ { "nPrepare", "(I)V", (void*) android_view_GLES20Canvas_prepare },
+
+ { "nSave", "(II)I", (void*) android_view_GLES20Canvas_save },
+ { "nRestore", "(I)V", (void*) android_view_GLES20Canvas_restore },
+ { "nRestoreToCount", "(II)V", (void*) android_view_GLES20Canvas_restoreToCount },
+ { "nGetSaveCount", "(I)I", (void*) android_view_GLES20Canvas_getSaveCount },
+
+ { "nSaveLayer", "(IFFFFII)I", (void*) android_view_GLES20Canvas_saveLayer },
+ { "nSaveLayerAlpha", "(IFFFFII)I", (void*) android_view_GLES20Canvas_saveLayerAlpha },
+
+ { "nQuickReject", "(IFFFFI)Z", (void*) android_view_GLES20Canvas_quickReject },
+ { "nClipRect", "(IFFFFI)Z", (void*) android_view_GLES20Canvas_clipRectF },
+ { "nClipRect", "(IIIIII)Z", (void*) android_view_GLES20Canvas_clipRect },
+
+ { "nTranslate", "(IFF)V", (void*) android_view_GLES20Canvas_translate },
+ { "nRotate", "(IF)V", (void*) android_view_GLES20Canvas_rotate },
+ { "nScale", "(IFF)V", (void*) android_view_GLES20Canvas_scale },
+
+ { "nSetMatrix", "(II)V", (void*) android_view_GLES20Canvas_setMatrix },
+ { "nGetMatrix", "(II)V", (void*) android_view_GLES20Canvas_getMatrix },
+ { "nConcatMatrix", "(II)V", (void*) android_view_GLES20Canvas_concatMatrix },
+
+ { "nDrawBitmap", "(IIFFI)V", (void*) android_view_GLES20Canvas_drawBitmap },
+ { "nDrawBitmap", "(IIFFFFFFFFI)V", (void*) android_view_GLES20Canvas_drawBitmapRect },
+ { "nDrawBitmap", "(IIII)V", (void*) android_view_GLES20Canvas_drawBitmapMatrix },
+ { "nDrawPatch", "(II[BFFFFI)V", (void*) android_view_GLES20Canvas_drawPatch },
+ { "nDrawColor", "(III)V", (void*) android_view_GLES20Canvas_drawColor },
+ { "nDrawRect", "(IFFFFI)V", (void*) android_view_GLES20Canvas_drawRect },
+ { "nDrawRects", "(III)V", (void*) android_view_GLES20Canvas_drawRects },
+ { "nDrawPath", "(III)V", (void*) android_view_GLES20Canvas_drawPath },
+
+ { "nResetModifiers", "(I)V", (void*) android_view_GLES20Canvas_resetModifiers },
+ { "nSetupShader", "(II)V", (void*) android_view_GLES20Canvas_setupShader },
+ { "nSetupColorFilter", "(II)V", (void*) android_view_GLES20Canvas_setupColorFilter },
+
+ { "nDrawText", "(I[CIIFFII)V", (void*) android_view_GLES20Canvas_drawTextArray },
+ { "nDrawText", "(ILjava/lang/String;IIFFII)V",
(void*) android_view_GLES20Canvas_drawText },
+ { "nDrawTextRun", "(I[CIIIIFFII)V", (void*) android_view_GLES20Canvas_drawTextRunArray },
+ { "nDrawTextRun", "(ILjava/lang/String;IIIIFFII)V",
+ (void*) android_view_GLES20Canvas_drawTextRun },
+
{ "nGetClipBounds", "(ILandroid/graphics/Rect;)Z",
(void*) android_view_GLES20Canvas_getClipBounds },
+#endif
};
-#define FIND_CLASS(var, className) \
- var = env->FindClass(className); \
- LOG_FATAL_IF(! var, "Unable to find class " className); \
- var = jclass(env->NewGlobalRef(var));
-
-#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
- var = env->GetMethodID(clazz, methodName, methodDescriptor); \
- LOG_FATAL_IF(! var, "Unable to find method " methodName);
+#ifdef USE_OPENGL_RENDERER
+ #define FIND_CLASS(var, className) \
+ var = env->FindClass(className); \
+ LOG_FATAL_IF(! var, "Unable to find class " className); \
+ var = jclass(env->NewGlobalRef(var));
+
+ #define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
+ var = env->GetMethodID(clazz, methodName, methodDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find method " methodName);
+#else
+ #define FIND_CLASS(var, className)
+ #define GET_METHOD_ID(var, clazz, methodName, methodDescriptor)
+#endif
int register_android_view_GLES20Canvas(JNIEnv* env) {
FIND_CLASS(gRectClassInfo.clazz, "android/graphics/Rect");
diff --git a/core/jni/android_view_HardwareRenderer.cpp b/core/jni/android_view_HardwareRenderer.cpp
deleted file mode 100644
index 6d20c9de85cf..000000000000
--- a/core/jni/android_view_HardwareRenderer.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <utils/SkGLCanvas.h>
-
-#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-#include <android_runtime/AndroidRuntime.h>
-#include <utils/misc.h>
-
-// ----------------------------------------------------------------------------
-
-namespace android {
-
-static void android_view_HardwareRenderer_abandonGlCaches(JNIEnv* env, jobject) {
- SkGLCanvas::AbandonAllTextures();
-}
-
-// ----------------------------------------------------------------------------
-
-const char* const kClassPathName = "android/view/HardwareRenderer";
-
-static JNINativeMethod gMethods[] = {
- { "nativeAbandonGlCaches", "()V", (void*)android_view_HardwareRenderer_abandonGlCaches },
-};
-
-int register_android_view_HardwareRenderer(JNIEnv* env) {
- return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
-}
-
-};
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 01ded6805a36..1f66d05d5e30 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1284,6 +1284,9 @@
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true">
</activity>
+ <activity android:name="com.android.internal.app.PlatLogoActivity"
+ android:theme="@style/Theme.NoTitleBar.Fullscreen">
+ </activity>
<activity android:name="com.android.internal.app.DisableCarModeActivity"
android:theme="@style/Theme.NoDisplay"
android:excludeFromRecents="true">
diff --git a/core/res/res/anim/animator_fade_in.xml b/core/res/res/anim/animator_fade_in.xml
new file mode 100644
index 000000000000..cd5042fb9a2e
--- /dev/null
+++ b/core/res/res/anim/animator_fade_in.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<property xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@anim/accelerate_interpolator"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:propertyName="alpha"
+ android:duration="@android:integer/config_mediumAnimTime"
+/>
diff --git a/core/res/res/anim/animator_fade_out.xml b/core/res/res/anim/animator_fade_out.xml
new file mode 100644
index 000000000000..dfb5d9c6b555
--- /dev/null
+++ b/core/res/res/anim/animator_fade_out.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<property xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@anim/accelerate_interpolator"
+ android:valueFrom="1.0"
+ android:valueTo="0.0"
+ android:propertyName="alpha"
+ android:duration="@android:integer/config_mediumAnimTime"
+/>
diff --git a/core/res/res/anim/fragment_close_enter.xml b/core/res/res/anim/fragment_close_enter.xml
new file mode 100644
index 000000000000..d4091e851d6d
--- /dev/null
+++ b/core/res/res/anim/fragment_close_enter.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<sequencer xmlns:android="http://schemas.android.com/apk/res/android">
+ <property
+ android:interpolator="@anim/decelerate_interpolator"
+ android:valueFrom="2"
+ android:valueTo="1"
+ android:valueType="floatType"
+ android:propertyName="scaleX"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <property
+ android:interpolator="@anim/decelerate_interpolator"
+ android:valueFrom="2"
+ android:valueTo="1"
+ android:valueType="floatType"
+ android:propertyName="scaleY"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <property
+ android:interpolator="@anim/decelerate_interpolator"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType"
+ android:propertyName="alpha"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <property
+ android:interpolator="@anim/decelerate_interpolator"
+ android:valueFrom="-400"
+ android:valueTo="0"
+ android:valueType="floatType"
+ android:propertyName="translationX"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+</sequencer> \ No newline at end of file
diff --git a/core/res/res/anim/fragment_close_exit.xml b/core/res/res/anim/fragment_close_exit.xml
new file mode 100644
index 000000000000..3e2cd22cc56f
--- /dev/null
+++ b/core/res/res/anim/fragment_close_exit.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<sequencer xmlns:android="http://schemas.android.com/apk/res/android">
+ <property
+ android:interpolator="@anim/accelerate_interpolator"
+ android:valueFrom="1"
+ android:valueTo=".5"
+ android:valueType="floatType"
+ android:propertyName="scaleX"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <property
+ android:interpolator="@anim/accelerate_interpolator"
+ android:valueFrom="1"
+ android:valueTo=".5"
+ android:valueType="floatType"
+ android:propertyName="scaleY"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <property
+ android:interpolator="@anim/accelerate_interpolator"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType"
+ android:propertyName="alpha"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <property
+ android:interpolator="@anim/accelerate_interpolator"
+ android:valueFrom="0"
+ android:valueTo="400"
+ android:valueType="floatType"
+ android:propertyName="translationX"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+</sequencer> \ No newline at end of file
diff --git a/core/res/res/anim/fragment_open_enter.xml b/core/res/res/anim/fragment_open_enter.xml
new file mode 100644
index 000000000000..c89001c09cd6
--- /dev/null
+++ b/core/res/res/anim/fragment_open_enter.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<sequencer xmlns:android="http://schemas.android.com/apk/res/android">
+ <property
+ android:interpolator="@anim/decelerate_interpolator"
+ android:valueFrom="2"
+ android:valueTo="1"
+ android:valueType="floatType"
+ android:propertyName="scaleX"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <property
+ android:interpolator="@anim/decelerate_interpolator"
+ android:valueFrom="2"
+ android:valueTo="1"
+ android:valueType="floatType"
+ android:propertyName="scaleY"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <property
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType"
+ android:propertyName="alpha"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <property
+ android:valueFrom="400"
+ android:valueTo="0"
+ android:valueType="floatType"
+ android:propertyName="translationX"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+</sequencer> \ No newline at end of file
diff --git a/core/res/res/anim/fragment_open_exit.xml b/core/res/res/anim/fragment_open_exit.xml
new file mode 100644
index 000000000000..427fe4f8f255
--- /dev/null
+++ b/core/res/res/anim/fragment_open_exit.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<sequencer xmlns:android="http://schemas.android.com/apk/res/android">
+ <property
+ android:interpolator="@anim/accelerate_interpolator"
+ android:valueFrom="1"
+ android:valueTo="2"
+ android:valueType="floatType"
+ android:propertyName="scaleX"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <property
+ android:interpolator="@anim/accelerate_interpolator"
+ android:valueFrom="1"
+ android:valueTo="2"
+ android:valueType="floatType"
+ android:propertyName="scaleY"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <property
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType"
+ android:propertyName="alpha"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <property
+ android:valueFrom="0"
+ android:valueTo="-400"
+ android:valueType="floatType"
+ android:propertyName="translationX"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+</sequencer> \ No newline at end of file
diff --git a/core/res/res/drawable-hdpi/btn_check_label_background_light.9.png b/core/res/res/drawable-hdpi/btn_check_label_background_light.9.png
new file mode 100644
index 000000000000..97e680664755
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_label_background_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off.png b/core/res/res/drawable-hdpi/btn_check_off.png
index aad9ef7800fd..911e1aa6598c 100644
--- a/core/res/res/drawable-hdpi/btn_check_off.png
+++ b/core/res/res/drawable-hdpi/btn_check_off.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_disable.png b/core/res/res/drawable-hdpi/btn_check_off_disable.png
index eaee9e0bec4d..d72e2b95b810 100644
--- a/core/res/res/drawable-hdpi/btn_check_off_disable.png
+++ b/core/res/res/drawable-hdpi/btn_check_off_disable.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_disable_focused.png b/core/res/res/drawable-hdpi/btn_check_off_disable_focused.png
index 6d2c2938a5a5..d72e2b95b810 100644
--- a/core/res/res/drawable-hdpi/btn_check_off_disable_focused.png
+++ b/core/res/res/drawable-hdpi/btn_check_off_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_disable_focused_light.png b/core/res/res/drawable-hdpi/btn_check_off_disable_focused_light.png
new file mode 100644
index 000000000000..240a0444e1eb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_disable_focused_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_disable_light.png b/core/res/res/drawable-hdpi/btn_check_off_disable_light.png
new file mode 100644
index 000000000000..240a0444e1eb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_disable_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_light.png b/core/res/res/drawable-hdpi/btn_check_off_light.png
new file mode 100644
index 000000000000..4ca3c56d3f3c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_pressed.png b/core/res/res/drawable-hdpi/btn_check_off_pressed.png
index 1c442e9b45b9..08f41812ff45 100644
--- a/core/res/res/drawable-hdpi/btn_check_off_pressed.png
+++ b/core/res/res/drawable-hdpi/btn_check_off_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_pressed_light.png b/core/res/res/drawable-hdpi/btn_check_off_pressed_light.png
new file mode 100644
index 000000000000..d3754ddb42ad
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_pressed_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_selected.png b/core/res/res/drawable-hdpi/btn_check_off_selected.png
index b852b2cb2f06..264f102132ed 100644
--- a/core/res/res/drawable-hdpi/btn_check_off_selected.png
+++ b/core/res/res/drawable-hdpi/btn_check_off_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_selected_light.png b/core/res/res/drawable-hdpi/btn_check_off_selected_light.png
new file mode 100644
index 000000000000..48506bf721ba
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_selected_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on.png b/core/res/res/drawable-hdpi/btn_check_on.png
index cd5c1814344a..5541c67ba042 100644
--- a/core/res/res/drawable-hdpi/btn_check_on.png
+++ b/core/res/res/drawable-hdpi/btn_check_on.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable.png b/core/res/res/drawable-hdpi/btn_check_on_disable.png
index b4fc51a1bf34..7805458d2cdc 100644
--- a/core/res/res/drawable-hdpi/btn_check_on_disable.png
+++ b/core/res/res/drawable-hdpi/btn_check_on_disable.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable_focused.png b/core/res/res/drawable-hdpi/btn_check_on_disable_focused.png
index bf346471edd5..7805458d2cdc 100644
--- a/core/res/res/drawable-hdpi/btn_check_on_disable_focused.png
+++ b/core/res/res/drawable-hdpi/btn_check_on_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable_focused_light.png b/core/res/res/drawable-hdpi/btn_check_on_disable_focused_light.png
new file mode 100644
index 000000000000..4e268d5168cb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_disable_focused_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable_light.png b/core/res/res/drawable-hdpi/btn_check_on_disable_light.png
new file mode 100644
index 000000000000..4e268d5168cb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_disable_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_light.png b/core/res/res/drawable-hdpi/btn_check_on_light.png
new file mode 100644
index 000000000000..768c4afaed20
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_pressed.png b/core/res/res/drawable-hdpi/btn_check_on_pressed.png
index fa5c7a23d932..37e3953c87b3 100644
--- a/core/res/res/drawable-hdpi/btn_check_on_pressed.png
+++ b/core/res/res/drawable-hdpi/btn_check_on_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_pressed_light.png b/core/res/res/drawable-hdpi/btn_check_on_pressed_light.png
new file mode 100644
index 000000000000..fc29e46248ab
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_pressed_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_selected.png b/core/res/res/drawable-hdpi/btn_check_on_selected.png
index a6a21adc1e02..a0beac44afc8 100644
--- a/core/res/res/drawable-hdpi/btn_check_on_selected.png
+++ b/core/res/res/drawable-hdpi/btn_check_on_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_selected_light.png b/core/res/res/drawable-hdpi/btn_check_on_selected_light.png
new file mode 100644
index 000000000000..5df45c74f140
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_selected_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_label_background_light.9.png b/core/res/res/drawable-hdpi/btn_radio_label_background_light.9.png
new file mode 100644
index 000000000000..45c5c6a76e19
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_label_background_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off.png b/core/res/res/drawable-hdpi/btn_radio_off.png
index c0b14aa4db86..301c97df691b 100644
--- a/core/res/res/drawable-hdpi/btn_radio_off.png
+++ b/core/res/res/drawable-hdpi/btn_radio_off.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_light.png b/core/res/res/drawable-hdpi/btn_radio_off_light.png
new file mode 100644
index 000000000000..657c8e55b8e9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_off_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_pressed.png b/core/res/res/drawable-hdpi/btn_radio_off_pressed.png
index 318958187c0e..5e6ef2b947df 100644
--- a/core/res/res/drawable-hdpi/btn_radio_off_pressed.png
+++ b/core/res/res/drawable-hdpi/btn_radio_off_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_pressed_light.png b/core/res/res/drawable-hdpi/btn_radio_off_pressed_light.png
new file mode 100644
index 000000000000..342bf1168c3f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_off_pressed_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_selected.png b/core/res/res/drawable-hdpi/btn_radio_off_selected.png
index f337703e2180..d11ae858a507 100644
--- a/core/res/res/drawable-hdpi/btn_radio_off_selected.png
+++ b/core/res/res/drawable-hdpi/btn_radio_off_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_selected_light.png b/core/res/res/drawable-hdpi/btn_radio_off_selected_light.png
new file mode 100644
index 000000000000..68bd1dfee468
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_off_selected_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on.png b/core/res/res/drawable-hdpi/btn_radio_on.png
index c90d2eb05e93..5b0dbe842358 100644
--- a/core/res/res/drawable-hdpi/btn_radio_on.png
+++ b/core/res/res/drawable-hdpi/btn_radio_on.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_light.png b/core/res/res/drawable-hdpi/btn_radio_on_light.png
new file mode 100644
index 000000000000..45ae36b5e040
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_on_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_pressed.png b/core/res/res/drawable-hdpi/btn_radio_on_pressed.png
index d79450b8e86a..c3a0d48f9fab 100644
--- a/core/res/res/drawable-hdpi/btn_radio_on_pressed.png
+++ b/core/res/res/drawable-hdpi/btn_radio_on_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_pressed_light.png b/core/res/res/drawable-hdpi/btn_radio_on_pressed_light.png
new file mode 100644
index 000000000000..ca223587650a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_on_pressed_light.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_selected.png b/core/res/res/drawable-hdpi/btn_radio_on_selected.png
index db50c437c5a2..6c05f472aca7 100644
--- a/core/res/res/drawable-hdpi/btn_radio_on_selected.png
+++ b/core/res/res/drawable-hdpi/btn_radio_on_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_selected_light.png b/core/res/res/drawable-hdpi/btn_radio_on_selected_light.png
new file mode 100644
index 000000000000..a17fa1edd124
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_on_selected_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_label_background_light.9.png b/core/res/res/drawable-mdpi/btn_check_label_background_light.9.png
new file mode 100644
index 000000000000..79367b8c5393
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_label_background_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off.png b/core/res/res/drawable-mdpi/btn_check_off.png
index 56d3861542ea..5e44c293ca33 100644
--- a/core/res/res/drawable-mdpi/btn_check_off.png
+++ b/core/res/res/drawable-mdpi/btn_check_off.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_disable.png b/core/res/res/drawable-mdpi/btn_check_off_disable.png
index e012afd28ddd..a603fb1aff9c 100644
--- a/core/res/res/drawable-mdpi/btn_check_off_disable.png
+++ b/core/res/res/drawable-mdpi/btn_check_off_disable.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_disable_focused.png b/core/res/res/drawable-mdpi/btn_check_off_disable_focused.png
index 0837bbdb89f7..a603fb1aff9c 100644
--- a/core/res/res/drawable-mdpi/btn_check_off_disable_focused.png
+++ b/core/res/res/drawable-mdpi/btn_check_off_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_disable_focused_light.png b/core/res/res/drawable-mdpi/btn_check_off_disable_focused_light.png
new file mode 100644
index 000000000000..69e9ff9eb1a2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_disable_focused_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_disable_light.png b/core/res/res/drawable-mdpi/btn_check_off_disable_light.png
new file mode 100644
index 000000000000..69e9ff9eb1a2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_disable_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_light.png b/core/res/res/drawable-mdpi/btn_check_off_light.png
new file mode 100644
index 000000000000..5b2ec928e324
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_pressed.png b/core/res/res/drawable-mdpi/btn_check_off_pressed.png
index 984dfd750d30..611bb1d7a05b 100644
--- a/core/res/res/drawable-mdpi/btn_check_off_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_check_off_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_pressed_light.png b/core/res/res/drawable-mdpi/btn_check_off_pressed_light.png
new file mode 100644
index 000000000000..5a0ea441f37a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_pressed_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_selected.png b/core/res/res/drawable-mdpi/btn_check_off_selected.png
index 20842d41c8f1..aa28df22a4fb 100644
--- a/core/res/res/drawable-mdpi/btn_check_off_selected.png
+++ b/core/res/res/drawable-mdpi/btn_check_off_selected.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_off_selected_light.png b/core/res/res/drawable-mdpi/btn_check_off_selected_light.png
new file mode 100644
index 000000000000..ade1136e0214
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_off_selected_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on.png b/core/res/res/drawable-mdpi/btn_check_on.png
index 791ac1d92c32..130d5629237c 100644
--- a/core/res/res/drawable-mdpi/btn_check_on.png
+++ b/core/res/res/drawable-mdpi/btn_check_on.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_disable.png b/core/res/res/drawable-mdpi/btn_check_on_disable.png
index 6cb02f3e4674..f19972a447b0 100644
--- a/core/res/res/drawable-mdpi/btn_check_on_disable.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_disable.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_disable_focused.png b/core/res/res/drawable-mdpi/btn_check_on_disable_focused.png
index 8a73b33f0df5..f19972a447b0 100644
--- a/core/res/res/drawable-mdpi/btn_check_on_disable_focused.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_disable_focused_light.png b/core/res/res/drawable-mdpi/btn_check_on_disable_focused_light.png
new file mode 100644
index 000000000000..13ef46e2c39d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_disable_focused_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_disable_light.png b/core/res/res/drawable-mdpi/btn_check_on_disable_light.png
new file mode 100644
index 000000000000..13ef46e2c39d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_disable_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_light.png b/core/res/res/drawable-mdpi/btn_check_on_light.png
new file mode 100644
index 000000000000..6b7808bbb6a6
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_pressed.png b/core/res/res/drawable-mdpi/btn_check_on_pressed.png
index 300d64afecb9..df753f51eb49 100644
--- a/core/res/res/drawable-mdpi/btn_check_on_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_pressed_light.png b/core/res/res/drawable-mdpi/btn_check_on_pressed_light.png
new file mode 100644
index 000000000000..6a4dd2ce7c76
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_pressed_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_selected.png b/core/res/res/drawable-mdpi/btn_check_on_selected.png
index 0b36adbe2712..7586881cc458 100644
--- a/core/res/res/drawable-mdpi/btn_check_on_selected.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_selected.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_selected_light.png b/core/res/res/drawable-mdpi/btn_check_on_selected_light.png
new file mode 100644
index 000000000000..24701ce9d59a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_on_selected_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_label_background_light.9.png b/core/res/res/drawable-mdpi/btn_radio_label_background_light.9.png
new file mode 100644
index 000000000000..16e89397baec
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_label_background_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off.png b/core/res/res/drawable-mdpi/btn_radio_off.png
index 407632b1c0d5..16c1c6bc7b51 100644
--- a/core/res/res/drawable-mdpi/btn_radio_off.png
+++ b/core/res/res/drawable-mdpi/btn_radio_off.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_light.png b/core/res/res/drawable-mdpi/btn_radio_off_light.png
new file mode 100644
index 000000000000..e8287f30693e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_off_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_pressed.png b/core/res/res/drawable-mdpi/btn_radio_off_pressed.png
index d6d8a9d4b685..b25217b6c214 100644
--- a/core/res/res/drawable-mdpi/btn_radio_off_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_radio_off_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_pressed_light.png b/core/res/res/drawable-mdpi/btn_radio_off_pressed_light.png
new file mode 100644
index 000000000000..b63b9b0250e6
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_off_pressed_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_selected.png b/core/res/res/drawable-mdpi/btn_radio_off_selected.png
index 53f3e870e77d..bef757299c1a 100644
--- a/core/res/res/drawable-mdpi/btn_radio_off_selected.png
+++ b/core/res/res/drawable-mdpi/btn_radio_off_selected.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_off_selected_light.png b/core/res/res/drawable-mdpi/btn_radio_off_selected_light.png
new file mode 100644
index 000000000000..af754e145cfa
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_off_selected_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on.png b/core/res/res/drawable-mdpi/btn_radio_on.png
index 25a3ccc54e4a..4ed74712e28c 100644
--- a/core/res/res/drawable-mdpi/btn_radio_on.png
+++ b/core/res/res/drawable-mdpi/btn_radio_on.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_light.png b/core/res/res/drawable-mdpi/btn_radio_on_light.png
new file mode 100644
index 000000000000..62aaa41318d1
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_on_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_pressed.png b/core/res/res/drawable-mdpi/btn_radio_on_pressed.png
index c904a35c742b..7cf91c6c7fa9 100644
--- a/core/res/res/drawable-mdpi/btn_radio_on_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_radio_on_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_pressed_light.png b/core/res/res/drawable-mdpi/btn_radio_on_pressed_light.png
new file mode 100644
index 000000000000..0d93507b66f4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_on_pressed_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_selected.png b/core/res/res/drawable-mdpi/btn_radio_on_selected.png
index 78e1fc0227ca..56f6f5b43fa8 100644
--- a/core/res/res/drawable-mdpi/btn_radio_on_selected.png
+++ b/core/res/res/drawable-mdpi/btn_radio_on_selected.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_selected_light.png b/core/res/res/drawable-mdpi/btn_radio_on_selected_light.png
new file mode 100644
index 000000000000..48dd8e9d3c6c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_on_selected_light.png
Binary files differ
diff --git a/core/res/res/drawable-nodpi/platlogo.jpg b/core/res/res/drawable-nodpi/platlogo.jpg
new file mode 100644
index 000000000000..0e7780c12b1a
--- /dev/null
+++ b/core/res/res/drawable-nodpi/platlogo.jpg
Binary files differ
diff --git a/core/res/res/drawable/btn_check_light.xml b/core/res/res/drawable/btn_check_light.xml
new file mode 100644
index 000000000000..85f119a3151a
--- /dev/null
+++ b/core/res/res/drawable/btn_check_light.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <!-- Enabled states -->
+
+ <item android:state_checked="true" android:state_window_focused="false"
+ android:state_enabled="true"
+ android:drawable="@drawable/btn_check_on_light" />
+ <item android:state_checked="false" android:state_window_focused="false"
+ android:state_enabled="true"
+ android:drawable="@drawable/btn_check_off_light" />
+
+ <item android:state_checked="true" android:state_pressed="true"
+ android:state_enabled="true"
+ android:drawable="@drawable/btn_check_on_pressed_light" />
+ <item android:state_checked="false" android:state_pressed="true"
+ android:state_enabled="true"
+ android:drawable="@drawable/btn_check_off_pressed_light" />
+
+ <item android:state_checked="true" android:state_focused="true"
+ android:state_enabled="true"
+ android:drawable="@drawable/btn_check_on_selected_light" />
+ <item android:state_checked="false" android:state_focused="true"
+ android:state_enabled="true"
+ android:drawable="@drawable/btn_check_off_selected_light" />
+
+ <item android:state_checked="false"
+ android:state_enabled="true"
+ android:drawable="@drawable/btn_check_off_light" />
+ <item android:state_checked="true"
+ android:state_enabled="true"
+ android:drawable="@drawable/btn_check_on_light" />
+
+
+ <!-- Disabled states -->
+
+ <item android:state_checked="true" android:state_window_focused="false"
+ android:drawable="@drawable/btn_check_on_disable_light" />
+ <item android:state_checked="false" android:state_window_focused="false"
+ android:drawable="@drawable/btn_check_off_disable_light" />
+
+ <item android:state_checked="true" android:state_focused="true"
+ android:drawable="@drawable/btn_check_on_disable_focused_light" />
+ <item android:state_checked="false" android:state_focused="true"
+ android:drawable="@drawable/btn_check_off_disable_focused_light" />
+
+ <item android:state_checked="false" android:drawable="@drawable/btn_check_off_disable_light" />
+ <item android:state_checked="true" android:drawable="@drawable/btn_check_on_disable_light" />
+
+</selector>
diff --git a/core/res/res/drawable/btn_radio_light.xml b/core/res/res/drawable/btn_radio_light.xml
new file mode 100644
index 000000000000..51c930bae4c6
--- /dev/null
+++ b/core/res/res/drawable/btn_radio_light.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_checked="true" android:state_window_focused="false"
+ android:drawable="@drawable/btn_radio_on_light" />
+ <item android:state_checked="false" android:state_window_focused="false"
+ android:drawable="@drawable/btn_radio_off_light" />
+
+ <item android:state_checked="true" android:state_pressed="true"
+ android:drawable="@drawable/btn_radio_on_pressed_light" />
+ <item android:state_checked="false" android:state_pressed="true"
+ android:drawable="@drawable/btn_radio_off_pressed_light" />
+
+ <item android:state_checked="true" android:state_focused="true"
+ android:drawable="@drawable/btn_radio_on_selected_light" />
+ <item android:state_checked="false" android:state_focused="true"
+ android:drawable="@drawable/btn_radio_off_selected_light" />
+
+ <item android:state_checked="false" android:drawable="@drawable/btn_radio_off_light" />
+ <item android:state_checked="true" android:drawable="@drawable/btn_radio_on_light" />
+</selector>
diff --git a/core/res/res/layout-xlarge/screen_action_bar_overlay.xml b/core/res/res/layout-xlarge/screen_action_bar_overlay.xml
new file mode 100644
index 000000000000..d0277f0687b2
--- /dev/null
+++ b/core/res/res/layout-xlarge/screen_action_bar_overlay.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!--
+This is an optimized layout for a screen with
+the Action Bar enabled overlaying application content.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fitsSystemWindows="true">
+ <FrameLayout android:id="@android:id/content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+ <ViewAnimator android:id="@+id/action_bar_animator"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="top"
+ android:inAnimation="@anim/push_down_in"
+ android:outAnimation="@anim/push_down_out">
+ <com.android.internal.widget.ActionBarView
+ android:id="@+id/action_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="?android:attr/windowActionBarStyle" />
+ <com.android.internal.widget.ActionBarContextView
+ android:id="@+id/action_context_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </ViewAnimator>
+ <ImageView android:src="?android:attr/windowContentOverlay"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/action_bar_animator" />
+</RelativeLayout>
diff --git a/core/res/res/layout/action_mode_bar.xml b/core/res/res/layout/action_mode_bar.xml
index 8e2e69dae673..acf327e808ec 100644
--- a/core/res/res/layout/action_mode_bar.xml
+++ b/core/res/res/layout/action_mode_bar.xml
@@ -19,4 +19,5 @@
<com.android.internal.widget.ActionBarContextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content"
+ android:visibility="gone" />
diff --git a/core/res/res/layout/preference_list_content.xml b/core/res/res/layout/preference_list_content.xml
index d530e96931cb..eeba18e743ca 100644
--- a/core/res/res/layout/preference_list_content.xml
+++ b/core/res/res/layout/preference_list_content.xml
@@ -59,15 +59,28 @@
android:drawablePadding="3dip"
android:text="@string/back_button_label"
/>
-
- <Button android:id="@+id/next_button"
- android:layout_width="150dip"
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_margin="5dip"
- android:layout_alignParentRight="true"
- android:drawableRight="@drawable/ic_btn_next"
- android:drawablePadding="3dip"
- android:text="@string/next_button_label"
- />
+ android:layout_alignParentRight="true">
+
+ <Button android:id="@+id/skip_button"
+ android:layout_width="150dip"
+ android:layout_height="wrap_content"
+ android:layout_margin="5dip"
+ android:text="@string/skip_button_label"
+ android:visibility="gone"
+ />
+
+ <Button android:id="@+id/next_button"
+ android:layout_width="150dip"
+ android:layout_height="wrap_content"
+ android:layout_margin="5dip"
+ android:drawableRight="@drawable/ic_btn_next"
+ android:drawablePadding="3dip"
+ android:text="@string/next_button_label"
+ />
+ </LinearLayout>
</RelativeLayout>
</LinearLayout>
diff --git a/core/res/res/layout/screen_action_bar_overlay.xml b/core/res/res/layout/screen_action_bar_overlay.xml
new file mode 100644
index 000000000000..cb1ffc57a05d
--- /dev/null
+++ b/core/res/res/layout/screen_action_bar_overlay.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!--
+This is an optimized layout for a screen with
+the Action Bar enabled overlaying application content.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fitsSystemWindows="true">
+ <FrameLayout android:id="@android:id/content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+ <ViewAnimator android:id="@+id/action_bar_animator"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="top"
+ android:inAnimation="@anim/push_down_in"
+ android:outAnimation="@anim/push_down_out">
+ <com.android.internal.widget.ActionBarView
+ android:id="@+id/action_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="?android:attr/windowActionBarStyle" />
+ <com.android.internal.widget.ActionBarContextView
+ android:id="@+id/action_context_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </ViewAnimator>
+ <ImageView android:src="?android:attr/windowContentOverlay"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/action_bar_animator" />
+ <LinearLayout android:id="@+id/lower_action_context_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom"
+ style="?android:attr/windowActionBarStyle"
+ android:visibility="gone" />
+</RelativeLayout>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index f7f4d6936bce..e768308e620e 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -746,8 +746,8 @@
<string name="force_close" msgid="3653416315450806396">"Ukončit aplikaci"</string>
<string name="report" msgid="4060218260984795706">"Nahlásit"</string>
<string name="wait" msgid="7147118217226317732">"Počkat"</string>
- <string name="smv_application" msgid="295583804361236288">"Aplikace <xliff:g id="APPLICATION">%1$s</xliff:g> (proces<xliff:g id="PROCESS">%2$s</xliff:g>) porušila své samovynucované zásady StrictMode."</string>
- <string name="smv_process" msgid="5120397012047462446">"Proces <xliff:g id="PROCESS">%1$s</xliff:g> porušil své samovynucované zásady StrictMode."</string>
+ <string name="smv_application" msgid="295583804361236288">"Aplikace <xliff:g id="APPLICATION">%1$s</xliff:g> (proces <xliff:g id="PROCESS">%2$s</xliff:g>) porušila své vlastní vynucené zásady StrictMode."</string>
+ <string name="smv_process" msgid="5120397012047462446">"Proces <xliff:g id="PROCESS">%1$s</xliff:g> porušil své vlastní vynucené zásady StrictMode."</string>
<string name="heavy_weight_notification" msgid="9087063985776626166">"Běží aplikace <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Tuto možnost vyberte, chcete-li přepnout na aplikaci."</string>
<string name="heavy_weight_switcher_title" msgid="1135403633766694316">"Přepnout mezi aplikacemi?"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index ac42fd1ec5d6..87376fec6a31 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -749,7 +749,7 @@
<string name="smv_application" msgid="295583804361236288">"Die Anwendung <xliff:g id="APPLICATION">%1$s</xliff:g> (Prozess <xliff:g id="PROCESS">%2$s</xliff:g>) hat gegen ihre selbsterzwungene StrictMode-Richtlinie verstoßen."</string>
<string name="smv_process" msgid="5120397012047462446">"Der Prozess <xliff:g id="PROCESS">%1$s</xliff:g> hat gegen seine selbsterzwungene StrictMode-Richtlinie verstoßen."</string>
<string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> läuft"</string>
- <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Auswahl zum Wechseln in die Anwendung"</string>
+ <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Auswählen zum Wechseln in die Anwendung"</string>
<string name="heavy_weight_switcher_title" msgid="1135403633766694316">"Anwendung wechseln?"</string>
<string name="heavy_weight_switcher_text" msgid="4592075610079319667">"Eine andere Anwendung wird bereits ausgeführt und muss vor dem Start einer neuen Anwendung beendet werden."</string>
<string name="old_app_action" msgid="493129172238566282">"Zu <xliff:g id="OLD_APP">%1$s</xliff:g> zurückkehren"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index c2dd90b254ae..04b6addc8ef6 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -746,8 +746,8 @@
<string name="force_close" msgid="3653416315450806396">"Forcer la fermeture"</string>
<string name="report" msgid="4060218260984795706">"Rapport"</string>
<string name="wait" msgid="7147118217226317732">"Attendre"</string>
- <string name="smv_application" msgid="295583804361236288">"L\'application <xliff:g id="APPLICATION">%1$s</xliff:g> (processus <xliff:g id="PROCESS">%2$s</xliff:g>) a enfreint ses propres règles StrictMode."</string>
- <string name="smv_process" msgid="5120397012047462446">"Le processus <xliff:g id="PROCESS">%1$s</xliff:g> a enfreint ses propres règles StrictMode."</string>
+ <string name="smv_application" msgid="295583804361236288">"L\'application <xliff:g id="APPLICATION">%1$s</xliff:g> (processus <xliff:g id="PROCESS">%2$s</xliff:g>) a enfreint ses propres règles du mode strict."</string>
+ <string name="smv_process" msgid="5120397012047462446">"Le processus <xliff:g id="PROCESS">%1$s</xliff:g> a enfreint ses propres règles du mode strict."</string>
<string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> en cours d\'exécution"</string>
<string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Sélectionner pour changer d\'application"</string>
<string name="heavy_weight_switcher_title" msgid="1135403633766694316">"Passer d\'une application à l\'autre ?"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index fcb7eb7f752f..21e8ad8d4f89 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -173,7 +173,7 @@
<string name="permlab_statusBar" msgid="7417192629601890791">"отключать или изменять строку состояния"</string>
<string name="permdesc_statusBar" msgid="1365473595331989732">"Позволяет приложению отключать строку состояния или добавлять/удалять системные значки."</string>
<string name="permlab_statusBarService" msgid="7247281911387931485">"строка состояния"</string>
- <string name="permdesc_statusBarService" msgid="4097605867643520920">"Позволяет приложению быть строкой состояния."</string>
+ <string name="permdesc_statusBarService" msgid="4097605867643520920">"Позволяет приложению заменять строку состояния."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"разворачивать/сворачивать строку состояния"</string>
<string name="permdesc_expandStatusBar" msgid="7088604400110768665">"Позволяет приложению разворачивать или сворачивать строку состояния."</string>
<string name="permlab_processOutgoingCalls" msgid="1136262550878335980">"перехватывать исходящие вызовы"</string>
@@ -315,7 +315,7 @@
<string name="permlab_recordAudio" msgid="3876049771427466323">"записывать аудио"</string>
<string name="permdesc_recordAudio" msgid="6493228261176552356">"Позволяет приложению получать доступ к пути аудиозаписи."</string>
<string name="permlab_camera" msgid="3616391919559751192">"снимать фото и видео"</string>
- <string name="permdesc_camera" msgid="6004878235852154239">"Позволяет приложению делать снимки и видео с помощью камеры. Это дает приложению возможность в любое время получать изображения с объектива камеры."</string>
+ <string name="permdesc_camera" msgid="6004878235852154239">"Позволяет приложению делать снимки и видео с помощью камеры в любое время."</string>
<string name="permlab_brick" msgid="8337817093326370537">"отключать телефон"</string>
<string name="permdesc_brick" msgid="5569526552607599221">"Позволяет данному приложению отключить телефон навсегда. Это очень опасно."</string>
<string name="permlab_reboot" msgid="2898560872462638242">"принудительно перезагружать телефон"</string>
@@ -749,7 +749,7 @@
<string name="smv_application" msgid="295583804361236288">"Приложение <xliff:g id="APPLICATION">%1$s</xliff:g> (процесс <xliff:g id="PROCESS">%2$s</xliff:g>) нарушило собственную политику StrictMode."</string>
<string name="smv_process" msgid="5120397012047462446">"Процесс <xliff:g id="PROCESS">%1$s</xliff:g> нарушил собственную политику StrictMode."</string>
<string name="heavy_weight_notification" msgid="9087063985776626166">"Приложение <xliff:g id="APP">%1$s</xliff:g> запущено"</string>
- <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Нажмите, чтобы переключиться в приложение"</string>
+ <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Нажмите, чтобы перейти к приложению"</string>
<string name="heavy_weight_switcher_title" msgid="1135403633766694316">"Переключить приложения?"</string>
<string name="heavy_weight_switcher_text" msgid="4592075610079319667">"Выполняется другое приложение, которое должно быть остановлено прежде, чем запускать новое."</string>
<string name="old_app_action" msgid="493129172238566282">"Вернуться к приложению <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 8e623e8adb49..e8f5a201605d 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -563,7 +563,7 @@
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM-kortet är låst."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Låser upp SIM-kort…"</string>
<string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="3514742106066877476">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. "\n\n"Försök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"Du har angett ditt lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. "\n\n"Försök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%d</xliff:g> gånger. "\n\n"Försök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
<string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6827749231465145590">"Du har angett din PIN-kod fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. "\n\n"Försök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
<string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%d</xliff:g> försök till kommer du att uppmanas att låsa upp telefonen med din Google-inloggning."\n\n" Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Försök igen om <xliff:g id="NUMBER">%d</xliff:g> sekunder."</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index b207db9513de..a9dd8871f9c7 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -746,8 +746,8 @@
<string name="force_close" msgid="3653416315450806396">"Kapanmaya zorla"</string>
<string name="report" msgid="4060218260984795706">"Rapor"</string>
<string name="wait" msgid="7147118217226317732">"Bekle"</string>
- <string name="smv_application" msgid="295583804361236288">"<xliff:g id="APPLICATION">%1$s</xliff:g> uygulaması (<xliff:g id="PROCESS">%2$s</xliff:g> işlemi) kendiliğinden zorunlu StrictMode politikasını ihlal etti."</string>
- <string name="smv_process" msgid="5120397012047462446">"<xliff:g id="PROCESS">%1$s</xliff:g> işlemi kendiliğinden zorunlu StrictMode politikasını ihlal etti."</string>
+ <string name="smv_application" msgid="295583804361236288">"<xliff:g id="APPLICATION">%1$s</xliff:g> uygulaması (<xliff:g id="PROCESS">%2$s</xliff:g> işlemi) kendiliğinden uyguladığı StrictMode politikasını ihlal etti."</string>
+ <string name="smv_process" msgid="5120397012047462446">"<xliff:g id="PROCESS">%1$s</xliff:g> işlemi kendiliğinden uyguladığı StrictMode politikasını ihlal etti."</string>
<string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> çalışıyor"</string>
<string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Uygulama değiştirmeyi seçin"</string>
<string name="heavy_weight_switcher_title" msgid="1135403633766694316">"Uygulamaların arasında geçiş yapılsın mı?"</string>
diff --git a/core/res/res/values-xlarge/styles.xml b/core/res/res/values-xlarge/styles.xml
index 40e423e23be1..ff7df7c3ec49 100644
--- a/core/res/res/values-xlarge/styles.xml
+++ b/core/res/res/values-xlarge/styles.xml
@@ -19,7 +19,6 @@
<style name="TextAppearance.StatusBar">
<item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
- <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
</style>
<style name="TextAppearance.StatusBar.Ticker">
</style>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 4d78b86bc8a1..0949553f4747 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -315,7 +315,7 @@
<string name="permlab_recordAudio" msgid="3876049771427466323">"录音"</string>
<string name="permdesc_recordAudio" msgid="6493228261176552356">"允许应用程序访问录音路径。"</string>
<string name="permlab_camera" msgid="3616391919559751192">"拍摄照片和视频"</string>
- <string name="permdesc_camera" msgid="6004878235852154239">"允许应用程序使用相机拍摄照片和视频,这样应用程序可随时收集进入相机镜头中看到的图片。"</string>
+ <string name="permdesc_camera" msgid="6004878235852154239">"允许应用程序使用相机拍摄照片和视频,这样应用程序可随时收集进入相机镜头中的图片。"</string>
<string name="permlab_brick" msgid="8337817093326370537">"永久停用手机"</string>
<string name="permdesc_brick" msgid="5569526552607599221">"允许应用程序永久停用整个手机,这非常危险。"</string>
<string name="permlab_reboot" msgid="2898560872462638242">"强行重新启动手机"</string>
@@ -747,7 +747,7 @@
<string name="report" msgid="4060218260984795706">"报告"</string>
<string name="wait" msgid="7147118217226317732">"等待"</string>
<string name="smv_application" msgid="295583804361236288">"应用程序<xliff:g id="APPLICATION">%1$s</xliff:g>(<xliff:g id="PROCESS">%2$s</xliff:g> 进程)违反了自我强制执行的严格模式 (StrictMode) 政策。"</string>
- <string name="smv_process" msgid="5120397012047462446">"进程 <xliff:g id="PROCESS">%1$s</xliff:g> 违反了自我强制执行的严格模式 (StrictMode) 政策"</string>
+ <string name="smv_process" msgid="5120397012047462446">"进程 <xliff:g id="PROCESS">%1$s</xliff:g> 违反了自我强制执行的严格模式 (StrictMode) 政策。"</string>
<string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g>正在运行"</string>
<string name="heavy_weight_notification_detail" msgid="2423977499339403402">"选择以切换到该应用程序"</string>
<string name="heavy_weight_switcher_title" msgid="1135403633766694316">"要切换应用程序吗?"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 4ab9907f8c5c..2f0a49bba9de 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -34,7 +34,7 @@
<string name="defaultVoiceMailAlphaTag" msgid="2660020990097733077">"語音留言"</string>
<string name="defaultMsisdnAlphaTag" msgid="2850889754919584674">"MSISDN1"</string>
<string name="mmiError" msgid="5154499457739052907">"連線發生問題或錯誤的 MMI 碼。"</string>
- <string name="mmiFdnError" msgid="5224398216385316471">"僅允許在固定撥號時使用此操作。"</string>
+ <string name="mmiFdnError" msgid="5224398216385316471">"僅限對固定撥號號碼執行此作業。"</string>
<string name="serviceEnabled" msgid="8147278346414714315">"服務已啟用。"</string>
<string name="serviceEnabledFor" msgid="6856228140453471041">"已啟用服務:"</string>
<string name="serviceDisabled" msgid="1937553226592516411">"服務已停用。"</string>
@@ -173,7 +173,7 @@
<string name="permlab_statusBar" msgid="7417192629601890791">"停用或變更狀態列"</string>
<string name="permdesc_statusBar" msgid="1365473595331989732">"允許應用程式停用狀態列或新增、移除系統圖示。"</string>
<string name="permlab_statusBarService" msgid="7247281911387931485">"狀態列"</string>
- <string name="permdesc_statusBarService" msgid="4097605867643520920">"允許應用程式成為狀態列。"</string>
+ <string name="permdesc_statusBarService" msgid="4097605867643520920">"允許應用程式以狀態列顯示。"</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"展開/收攏狀態列"</string>
<string name="permdesc_expandStatusBar" msgid="7088604400110768665">"允許應用程式展開或收攏狀態列。"</string>
<string name="permlab_processOutgoingCalls" msgid="1136262550878335980">"攔截撥出電話"</string>
@@ -314,8 +314,8 @@
<string name="permdesc_modifyAudioSettings" msgid="5793461287365991922">"允許應用程式編輯全域音訊設定,例如音量與路由。"</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"錄製音訊"</string>
<string name="permdesc_recordAudio" msgid="6493228261176552356">"允許應用程式存取音訊錄製路徑。"</string>
- <string name="permlab_camera" msgid="3616391919559751192">"拍照和錄影"</string>
- <string name="permdesc_camera" msgid="6004878235852154239">"允許應用程式使用相機拍照和錄影。此功能可讓應用程式隨時透過相機收集圖片。"</string>
+ <string name="permlab_camera" msgid="3616391919559751192">"拍照和拍攝影片"</string>
+ <string name="permdesc_camera" msgid="6004878235852154239">"允許應用程式使用相機拍照和錄影,此功能可讓應用程式隨時透過相機收集圖片。"</string>
<string name="permlab_brick" msgid="8337817093326370537">"永久停用電話"</string>
<string name="permdesc_brick" msgid="5569526552607599221">"允許應用程式永久停用手機。此項操作非常危險。"</string>
<string name="permlab_reboot" msgid="2898560872462638242">"強制重開機"</string>
@@ -535,7 +535,7 @@
<string name="orgTypeCustom" msgid="225523415372088322">"自訂"</string>
<string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"輸入 PIN 碼"</string>
<string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"輸入密碼即可解鎖"</string>
- <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"輸入 PIN 即可解鎖"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"輸入 PIN 進行解鎖"</string>
<string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"PIN 碼錯誤!"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"如要解鎖,請按 Menu 鍵,然後按 0。"</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"緊急電話號碼"</string>
@@ -563,8 +563,8 @@
<string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"SIM 卡已鎖定。"</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"解鎖 SIM 卡中..."</string>
<string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="3514742106066877476">"畫出解鎖圖形已錯誤 <xliff:g id="NUMBER_0">%d</xliff:g> 次。"\n\n" 請在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再嘗試。"</string>
- <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"您已輸入 <xliff:g id="NUMBER_0">%d</xliff:g> 次不正確的密碼。"\n\n"請於 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6827749231465145590">"您已輸入 <xliff:g id="NUMBER_0">%d</xliff:g> 次不正確的 PIN。"\n\n"請於 <xliff:g id="NUMBER_1">%d</xliff:g> 秒內再試一次。"</string>
+ <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="4906034376425175381">"您已 <xliff:g id="NUMBER_0">%d</xliff:g> 次輸入不正確的密碼。"\n\n"請於 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6827749231465145590">"您已 <xliff:g id="NUMBER_0">%d</xliff:g> 次輸入不正確的 PIN。"\n\n"請於 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string>
<string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"畫出解鎖圖形已錯誤 <xliff:g id="NUMBER_0">%d</xliff:g> 次。再錯誤 <xliff:g id="NUMBER_1">%d</xliff:g> 次後,系統會要求使用 Google 登入來解鎖。"\n\n" 請在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒後再試一次。"</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"<xliff:g id="NUMBER">%d</xliff:g> 秒後再試一次。"</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"忘記解鎖圖形?"</string>
@@ -746,12 +746,12 @@
<string name="force_close" msgid="3653416315450806396">"強制關閉"</string>
<string name="report" msgid="4060218260984795706">"回報"</string>
<string name="wait" msgid="7147118217226317732">"等待"</string>
- <string name="smv_application" msgid="295583804361236288">"應用程式 <xliff:g id="APPLICATION">%1$s</xliff:g> (處理程序 <xliff:g id="PROCESS">%2$s</xliff:g>) 已違反其自行實施的 StrictMode 政策。"</string>
- <string name="smv_process" msgid="5120397012047462446">"處理程序 <xliff:g id="PROCESS">%1$s</xliff:g> 已違反其自行實施的 StrictMode 政策。"</string>
+ <string name="smv_application" msgid="295583804361236288">"應用程式 <xliff:g id="APPLICATION">%1$s</xliff:g> (處理程序 <xliff:g id="PROCESS">%2$s</xliff:g>) 已違反其自行強制實施的嚴格模式 (StrictMode) 政策。"</string>
+ <string name="smv_process" msgid="5120397012047462446">"處理程序 <xliff:g id="PROCESS">%1$s</xliff:g> 已違反其自行強制實施的嚴格模式 (StrictMode) 政策。"</string>
<string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> 執行中"</string>
- <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"選取切換應用程式"</string>
+ <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"選取以切換到該應用程式"</string>
<string name="heavy_weight_switcher_title" msgid="1135403633766694316">"切換應用程式?"</string>
- <string name="heavy_weight_switcher_text" msgid="4592075610079319667">"其他應用程式已在執行中,您必須停止執行,才能啟動新的應用程式。"</string>
+ <string name="heavy_weight_switcher_text" msgid="4592075610079319667">"其他應用程式已在執行中,您必須停止執行該應用程式,才能啟動新的應用程式。"</string>
<string name="old_app_action" msgid="493129172238566282">"返回 <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
<string name="old_app_description" msgid="942967900237208466">"請勿啟動新的應用程式。"</string>
<string name="new_app_action" msgid="5472756926945440706">"啟動 <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
@@ -862,15 +862,15 @@
<string name="l2tp_ipsec_psk_vpn_description" msgid="3945043564008303239">"採用預先共用金鑰的 L2TP/IPSec VPN"</string>
<string name="l2tp_ipsec_crt_vpn_description" msgid="5382714073103653577">"採用憑證的 L2TP/IPSec VPN"</string>
<string name="upload_file" msgid="2897957172366730416">"選擇檔案"</string>
- <string name="no_file_chosen" msgid="6363648562170759465">"沒有選擇檔案"</string>
+ <string name="no_file_chosen" msgid="6363648562170759465">"未選擇任何檔案"</string>
<string name="reset" msgid="2448168080964209908">"重設"</string>
<string name="submit" msgid="1602335572089911941">"提交"</string>
<string name="car_mode_disable_notification_title" msgid="3164768212003864316">"已啟用車用模式"</string>
<string name="car_mode_disable_notification_message" msgid="668663626721675614">"選取結束車用模式。"</string>
<string name="tethered_notification_title" msgid="3146694234398202601">"數據連線或無線基地台已啟用"</string>
<string name="tethered_notification_message" msgid="3067108323903048927">"輕觸以設定"</string>
- <string name="back_button_label" msgid="2300470004503343439">"上一個"</string>
- <string name="next_button_label" msgid="1080555104677992408">"下一個"</string>
+ <string name="back_button_label" msgid="2300470004503343439">"上一頁"</string>
+ <string name="next_button_label" msgid="1080555104677992408">"下一頁"</string>
<string name="throttle_warning_notification_title" msgid="4890894267454867276">"高行動資料用量"</string>
<string name="throttle_warning_notification_message" msgid="2609734763845705708">"輕觸即可瞭解更多有關行動資料用量的詳細資訊"</string>
<string name="throttled_notification_title" msgid="6269541897729781332">"已達行動資料上限"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 97c58226b16c..a75f1a60d23e 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -248,6 +248,11 @@
in place of the usual title bar. -->
<attr name="windowActionBar" format="boolean" />
+ <!-- Flag indicating whether this window's Action Bar should overlay
+ application content. Does nothing if the window would not
+ have an Action Bar. -->
+ <attr name="windowActionBarOverlay" format="boolean" />
+
<!-- Reference to a style for the Action Bar -->
<attr name="windowActionBarStyle" format="reference" />
@@ -999,6 +1004,7 @@
<attr name="windowActionBar" />
<attr name="windowActionBarStyle" />
<attr name="windowActionModeOverlay" />
+ <attr name="windowActionBarOverlay" />
</declare-styleable>
<!-- The set of attributes that describe a AlertDialog's theme. -->
@@ -1015,6 +1021,14 @@
<attr name="centerMedium" format="reference|color" />
</declare-styleable>
+ <!-- Fragment animation class attributes. -->
+ <declare-styleable name="FragmentAnimation">
+ <attr name="fragmentOpenEnterAnimation" format="reference" />
+ <attr name="fragmentOpenExitAnimation" format="reference" />
+ <attr name="fragmentCloseEnterAnimation" format="reference" />
+ <attr name="fragmentCloseExitAnimation" format="reference" />
+ </declare-styleable>
+
<!-- Window animation class attributes. -->
<declare-styleable name="WindowAnimation">
<!-- The animation used when a window is being added. -->
@@ -3987,6 +4001,10 @@
<attr name="background" />
<!-- Specifies a layout for custom navigation. Overrides navigationMode. -->
<attr name="customNavigationLayout" format="reference" />
+ <!-- Specifies a fixed height. -->
+ <attr name="height" />
+ <!-- Specifies padding around all sides. -->
+ <attr name="padding" />
</declare-styleable>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 99263d72c5f9..539f1c010eee 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1315,6 +1315,13 @@
<public type="attr" name="propertyName" />
<public type="attr" name="ordering" />
<public type="attr" name="fragment" />
+ <public type="attr" name="windowActionBarOverlay" />
+ <public type="attr" name="fragmentOpenEnterAnimation" />
+ <public type="attr" name="fragmentOpenExitAnimation" />
+ <public type="attr" name="fragmentCloseEnterAnimation" />
+ <public type="attr" name="fragmentCloseExitAnimation" />
+ <public type="anim" name="animator_fade_in" />
+ <public type="anim" name="animator_fade_out" />
<public type="id" name="home" />
<!-- Context menu ID for the "Select text..." menu item to switch to text
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 32a1f0dbed2e..2b279304500d 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -807,8 +807,8 @@
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_writeCalendar">add or modify calendar events and send email to guests</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permdesc_writeCalendar">Allows an application to add or change the
- events on your calendar, which may send email to guests. Malicious applications can use this
+ <string name="permdesc_writeCalendar">Allows an application to add or change the
+ events on your calendar, which may send email to guests. Malicious applications can use this
to erase or modify your calendar events or to send email to guests.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -1610,7 +1610,7 @@
<!-- Do not translate. WebView User Agent string -->
<string name="web_user_agent" translatable="false">Mozilla/5.0 (Linux; U; <xliff:g id="x">Android %s</xliff:g>)
- AppleWebKit/534.4 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.4</string>
+ AppleWebKit/534.5 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.5</string>
<!-- Do not translate. WebView User Agent targeted content -->
<string name="web_user_agent_target_content" translatable="false">"Mobile "</string>
@@ -1918,7 +1918,7 @@
<!-- Text shown by list fragment when waiting for data to display. -->
<string name="loading">Loading...</string>
-
+
<!-- Default text for a button that can be toggled on and off. -->
<string name="capital_on">ON</string>
<!-- Default text for a button that can be toggled on and off. -->
@@ -1969,23 +1969,23 @@
<!-- Notification text to tell the user that a heavy-weight application is running. -->
<string name="heavy_weight_notification"><xliff:g id="app">%1$s</xliff:g> running</string>
-
+
<!-- Notification details to tell the user that a heavy-weight application is running. -->
<string name="heavy_weight_notification_detail">Select to switch to application</string>
-
+
<!-- Title of dialog prompting whether user wants to switch between heavy-weight apps. -->
<string name="heavy_weight_switcher_title">Switch applications?</string>
-
+
<!-- Descriptive text for switching to a new heavy-weight application. -->
<string name="heavy_weight_switcher_text">Another application is already running
that must be stopped before you can start a new one.</string>
-
+
<string name="old_app_action">Return to <xliff:g id="old_app">%1$s</xliff:g></string>
<string name="old_app_description">Don\'t start the new application.</string>
-
+
<string name="new_app_action">Start <xliff:g id="old_app">%1$s</xliff:g></string>
<string name="new_app_description">Stop the old application without saving.</string>
-
+
<!-- Displayed in the title of the chooser for things to do with text that
is to be sent to another application. For example, I can send
text through SMS or IM. A dialog with those choices would be shown,
@@ -2278,6 +2278,9 @@
<string name="back_button_label">Back</string>
<string name="next_button_label">Next</string>
+ <!-- Optional button to Skip a PreferenceActivity [CHAR LIMIT=20] -->
+ <string name="skip_button_label">Skip</string>
+
<!-- Strings for throttling notification -->
<!-- Shown when the user is in danger of being throttled -->
<string name="throttle_warning_notification_title">High mobile data use</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 993048d95d89..7e52ebd774a2 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -74,6 +74,10 @@
<item name="wallpaperIntraOpenExitAnimation">@anim/wallpaper_intra_open_exit</item>
<item name="wallpaperIntraCloseEnterAnimation">@anim/wallpaper_intra_close_enter</item>
<item name="wallpaperIntraCloseExitAnimation">@anim/wallpaper_intra_close_exit</item>
+ <item name="fragmentOpenEnterAnimation">@anim/fragment_open_enter</item>
+ <item name="fragmentOpenExitAnimation">@anim/fragment_open_exit</item>
+ <item name="fragmentCloseEnterAnimation">@anim/fragment_close_enter</item>
+ <item name="fragmentCloseExitAnimation">@anim/fragment_close_exit</item>
</style>
<!-- Standard animations for a non-full-screen window or activity. -->
@@ -279,11 +283,21 @@
<item name="android:button">@android:drawable/btn_check</item>
</style>
+ <style name="Widget.CompoundButton.CheckBox.Inverse">
+ <item name="android:background">@android:drawable/btn_check_label_background_light</item>
+ <item name="android:button">@android:drawable/btn_check_light</item>
+ </style>
+
<style name="Widget.CompoundButton.RadioButton">
<item name="android:background">@android:drawable/btn_radio_label_background</item>
<item name="android:button">@android:drawable/btn_radio</item>
</style>
+ <style name="Widget.CompoundButton.RadioButton.Inverse">
+ <item name="android:background">@android:drawable/btn_radio_label_background_light</item>
+ <item name="android:button">@android:drawable/btn_radio_light</item>
+ </style>
+
<style name="Widget.CompoundButton.Star">
<item name="android:background">@android:drawable/btn_star_label_background</item>
<item name="android:button">@android:drawable/btn_star</item>
@@ -879,6 +893,8 @@
<item name="android:background">@android:drawable/action_bar_background</item>
<item name="android:displayOptions">useLogo</item>
<item name="android:divider">@android:drawable/action_bar_divider</item>
+ <item name="android:height">56dip</item>
+ <item name="android:padding">3dip</item>
</style>
<style name="Widget.ActionButton">
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 739912a1a00d..3348b4ee90b7 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -84,9 +84,9 @@
<item name="searchResultListItemHeight">58dip</item>
<item name="listDivider">@drawable/divider_horizontal_dark</item>
<item name="listSeparatorTextViewStyle">@android:style/Widget.TextView.ListSeparator</item>
-
- <item name="listChoiceIndicatorSingle">@android:drawable/btn_radio</item>
- <item name="listChoiceIndicatorMultiple">@android:drawable/btn_check</item>
+
+ <item name="listChoiceIndicatorSingle">@android:drawable/btn_radio</item>
+ <item name="listChoiceIndicatorMultiple">@android:drawable/btn_check</item>
<item name="expandableListPreferredItemPaddingLeft">40dip</item>
<item name="expandableListPreferredChildPaddingLeft">
@@ -160,8 +160,8 @@
<item name="progressBarStyleSmallTitle">@android:style/Widget.ProgressBar.Small.Title</item>
<item name="progressBarStyleLarge">@android:style/Widget.ProgressBar.Large</item>
<item name="progressBarStyleInverse">@android:style/Widget.ProgressBar.Inverse</item>
- <item name="progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small.Inverse</item>
- <item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large.Inverse</item>
+ <item name="progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small.Inverse</item>
+ <item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large.Inverse</item>
<item name="seekBarStyle">@android:style/Widget.SeekBar</item>
<item name="ratingBarStyle">@android:style/Widget.RatingBar</item>
<item name="ratingBarStyleIndicator">@android:style/Widget.RatingBar.Indicator</item>
@@ -253,18 +253,24 @@
<item name="textCheckMark">@android:drawable/indicator_check_mark_light</item>
<item name="textCheckMarkInverse">@android:drawable/indicator_check_mark_dark</item>
+ <!-- List attributes -->
+ <item name="listChoiceIndicatorSingle">@android:drawable/btn_radio_light</item>
+ <item name="listChoiceIndicatorMultiple">@android:drawable/btn_check_light</item>
+
+ <!-- Widget styles -->
+ <item name="checkboxStyle">@android:style/Widget.CompoundButton.CheckBox.Inverse</item>
<item name="gestureOverlayViewStyle">@android:style/Widget.GestureOverlayView.White</item>
<item name="expandableListViewStyle">@android:style/Widget.ExpandableListView.White</item>
<item name="listViewStyle">@android:style/Widget.ListView.White</item>
<item name="listDivider">@drawable/divider_horizontal_bright</item>
<item name="listSeparatorTextViewStyle">@android:style/Widget.TextView.ListSeparator.White</item>
-
<item name="progressBarStyle">@android:style/Widget.ProgressBar.Inverse</item>
- <item name="progressBarStyleSmall">@android:style/Widget.ProgressBar.Small.Inverse</item>
- <item name="progressBarStyleLarge">@android:style/Widget.ProgressBar.Large.Inverse</item>
- <item name="progressBarStyleInverse">@android:style/Widget.ProgressBar</item>
- <item name="progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small</item>
- <item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large</item>
+ <item name="progressBarStyleSmall">@android:style/Widget.ProgressBar.Small.Inverse</item>
+ <item name="progressBarStyleLarge">@android:style/Widget.ProgressBar.Large.Inverse</item>
+ <item name="progressBarStyleInverse">@android:style/Widget.ProgressBar</item>
+ <item name="progressBarStyleSmallInverse">@android:style/Widget.ProgressBar.Small</item>
+ <item name="progressBarStyleLargeInverse">@android:style/Widget.ProgressBar.Large</item>
+ <item name="radioButtonStyle">@android:style/Widget.CompoundButton.RadioButton.Inverse</item>
</style>
<!-- Variant of the light theme with no title bar -->
diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
index 0fe83e194378..cbd87140da33 100644
--- a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
+++ b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
@@ -17,13 +17,11 @@
package android.bluetooth;
import android.app.Instrumentation;
-import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
public class BluetoothStressTest extends InstrumentationTestCase {
@@ -161,7 +159,6 @@ public class BluetoothStressTest extends InstrumentationTestCase {
mContext.unregisterReceiver(mReceiver);
}
- @LargeTest
public void testEnableDisable() {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
@@ -172,7 +169,6 @@ public class BluetoothStressTest extends InstrumentationTestCase {
}
}
- @LargeTest
public void testDiscoverable() {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
enable(adapter);
@@ -186,7 +182,6 @@ public class BluetoothStressTest extends InstrumentationTestCase {
disable(adapter);
}
- @LargeTest
public void testScan() {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
enable(adapter);
@@ -336,7 +331,7 @@ public class BluetoothStressTest extends InstrumentationTestCase {
mReceiver.resetFiredFlags();
if (!adapter.isEnabled()) {
- fail("undiscoverable(): bluetooth not enabled");
+ fail("undiscoverable() bluetooth not enabled");
}
int scanMode = adapter.getScanMode();
@@ -374,7 +369,7 @@ public class BluetoothStressTest extends InstrumentationTestCase {
mReceiver.resetFiredFlags();
if (!adapter.isEnabled()) {
- fail("startScan(): bluetooth not enabled");
+ fail("startScan() bluetooth not enabled");
}
if (adapter.isDiscovering()) {
@@ -404,7 +399,7 @@ public class BluetoothStressTest extends InstrumentationTestCase {
mReceiver.resetFiredFlags();
if (!adapter.isEnabled()) {
- fail("stopScan(): bluetooth not enabled");
+ fail("stopScan() bluetooth not enabled");
}
if (!adapter.isDiscovering()) {
diff --git a/core/tests/coretests/src/android/util/JsonReaderTest.java b/core/tests/coretests/src/android/util/JsonReaderTest.java
index b0cfb7bf4364..ced9310d5d5d 100644
--- a/core/tests/coretests/src/android/util/JsonReaderTest.java
+++ b/core/tests/coretests/src/android/util/JsonReaderTest.java
@@ -32,6 +32,14 @@ public final class JsonReaderTest extends TestCase {
assertEquals(JsonToken.END_DOCUMENT, reader.peek());
}
+ public void testReadEmptyArray() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("[]"));
+ reader.beginArray();
+ assertFalse(reader.hasNext());
+ reader.endArray();
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
public void testReadObject() throws IOException {
JsonReader reader = new JsonReader(new StringReader(
"{\"a\": \"android\", \"b\": \"banana\"}"));
@@ -44,6 +52,14 @@ public final class JsonReaderTest extends TestCase {
assertEquals(JsonToken.END_DOCUMENT, reader.peek());
}
+ public void testReadEmptyObject() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("{}"));
+ reader.beginObject();
+ assertFalse(reader.hasNext());
+ reader.endObject();
+ assertEquals(JsonToken.END_DOCUMENT, reader.peek());
+ }
+
public void testSkipObject() throws IOException {
JsonReader reader = new JsonReader(new StringReader(
"{\"a\": { \"c\": [], \"d\": [true, true, {}] }, \"b\": \"banana\"}"));
@@ -412,4 +428,256 @@ public final class JsonReaderTest extends TestCase {
} catch (IllegalStateException expected) {
}
}
+
+ public void testStrictNameValueSeparator() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("{\"a\"=true}"));
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ try {
+ reader.nextBoolean();
+ fail();
+ } catch (IOException expected) {
+ }
+
+ reader = new JsonReader(new StringReader("{\"a\"=>true}"));
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ try {
+ reader.nextBoolean();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testLenientNameValueSeparator() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("{\"a\"=true}"));
+ reader.setLenient(true);
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ assertEquals(true, reader.nextBoolean());
+
+ reader = new JsonReader(new StringReader("{\"a\"=>true}"));
+ reader.setLenient(true);
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ assertEquals(true, reader.nextBoolean());
+ }
+
+ public void testStrictComments() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("[// comment \n true]"));
+ reader.beginArray();
+ try {
+ reader.nextBoolean();
+ fail();
+ } catch (IOException expected) {
+ }
+
+ reader = new JsonReader(new StringReader("[# comment \n true]"));
+ reader.beginArray();
+ try {
+ reader.nextBoolean();
+ fail();
+ } catch (IOException expected) {
+ }
+
+ reader = new JsonReader(new StringReader("[/* comment */ true]"));
+ reader.beginArray();
+ try {
+ reader.nextBoolean();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testLenientComments() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("[// comment \n true]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals(true, reader.nextBoolean());
+
+ reader = new JsonReader(new StringReader("[# comment \n true]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals(true, reader.nextBoolean());
+
+ reader = new JsonReader(new StringReader("[/* comment */ true]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals(true, reader.nextBoolean());
+ }
+
+ public void testStrictUnquotedNames() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("{a:true}"));
+ reader.beginObject();
+ try {
+ reader.nextName();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testLenientUnquotedNames() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("{a:true}"));
+ reader.setLenient(true);
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ }
+
+ public void testStrictSingleQuotedNames() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("{'a':true}"));
+ reader.beginObject();
+ try {
+ reader.nextName();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testLenientSingleQuotedNames() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("{'a':true}"));
+ reader.setLenient(true);
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ }
+
+ public void testStrictUnquotedStrings() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("[a]"));
+ reader.beginArray();
+ try {
+ reader.nextString();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testLenientUnquotedStrings() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("[a]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals("a", reader.nextString());
+ }
+
+ public void testStrictSingleQuotedStrings() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("['a']"));
+ reader.beginArray();
+ try {
+ reader.nextString();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testLenientSingleQuotedStrings() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("['a']"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals("a", reader.nextString());
+ }
+
+ public void testStrictSemicolonDelimitedArray() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("[true;true]"));
+ reader.beginArray();
+ try {
+ reader.nextBoolean();
+ reader.nextBoolean();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testLenientSemicolonDelimitedArray() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("[true;true]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals(true, reader.nextBoolean());
+ assertEquals(true, reader.nextBoolean());
+ }
+
+ public void testStrictSemicolonDelimitedNameValuePair() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("{\"a\":true;\"b\":true}"));
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ try {
+ reader.nextBoolean();
+ reader.nextName();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testLenientSemicolonDelimitedNameValuePair() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("{\"a\":true;\"b\":true}"));
+ reader.setLenient(true);
+ reader.beginObject();
+ assertEquals("a", reader.nextName());
+ assertEquals(true, reader.nextBoolean());
+ assertEquals("b", reader.nextName());
+ }
+
+ public void testStrictUnnecessaryArraySeparators() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("[true,,true]"));
+ reader.beginArray();
+ assertEquals(true, reader.nextBoolean());
+ try {
+ reader.nextNull();
+ fail();
+ } catch (IOException expected) {
+ }
+
+ reader = new JsonReader(new StringReader("[,true]"));
+ reader.beginArray();
+ try {
+ reader.nextNull();
+ fail();
+ } catch (IOException expected) {
+ }
+
+ reader = new JsonReader(new StringReader("[true,]"));
+ reader.beginArray();
+ assertEquals(true, reader.nextBoolean());
+ try {
+ reader.nextNull();
+ fail();
+ } catch (IOException expected) {
+ }
+
+ reader = new JsonReader(new StringReader("[,]"));
+ reader.beginArray();
+ try {
+ reader.nextNull();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ public void testLenientUnnecessaryArraySeparators() throws IOException {
+ JsonReader reader = new JsonReader(new StringReader("[true,,true]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals(true, reader.nextBoolean());
+ reader.nextNull();
+ assertEquals(true, reader.nextBoolean());
+ reader.endArray();
+
+ reader = new JsonReader(new StringReader("[,true]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ reader.nextNull();
+ assertEquals(true, reader.nextBoolean());
+ reader.endArray();
+
+ reader = new JsonReader(new StringReader("[true,]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ assertEquals(true, reader.nextBoolean());
+ reader.nextNull();
+ reader.endArray();
+
+ reader = new JsonReader(new StringReader("[,]"));
+ reader.setLenient(true);
+ reader.beginArray();
+ reader.nextNull();
+ reader.nextNull();
+ reader.endArray();
+ }
}
diff --git a/core/tests/coretests/src/android/util/JsonWriterTest.java b/core/tests/coretests/src/android/util/JsonWriterTest.java
index 0bf7e042a7ac..fa840239d7b8 100644
--- a/core/tests/coretests/src/android/util/JsonWriterTest.java
+++ b/core/tests/coretests/src/android/util/JsonWriterTest.java
@@ -346,7 +346,7 @@ public final class JsonWriterTest extends TestCase {
public void testPrettyPrintObject() throws IOException {
StringWriter stringWriter = new StringWriter();
JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.setIndentSpaces(3);
+ jsonWriter.setIndent(" ");
jsonWriter.beginObject();
jsonWriter.name("a").value(true);
@@ -383,7 +383,7 @@ public final class JsonWriterTest extends TestCase {
public void testPrettyPrintArray() throws IOException {
StringWriter stringWriter = new StringWriter();
JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.setIndentSpaces(3);
+ jsonWriter.setIndent(" ");
jsonWriter.beginArray();
jsonWriter.value(true);
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 537dd3aeb730..d9ee3ec700a8 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -19,12 +19,16 @@ package android.graphics;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.DisplayMetrics;
+import android.util.Finalizers;
import java.io.OutputStream;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
public final class Bitmap implements Parcelable {
/**
@@ -55,7 +59,7 @@ public final class Bitmap implements Parcelable {
private static volatile Matrix sScaleMatrix;
private static volatile int sDefaultDensity = -1;
-
+
/**
* For backwards compatibility, allows the app layer to change the default
* density when running old apps.
@@ -81,8 +85,7 @@ public final class Bitmap implements Parcelable {
This can be called from JNI code.
*/
- private Bitmap(int nativeBitmap, boolean isMutable, byte[] ninePatchChunk,
- int density) {
+ private Bitmap(int nativeBitmap, boolean isMutable, byte[] ninePatchChunk, int density) {
if (nativeBitmap == 0) {
throw new RuntimeException("internal error: native bitmap is 0");
}
@@ -94,6 +97,13 @@ public final class Bitmap implements Parcelable {
if (density >= 0) {
mDensity = density;
}
+
+ // If the finalizers queue is null, we are running in zygote and the
+ // bitmap will never be reclaimed, so we don't need to run our native
+ // destructor
+ if (Finalizers.getQueue() != null) {
+ new BitmapFinalizer(this);
+ }
}
/**
@@ -1016,12 +1026,22 @@ public final class Bitmap implements Parcelable {
nativePrepareToDraw(mNativeBitmap);
}
- @Override
- protected void finalize() throws Throwable {
- try {
+ private static class BitmapFinalizer extends Finalizers.ReclaimableReference<Bitmap> {
+ private static final Set<BitmapFinalizer> sFinalizers = Collections.synchronizedSet(
+ new HashSet<BitmapFinalizer>());
+
+ private int mNativeBitmap;
+
+ BitmapFinalizer(Bitmap b) {
+ super(b, Finalizers.getQueue());
+ mNativeBitmap = b.mNativeBitmap;
+ sFinalizers.add(this);
+ }
+
+ @Override
+ public void reclaim() {
nativeDestructor(mNativeBitmap);
- } finally {
- super.finalize();
+ sFinalizers.remove(this);
}
}
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 77a1930b5fc3..36a8e574a92c 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -42,7 +42,6 @@ public class Canvas {
for both to be null.
*/
private Bitmap mBitmap; // if not null, mGL must be null
- private GL mGL; // if not null, mBitmap must be null
// optional field set by the caller
private DrawFilter mDrawFilter;
@@ -106,31 +105,22 @@ public class Canvas {
mDensity = bitmap.mDensity;
}
- /*package*/ Canvas(int nativeCanvas) {
+ Canvas(int nativeCanvas) {
if (nativeCanvas == 0) {
throw new IllegalStateException();
}
mNativeCanvas = nativeCanvas;
mDensity = Bitmap.getDefaultDensity();
}
-
+
/**
- * Construct a canvas with the specified gl context. All drawing through
- * this canvas will be redirected to OpenGL. Note: some features may not
- * be supported in this mode (e.g. some GL implementations may not support
- * antialiasing or certain effects like ColorMatrix or certain Xfermodes).
- * However, no exception will be thrown in those cases.
+ * Returns null.
*
- * <p>The initial target density of the canvas is the same as the initial
- * density of bitmaps as per {@link Bitmap#getDensity() Bitmap.getDensity()}.
- *
- * @deprecated This constructor is not supported and should not be invoked.
+ * @deprecated This method is not supported and should not be invoked.
*/
@Deprecated
- public Canvas(GL gl) {
- mNativeCanvas = initGL();
- mGL = gl;
- mDensity = Bitmap.getDefaultDensity();
+ protected GL getGL() {
+ return null;
}
/**
@@ -143,32 +133,9 @@ public class Canvas {
* false otherwise.
*/
public boolean isHardwareAccelerated() {
- return mGL != null;
- }
-
- /**
- * Return the GL object associated with this canvas, or null if it is not
- * backed by GL.
- *
- * @deprecated This method is not supported and should not be invoked.
- */
- @Deprecated
- public GL getGL() {
- return mGL;
- }
-
- /**
- * Call this to free up OpenGL resources that may be cached or allocated
- * on behalf of the Canvas. Any subsequent drawing with a GL-backed Canvas
- * will have to recreate those resources.
- *
- * @deprecated This method is not supported and should not be invoked.
- */
- @Deprecated
- public static void freeGlCaches() {
- freeCaches();
+ return false;
}
-
+
/**
* Specify a bitmap for the canvas to draw into. As a side-effect, also
* updates the canvas's target density to match that of the bitmap.
@@ -182,7 +149,7 @@ public class Canvas {
if (!bitmap.isMutable()) {
throw new IllegalStateException();
}
- if (mGL != null) {
+ if (isHardwareAccelerated()) {
throw new RuntimeException("Can't set a bitmap device on a GL canvas");
}
throwIfRecycled(bitmap);
@@ -196,16 +163,12 @@ public class Canvas {
* Set the viewport dimensions if this canvas is GL based. If it is not,
* this method is ignored and no exception is thrown.
*
- * @param width The width of the viewport
- * @param height The height of the viewport
+ * @param width The width of the viewport
+ * @param height The height of the viewport
*
- * @deprecated This method is not supported and should not be invoked.
+ * @hide
*/
- @Deprecated
public void setViewport(int width, int height) {
- if (mGL != null) {
- nativeSetViewport(mNativeCanvas, width, height);
- }
}
/**
@@ -1591,26 +1554,26 @@ public class Canvas {
@Override
protected void finalize() throws Throwable {
- super.finalize();
- // If the constructor threw an exception before setting mNativeCanvas, the native finalizer
- // must not be invoked.
- if (mNativeCanvas != 0) {
- finalizer(mNativeCanvas);
+ try {
+ super.finalize();
+ } finally {
+ // If the constructor threw an exception before setting mNativeCanvas,
+ // the native finalizer must not be invoked.
+ if (mNativeCanvas != 0) {
+ finalizer(mNativeCanvas);
+ }
}
}
/**
- * Free up as much memory as possible from private caches (e.g. fonts,
- * images)
+ * Free up as much memory as possible from private caches (e.g. fonts, images)
*
- * @hide - for now
+ * @hide
*/
public static native void freeCaches();
private static native int initRaster(int nativeBitmapOrZero);
- private static native int initGL();
private static native void native_setBitmap(int nativeCanvas, int bitmap);
- private static native void nativeSetViewport(int nCanvas, int w, int h);
private static native int native_saveLayer(int nativeCanvas, RectF bounds,
int paint, int layerFlags);
private static native int native_saveLayer(int nativeCanvas, float l,
diff --git a/graphics/java/android/graphics/ComposeShader.java b/graphics/java/android/graphics/ComposeShader.java
index 9b57ea44242c..8d5c913d1b69 100644
--- a/graphics/java/android/graphics/ComposeShader.java
+++ b/graphics/java/android/graphics/ComposeShader.java
@@ -20,6 +20,14 @@ package android.graphics;
an {@link android.graphics.Xfermode} subclass.
*/
public class ComposeShader extends Shader {
+ /**
+ * Hold onto the shaders to avoid GC.
+ */
+ @SuppressWarnings({"UnusedDeclaration"})
+ private final Shader mShaderA;
+ @SuppressWarnings({"UnusedDeclaration"})
+ private final Shader mShaderB;
+
/** Create a new compose shader, given shaders A, B, and a combining mode.
When the mode is applied, it will be given the result from shader A as its
"dst", and the result of from shader B as its "src".
@@ -29,10 +37,18 @@ public class ComposeShader extends Shader {
is null, then SRC_OVER is assumed.
*/
public ComposeShader(Shader shaderA, Shader shaderB, Xfermode mode) {
+ mShaderA = shaderA;
+ mShaderB = shaderB;
native_instance = nativeCreate1(shaderA.native_instance, shaderB.native_instance,
(mode != null) ? mode.native_instance : 0);
- native_shader = nativePostCreate1(native_instance, shaderA.native_shader,
- shaderB.native_shader, (mode != null) ? mode.native_instance : 0);
+ if (mode instanceof PorterDuffXfermode) {
+ PorterDuff.Mode pdMode = ((PorterDuffXfermode) mode).mode;
+ native_shader = nativePostCreate2(native_instance, shaderA.native_shader,
+ shaderB.native_shader, pdMode != null ? pdMode.nativeInt : 0);
+ } else {
+ native_shader = nativePostCreate1(native_instance, shaderA.native_shader,
+ shaderB.native_shader, mode != null ? mode.native_instance : 0);
+ }
}
/** Create a new compose shader, given shaders A, B, and a combining PorterDuff mode.
@@ -43,6 +59,8 @@ public class ComposeShader extends Shader {
@param mode The PorterDuff mode that combines the colors from the two shaders.
*/
public ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode) {
+ mShaderA = shaderA;
+ mShaderB = shaderB;
native_instance = nativeCreate2(shaderA.native_instance, shaderB.native_instance,
mode.nativeInt);
native_shader = nativePostCreate2(native_instance, shaderA.native_shader,
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index cb2c6a294f38..c3416a01f571 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -16,6 +16,8 @@
package android.graphics;
+import android.view.HardwareRenderer;
+
/**
* The Path class encapsulates compound (multiple contour) geometric paths
* consisting of straight line segments, quadratic curves, and cubic curves.
@@ -24,12 +26,27 @@ package android.graphics;
* text on a path.
*/
public class Path {
+ /**
+ * @hide
+ */
+ public final int mNativePath;
+
+ /**
+ * @hide
+ */
+ public boolean isSimplePath = true;
+ /**
+ * @hide
+ */
+ public Region rects;
+ private boolean mDetectSimplePaths;
/**
* Create an empty path
*/
public Path() {
mNativePath = init1();
+ mDetectSimplePaths = HardwareRenderer.isAvailable();
}
/**
@@ -43,6 +60,7 @@ public class Path {
valNative = src.mNativePath;
}
mNativePath = init2(valNative);
+ mDetectSimplePaths = HardwareRenderer.isAvailable();
}
/**
@@ -50,6 +68,10 @@ public class Path {
* This does NOT change the fill-type setting.
*/
public void reset() {
+ isSimplePath = true;
+ if (mDetectSimplePaths) {
+ if (rects != null) rects.setEmpty();
+ }
native_reset(mNativePath);
}
@@ -58,6 +80,10 @@ public class Path {
* keeps the internal data structure for faster reuse.
*/
public void rewind() {
+ isSimplePath = true;
+ if (mDetectSimplePaths) {
+ if (rects != null) rects.setEmpty();
+ }
native_rewind(mNativePath);
}
@@ -65,6 +91,7 @@ public class Path {
*/
public void set(Path src) {
if (this != src) {
+ isSimplePath = src.isSimplePath;
native_set(mNativePath, src.mNativePath);
}
}
@@ -160,6 +187,7 @@ public class Path {
* @param bounds Returns the computed bounds of the path's control points.
* @param exact This parameter is no longer used.
*/
+ @SuppressWarnings({"UnusedDeclaration"})
public void computeBounds(RectF bounds, boolean exact) {
native_computeBounds(mNativePath, bounds);
}
@@ -236,6 +264,7 @@ public class Path {
* @param y2 The y-coordinate of the end point on a quadratic curve
*/
public void quadTo(float x1, float y1, float x2, float y2) {
+ isSimplePath = false;
native_quadTo(mNativePath, x1, y1, x2, y2);
}
@@ -254,6 +283,7 @@ public class Path {
* this contour, for the end point of a quadratic curve
*/
public void rQuadTo(float dx1, float dy1, float dx2, float dy2) {
+ isSimplePath = false;
native_rQuadTo(mNativePath, dx1, dy1, dx2, dy2);
}
@@ -271,6 +301,7 @@ public class Path {
*/
public void cubicTo(float x1, float y1, float x2, float y2,
float x3, float y3) {
+ isSimplePath = false;
native_cubicTo(mNativePath, x1, y1, x2, y2, x3, y3);
}
@@ -281,6 +312,7 @@ public class Path {
*/
public void rCubicTo(float x1, float y1, float x2, float y2,
float x3, float y3) {
+ isSimplePath = false;
native_rCubicTo(mNativePath, x1, y1, x2, y2, x3, y3);
}
@@ -299,6 +331,7 @@ public class Path {
*/
public void arcTo(RectF oval, float startAngle, float sweepAngle,
boolean forceMoveTo) {
+ isSimplePath = false;
native_arcTo(mNativePath, oval, startAngle, sweepAngle, forceMoveTo);
}
@@ -314,6 +347,7 @@ public class Path {
* @param sweepAngle Sweep angle (in degrees) measured clockwise
*/
public void arcTo(RectF oval, float startAngle, float sweepAngle) {
+ isSimplePath = false;
native_arcTo(mNativePath, oval, startAngle, sweepAngle, false);
}
@@ -322,6 +356,7 @@ public class Path {
* first point of the contour, a line segment is automatically added.
*/
public void close() {
+ isSimplePath = false;
native_close(mNativePath);
}
@@ -351,6 +386,11 @@ public class Path {
if (rect == null) {
throw new NullPointerException("need rect parameter");
}
+ if (mDetectSimplePaths) {
+ if (rects == null) rects = new Region();
+ rects.op((int) rect.left, (int) rect.top, (int) rect.right, (int) rect.bottom,
+ Region.Op.UNION);
+ }
native_addRect(mNativePath, rect, dir.nativeInt);
}
@@ -363,8 +403,11 @@ public class Path {
* @param bottom The bottom of a rectangle to add to the path
* @param dir The direction to wind the rectangle's contour
*/
- public void addRect(float left, float top, float right, float bottom,
- Direction dir) {
+ public void addRect(float left, float top, float right, float bottom, Direction dir) {
+ if (mDetectSimplePaths) {
+ if (rects == null) rects = new Region();
+ rects.op((int) left, (int) top, (int) right, (int) bottom, Region.Op.UNION);
+ }
native_addRect(mNativePath, left, top, right, bottom, dir.nativeInt);
}
@@ -378,6 +421,7 @@ public class Path {
if (oval == null) {
throw new NullPointerException("need oval parameter");
}
+ isSimplePath = false;
native_addOval(mNativePath, oval, dir.nativeInt);
}
@@ -390,6 +434,7 @@ public class Path {
* @param dir The direction to wind the circle's contour
*/
public void addCircle(float x, float y, float radius, Direction dir) {
+ isSimplePath = false;
native_addCircle(mNativePath, x, y, radius, dir.nativeInt);
}
@@ -404,6 +449,7 @@ public class Path {
if (oval == null) {
throw new NullPointerException("need oval parameter");
}
+ isSimplePath = false;
native_addArc(mNativePath, oval, startAngle, sweepAngle);
}
@@ -419,6 +465,7 @@ public class Path {
if (rect == null) {
throw new NullPointerException("need rect parameter");
}
+ isSimplePath = false;
native_addRoundRect(mNativePath, rect, rx, ry, dir.nativeInt);
}
@@ -438,6 +485,7 @@ public class Path {
if (radii.length < 8) {
throw new ArrayIndexOutOfBoundsException("radii[] needs 8 values");
}
+ isSimplePath = false;
native_addRoundRect(mNativePath, rect, radii, dir.nativeInt);
}
@@ -448,6 +496,7 @@ public class Path {
* @param dx The amount to translate the path in X as it is added
*/
public void addPath(Path src, float dx, float dy) {
+ isSimplePath = false;
native_addPath(mNativePath, src.mNativePath, dx, dy);
}
@@ -457,6 +506,7 @@ public class Path {
* @param src The path that is appended to the current path
*/
public void addPath(Path src) {
+ isSimplePath = false;
native_addPath(mNativePath, src.mNativePath);
}
@@ -466,6 +516,7 @@ public class Path {
* @param src The path to add as a new contour
*/
public void addPath(Path src, Matrix matrix) {
+ if (!src.isSimplePath) isSimplePath = false;
native_addPath(mNativePath, src.mNativePath, matrix.native_instance);
}
@@ -502,6 +553,7 @@ public class Path {
* @param dy The new Y coordinate for the last point
*/
public void setLastPoint(float dx, float dy) {
+ isSimplePath = false;
native_setLastPoint(mNativePath, dx, dy);
}
@@ -537,8 +589,8 @@ public class Path {
super.finalize();
}
}
-
- /*package*/ final int ni() {
+
+ final int ni() {
return mNativePath;
}
@@ -592,9 +644,4 @@ public class Path {
int dst_path);
private static native void native_transform(int nPath, int matrix);
private static native void finalizer(int nPath);
-
- /**
- * @hide
- */
- public final int mNativePath;
}
diff --git a/graphics/java/android/graphics/PorterDuffXfermode.java b/graphics/java/android/graphics/PorterDuffXfermode.java
index cb127fdaa3fb..6ba064cc05ef 100644
--- a/graphics/java/android/graphics/PorterDuffXfermode.java
+++ b/graphics/java/android/graphics/PorterDuffXfermode.java
@@ -18,11 +18,17 @@ package android.graphics;
public class PorterDuffXfermode extends Xfermode {
/**
+ * @hide
+ */
+ public final PorterDuff.Mode mode;
+
+ /**
* Create an xfermode that uses the specified porter-duff mode.
*
* @param mode The porter-duff mode that is applied
*/
public PorterDuffXfermode(PorterDuff.Mode mode) {
+ this.mode = mode;
native_instance = nativeCreateXfermode(mode.nativeInt);
}
diff --git a/graphics/java/android/graphics/Region.java b/graphics/java/android/graphics/Region.java
index 489ef83811f5..e5408068e64f 100644
--- a/graphics/java/android/graphics/Region.java
+++ b/graphics/java/android/graphics/Region.java
@@ -20,6 +20,10 @@ import android.os.Parcel;
import android.os.Parcelable;
public class Region implements Parcelable {
+ /**
+ * @hide
+ */
+ public final int mNativeRegion;
// the native values for these must match up with the enum in SkRegion.h
public enum Op {
@@ -329,10 +333,14 @@ public class Region implements Parcelable {
}
protected void finalize() throws Throwable {
- nativeDestructor(mNativeRegion);
+ try {
+ nativeDestructor(mNativeRegion);
+ } finally {
+ super.finalize();
+ }
}
- /*package*/ Region(int ni) {
+ Region(int ni) {
if (ni == 0) {
throw new RuntimeException();
}
@@ -345,7 +353,7 @@ public class Region implements Parcelable {
this(ni);
}
- /*package*/ final int ni() {
+ final int ni() {
return mNativeRegion;
}
@@ -374,6 +382,4 @@ public class Region implements Parcelable {
Parcel p);
private static native boolean nativeEquals(int native_r1, int native_r2);
-
- private final int mNativeRegion;
}
diff --git a/graphics/java/android/graphics/Xfermode.java b/graphics/java/android/graphics/Xfermode.java
index 42c410e58b3a..2467bdc29bcc 100644
--- a/graphics/java/android/graphics/Xfermode.java
+++ b/graphics/java/android/graphics/Xfermode.java
@@ -31,7 +31,11 @@ package android.graphics;
public class Xfermode {
protected void finalize() throws Throwable {
- finalizer(native_instance);
+ try {
+ finalizer(native_instance);
+ } finally {
+ super.finalize();
+ }
}
private static native void finalizer(int native_instance);
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 33ecbea910ed..88f6d43c834c 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -313,18 +313,16 @@ public class GradientDrawable extends Drawable {
case RECTANGLE:
if (st.mRadiusArray != null) {
mPath.reset();
- mPath.addRoundRect(mRect, st.mRadiusArray,
- Path.Direction.CW);
+ mPath.addRoundRect(mRect, st.mRadiusArray, Path.Direction.CW);
canvas.drawPath(mPath, mFillPaint);
if (haveStroke) {
canvas.drawPath(mPath, mStrokePaint);
}
- }
- else {
+ } else if (st.mRadius > 0.0f) {
// since the caller is only giving us 1 value, we will force
// it to be square if the rect is too small in one dimension
// to show it. If we did nothing, Skia would clamp the rad
- // independently along each axis, giving us a thin ellips
+ // independently along each axis, giving us a thin ellipse
// if the rect were very wide but not very tall
float rad = st.mRadius;
float r = Math.min(mRect.width(), mRect.height()) * 0.5f;
@@ -335,6 +333,11 @@ public class GradientDrawable extends Drawable {
if (haveStroke) {
canvas.drawRoundRect(mRect, rad, rad, mStrokePaint);
}
+ } else {
+ canvas.drawRect(mRect, mFillPaint);
+ if (haveStroke) {
+ canvas.drawRect(mRect, mStrokePaint);
+ }
}
break;
case OVAL:
diff --git a/graphics/java/android/renderscript/Matrix4f.java b/graphics/java/android/renderscript/Matrix4f.java
index e854cd9dd6b7..5ffc21a58939 100644
--- a/graphics/java/android/renderscript/Matrix4f.java
+++ b/graphics/java/android/renderscript/Matrix4f.java
@@ -179,6 +179,76 @@ public class Matrix4f {
tmp.loadTranslate(x, y, z);
multiply(tmp);
}
+ private float computeCofactor(int i, int j) {
+ int c0 = (i+1) % 4;
+ int c1 = (i+2) % 4;
+ int c2 = (i+3) % 4;
+ int r0 = (j+1) % 4;
+ int r1 = (j+2) % 4;
+ int r2 = (j+3) % 4;
+
+ float minor = (mMat[c0 + 4*r0] * (mMat[c1 + 4*r1] * mMat[c2 + 4*r2] -
+ mMat[c1 + 4*r2] * mMat[c2 + 4*r1]))
+ - (mMat[c0 + 4*r1] * (mMat[c1 + 4*r0] * mMat[c2 + 4*r2] -
+ mMat[c1 + 4*r2] * mMat[c2 + 4*r0]))
+ + (mMat[c0 + 4*r2] * (mMat[c1 + 4*r0] * mMat[c2 + 4*r1] -
+ mMat[c1 + 4*r1] * mMat[c2 + 4*r0]));
+
+ float cofactor = ((i+j) & 1) != 0 ? -minor : minor;
+ return cofactor;
+ }
+
+ public boolean inverse() {
+
+ Matrix4f result = new Matrix4f();
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ result.mMat[4*i + j] = computeCofactor(i, j);
+ }
+ }
+
+ // Dot product of 0th column of source and 0th row of result
+ float det = mMat[0]*result.mMat[0] + mMat[4]*result.mMat[1] +
+ mMat[8]*result.mMat[2] + mMat[12]*result.mMat[3];
+
+ if (Math.abs(det) < 1e-6) {
+ return false;
+ }
+
+ det = 1.0f / det;
+ for (int i = 0; i < 16; ++i) {
+ mMat[i] = result.mMat[i] * det;
+ }
+
+ return true;
+ }
+
+ public boolean inverseTranspose() {
+
+ Matrix4f result = new Matrix4f();
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ result.mMat[4*j + i] = computeCofactor(i, j);
+ }
+ }
+
+ float det = mMat[0]*result.mMat[0] + mMat[4]*result.mMat[4] +
+ mMat[8]*result.mMat[8] + mMat[12]*result.mMat[12];
+
+ if (Math.abs(det) < 1e-6) {
+ return false;
+ }
+
+ det = 1.0f / det;
+ for (int i = 0; i < 16; ++i) {
+ mMat[i] = result.mMat[i] * det;
+ }
+
+ return true;
+ }
+
public void transpose() {
for(int i = 0; i < 3; ++i) {
for(int j = i + 1; j < 4; ++j) {
diff --git a/include/android_runtime/android_content_res_Configuration.h b/include/android_runtime/android_content_res_Configuration.h
new file mode 100644
index 000000000000..2f5a98249cb7
--- /dev/null
+++ b/include/android_runtime/android_content_res_Configuration.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_CONTENT_RES_CONFIGURATION_H
+#define _ANDROID_CONTENT_RES_CONFIGURATION_H
+
+#include <utils/ResourceTypes.h>
+#include <android/configuration.h>
+
+#include "jni.h"
+
+struct AConfiguration : android::ResTable_config {
+};
+
+namespace android {
+
+extern void android_Configuration_getFromJava(
+ JNIEnv* env, jobject clazz, struct AConfiguration* out);
+
+} // namespace android
+
+
+#endif // _ANDROID_CONTENT_RES_CONFIGURATION_H
diff --git a/include/media/stagefright/MediaDefs.h b/include/media/stagefright/MediaDefs.h
index 9cddab2b358d..92ce068a40d5 100644
--- a/include/media/stagefright/MediaDefs.h
+++ b/include/media/stagefright/MediaDefs.h
@@ -34,6 +34,8 @@ extern const char *MEDIA_MIMETYPE_AUDIO_MPEG;
extern const char *MEDIA_MIMETYPE_AUDIO_AAC;
extern const char *MEDIA_MIMETYPE_AUDIO_QCELP;
extern const char *MEDIA_MIMETYPE_AUDIO_VORBIS;
+extern const char *MEDIA_MIMETYPE_AUDIO_G711_ALAW;
+extern const char *MEDIA_MIMETYPE_AUDIO_G711_MLAW;
extern const char *MEDIA_MIMETYPE_AUDIO_RAW;
extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG4;
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index f1622316fcc8..71c6c517692d 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -454,6 +454,8 @@ public:
virtual void reset();
virtual void process(const RawEvent* rawEvent);
+ virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
+
private:
// Amount that trackball needs to move in order to generate a key event.
static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6;
diff --git a/include/utils/AssetManager.h b/include/utils/AssetManager.h
index 97694ff0a997..9e2bf37e8fa4 100644
--- a/include/utils/AssetManager.h
+++ b/include/utils/AssetManager.h
@@ -129,6 +129,8 @@ public:
*/
void setConfiguration(const ResTable_config& config, const char* locale = NULL);
+ void getConfiguration(ResTable_config* outConfig) const;
+
typedef Asset::AccessMode AccessMode; // typing shortcut
/*
diff --git a/include/utils/ObbFile.h b/include/utils/ObbFile.h
index 075927cdfa2e..d2ca82eb55c7 100644
--- a/include/utils/ObbFile.h
+++ b/include/utils/ObbFile.h
@@ -35,6 +35,8 @@ public:
bool readFrom(int fd);
bool writeTo(const char* filename);
bool writeTo(int fd);
+ bool removeFrom(const char* filename);
+ bool removeFrom(int fd);
const char* getFileName() const {
return mFileName;
@@ -78,6 +80,8 @@ private:
size_t mFileSize;
+ size_t mFooterStart;
+
unsigned char* mReadBuf;
bool parseObbFile(int fd);
diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h
index c7d9ff1dd678..da86da41006d 100644
--- a/include/utils/ResourceTypes.h
+++ b/include/utils/ResourceTypes.h
@@ -31,6 +31,8 @@
#include <stdint.h>
#include <sys/types.h>
+#include <android/configuration.h>
+
namespace android {
/** ********************************************************************
@@ -822,25 +824,25 @@ struct ResTable_config
};
enum {
- ORIENTATION_ANY = 0x0000,
- ORIENTATION_PORT = 0x0001,
- ORIENTATION_LAND = 0x0002,
- ORIENTATION_SQUARE = 0x0003,
+ ORIENTATION_ANY = ACONFIGURATION_ORIENTATION_ANY,
+ ORIENTATION_PORT = ACONFIGURATION_ORIENTATION_PORT,
+ ORIENTATION_LAND = ACONFIGURATION_ORIENTATION_LAND,
+ ORIENTATION_SQUARE = ACONFIGURATION_ORIENTATION_SQUARE,
};
enum {
- TOUCHSCREEN_ANY = 0x0000,
- TOUCHSCREEN_NOTOUCH = 0x0001,
- TOUCHSCREEN_STYLUS = 0x0002,
- TOUCHSCREEN_FINGER = 0x0003,
+ TOUCHSCREEN_ANY = ACONFIGURATION_TOUCHSCREEN_ANY,
+ TOUCHSCREEN_NOTOUCH = ACONFIGURATION_TOUCHSCREEN_NOTOUCH,
+ TOUCHSCREEN_STYLUS = ACONFIGURATION_TOUCHSCREEN_STYLUS,
+ TOUCHSCREEN_FINGER = ACONFIGURATION_TOUCHSCREEN_FINGER,
};
enum {
- DENSITY_DEFAULT = 0,
- DENSITY_LOW = 120,
- DENSITY_MEDIUM = 160,
- DENSITY_HIGH = 240,
- DENSITY_NONE = 0xffff
+ DENSITY_DEFAULT = ACONFIGURATION_DENSITY_DEFAULT,
+ DENSITY_LOW = ACONFIGURATION_DENSITY_LOW,
+ DENSITY_MEDIUM = ACONFIGURATION_DENSITY_MEDIUM,
+ DENSITY_HIGH = ACONFIGURATION_DENSITY_HIGH,
+ DENSITY_NONE = ACONFIGURATION_DENSITY_NONE
};
union {
@@ -853,33 +855,34 @@ struct ResTable_config
};
enum {
- KEYBOARD_ANY = 0x0000,
- KEYBOARD_NOKEYS = 0x0001,
- KEYBOARD_QWERTY = 0x0002,
- KEYBOARD_12KEY = 0x0003,
+ KEYBOARD_ANY = ACONFIGURATION_KEYBOARD_ANY,
+ KEYBOARD_NOKEYS = ACONFIGURATION_KEYBOARD_NOKEYS,
+ KEYBOARD_QWERTY = ACONFIGURATION_KEYBOARD_QWERTY,
+ KEYBOARD_12KEY = ACONFIGURATION_KEYBOARD_12KEY,
};
enum {
- NAVIGATION_ANY = 0x0000,
- NAVIGATION_NONAV = 0x0001,
- NAVIGATION_DPAD = 0x0002,
- NAVIGATION_TRACKBALL = 0x0003,
- NAVIGATION_WHEEL = 0x0004,
+ NAVIGATION_ANY = ACONFIGURATION_NAVIGATION_ANY,
+ NAVIGATION_NONAV = ACONFIGURATION_NAVIGATION_NONAV,
+ NAVIGATION_DPAD = ACONFIGURATION_NAVIGATION_DPAD,
+ NAVIGATION_TRACKBALL = ACONFIGURATION_NAVIGATION_TRACKBALL,
+ NAVIGATION_WHEEL = ACONFIGURATION_NAVIGATION_WHEEL,
};
enum {
MASK_KEYSHIDDEN = 0x0003,
- KEYSHIDDEN_ANY = 0x0000,
- KEYSHIDDEN_NO = 0x0001,
- KEYSHIDDEN_YES = 0x0002,
- KEYSHIDDEN_SOFT = 0x0003,
+ KEYSHIDDEN_ANY = ACONFIGURATION_KEYSHIDDEN_ANY,
+ KEYSHIDDEN_NO = ACONFIGURATION_KEYSHIDDEN_NO,
+ KEYSHIDDEN_YES = ACONFIGURATION_KEYSHIDDEN_YES,
+ KEYSHIDDEN_SOFT = ACONFIGURATION_KEYSHIDDEN_SOFT,
};
enum {
MASK_NAVHIDDEN = 0x000c,
- NAVHIDDEN_ANY = 0x0000,
- NAVHIDDEN_NO = 0x0004,
- NAVHIDDEN_YES = 0x0008,
+ SHIFT_NAVHIDDEN = 2,
+ NAVHIDDEN_ANY = ACONFIGURATION_NAVHIDDEN_ANY << SHIFT_NAVHIDDEN,
+ NAVHIDDEN_NO = ACONFIGURATION_NAVHIDDEN_NO << SHIFT_NAVHIDDEN,
+ NAVHIDDEN_YES = ACONFIGURATION_NAVHIDDEN_YES << SHIFT_NAVHIDDEN,
};
union {
@@ -929,32 +932,34 @@ struct ResTable_config
enum {
// screenLayout bits for screen size class.
MASK_SCREENSIZE = 0x0f,
- SCREENSIZE_ANY = 0x00,
- SCREENSIZE_SMALL = 0x01,
- SCREENSIZE_NORMAL = 0x02,
- SCREENSIZE_LARGE = 0x03,
- SCREENSIZE_XLARGE = 0x04,
+ SCREENSIZE_ANY = ACONFIGURATION_SCREENSIZE_ANY,
+ SCREENSIZE_SMALL = ACONFIGURATION_SCREENSIZE_SMALL,
+ SCREENSIZE_NORMAL = ACONFIGURATION_SCREENSIZE_NORMAL,
+ SCREENSIZE_LARGE = ACONFIGURATION_SCREENSIZE_LARGE,
+ SCREENSIZE_XLARGE = ACONFIGURATION_SCREENSIZE_XLARGE,
// screenLayout bits for wide/long screen variation.
MASK_SCREENLONG = 0x30,
- SCREENLONG_ANY = 0x00,
- SCREENLONG_NO = 0x10,
- SCREENLONG_YES = 0x20,
+ SHIFT_SCREENLONG = 4,
+ SCREENLONG_ANY = ACONFIGURATION_SCREENLONG_ANY << SHIFT_SCREENLONG,
+ SCREENLONG_NO = ACONFIGURATION_SCREENLONG_NO << SHIFT_SCREENLONG,
+ SCREENLONG_YES = ACONFIGURATION_SCREENLONG_YES << SHIFT_SCREENLONG,
};
enum {
// uiMode bits for the mode type.
MASK_UI_MODE_TYPE = 0x0f,
- UI_MODE_TYPE_ANY = 0x00,
- UI_MODE_TYPE_NORMAL = 0x01,
- UI_MODE_TYPE_DESK = 0x02,
- UI_MODE_TYPE_CAR = 0x03,
+ UI_MODE_TYPE_ANY = ACONFIGURATION_UI_MODE_TYPE_ANY,
+ UI_MODE_TYPE_NORMAL = ACONFIGURATION_UI_MODE_TYPE_NORMAL,
+ UI_MODE_TYPE_DESK = ACONFIGURATION_UI_MODE_TYPE_DESK,
+ UI_MODE_TYPE_CAR = ACONFIGURATION_UI_MODE_TYPE_CAR,
// uiMode bits for the night switch.
MASK_UI_MODE_NIGHT = 0x30,
- UI_MODE_NIGHT_ANY = 0x00,
- UI_MODE_NIGHT_NO = 0x10,
- UI_MODE_NIGHT_YES = 0x20,
+ SHIFT_UI_MODE_NIGHT = 4,
+ UI_MODE_NIGHT_ANY = ACONFIGURATION_UI_MODE_NIGHT_ANY << SHIFT_UI_MODE_NIGHT,
+ UI_MODE_NIGHT_NO = ACONFIGURATION_UI_MODE_NIGHT_NO << SHIFT_UI_MODE_NIGHT,
+ UI_MODE_NIGHT_YES = ACONFIGURATION_UI_MODE_NIGHT_YES << SHIFT_UI_MODE_NIGHT,
};
union {
@@ -1023,19 +1028,19 @@ struct ResTable_config
// match the corresponding ones in android.content.pm.ActivityInfo and
// attrs_manifest.xml.
enum {
- CONFIG_MCC = 0x0001,
- CONFIG_MNC = 0x0002,
- CONFIG_LOCALE = 0x0004,
- CONFIG_TOUCHSCREEN = 0x0008,
- CONFIG_KEYBOARD = 0x0010,
- CONFIG_KEYBOARD_HIDDEN = 0x0020,
- CONFIG_NAVIGATION = 0x0040,
- CONFIG_ORIENTATION = 0x0080,
- CONFIG_DENSITY = 0x0100,
- CONFIG_SCREEN_SIZE = 0x0200,
- CONFIG_VERSION = 0x0400,
- CONFIG_SCREEN_LAYOUT = 0x0800,
- CONFIG_UI_MODE = 0x1000
+ CONFIG_MCC = ACONFIGURATION_MCC,
+ CONFIG_MNC = ACONFIGURATION_MCC,
+ CONFIG_LOCALE = ACONFIGURATION_LOCALE,
+ CONFIG_TOUCHSCREEN = ACONFIGURATION_TOUCHSCREEN,
+ CONFIG_KEYBOARD = ACONFIGURATION_KEYBOARD,
+ CONFIG_KEYBOARD_HIDDEN = ACONFIGURATION_KEYBOARD_HIDDEN,
+ CONFIG_NAVIGATION = ACONFIGURATION_NAVIGATION,
+ CONFIG_ORIENTATION = ACONFIGURATION_ORIENTATION,
+ CONFIG_DENSITY = ACONFIGURATION_DENSITY,
+ CONFIG_SCREEN_SIZE = ACONFIGURATION_SCREEN_SIZE,
+ CONFIG_VERSION = ACONFIGURATION_VERSION,
+ CONFIG_SCREEN_LAYOUT = ACONFIGURATION_SCREEN_LAYOUT,
+ CONFIG_UI_MODE = ACONFIGURATION_UI_MODE
};
// Compare two configuration, returning CONFIG_* flags set for each value
diff --git a/include/utils/String8.h b/include/utils/String8.h
index 0b18fe3d22a8..ef0b51a443ec 100644
--- a/include/utils/String8.h
+++ b/include/utils/String8.h
@@ -171,7 +171,8 @@ public:
status_t append(const char* other);
status_t append(const char* other, size_t numChars);
- status_t appendFormat(const char* fmt, ...);
+ status_t appendFormat(const char* fmt, ...)
+ __attribute__((format (printf, 2, 3)));
// Note that this function takes O(N) time to calculate the value.
// No cache value is stored.
@@ -374,7 +375,7 @@ inline String8& String8::operator+=(const String8& other)
inline String8 String8::operator+(const String8& other) const
{
- String8 tmp;
+ String8 tmp(*this);
tmp += other;
return tmp;
}
@@ -387,7 +388,7 @@ inline String8& String8::operator+=(const char* other)
inline String8 String8::operator+(const char* other) const
{
- String8 tmp;
+ String8 tmp(*this);
tmp += other;
return tmp;
}
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 04449645cbeb..1efe6b55384c 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -1,33 +1,41 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES:= \
- FontRenderer.cpp \
- GradientCache.cpp \
- LayerCache.cpp \
- Matrix.cpp \
- OpenGLRenderer.cpp \
- Patch.cpp \
- PatchCache.cpp \
- PathCache.cpp \
- Program.cpp \
- ProgramCache.cpp \
- SkiaColorFilter.cpp \
- SkiaShader.cpp \
- TextureCache.cpp
+# Only build libhwui when USE_OPENGL_RENDERER is
+# defined in the current device/board configuration
+ifeq ($(USE_OPENGL_RENDERER),true)
+ LOCAL_SRC_FILES:= \
+ FontRenderer.cpp \
+ GradientCache.cpp \
+ LayerCache.cpp \
+ Matrix.cpp \
+ OpenGLRenderer.cpp \
+ Patch.cpp \
+ PatchCache.cpp \
+ PathCache.cpp \
+ Program.cpp \
+ ProgramCache.cpp \
+ SkiaColorFilter.cpp \
+ SkiaShader.cpp \
+ TextureCache.cpp
+
+ LOCAL_C_INCLUDES += \
+ $(JNI_H_INCLUDE) \
+ $(LOCAL_PATH)/../../include/utils \
+ external/skia/include/core \
+ external/skia/include/effects \
+ external/skia/include/images \
+ external/skia/src/ports \
+ external/skia/include/utils
-LOCAL_C_INCLUDES += \
- $(JNI_H_INCLUDE) \
- $(LOCAL_PATH)/../../include/utils \
- external/skia/include/core \
- external/skia/include/effects \
- external/skia/include/images \
- external/skia/src/ports \
- external/skia/include/utils
+ LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER
+ LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+ LOCAL_SHARED_LIBRARIES := libcutils libutils libGLESv2 libskia
+ LOCAL_MODULE := libhwui
+ LOCAL_MODULE_TAGS := optional
+ LOCAL_PRELINK_MODULE := false
+
+ include $(BUILD_SHARED_LIBRARY)
-LOCAL_MODULE_CLASS := SHARED_LIBRARIES
-LOCAL_SHARED_LIBRARIES := libcutils libutils libGLESv2 libskia
-LOCAL_MODULE := libhwui
-LOCAL_PRELINK_MODULE := false
-
-include $(BUILD_SHARED_LIBRARY)
+ include $(call all-makefiles-under,$(LOCAL_PATH))
+endif
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index dbea1147b87d..e807aba12a9c 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -69,16 +69,16 @@ void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds
int width = (int) glyph->mBitmapWidth;
int height = (int) glyph->mBitmapHeight;
- if(bounds->bottom > nPenY) {
+ if (bounds->bottom > nPenY) {
bounds->bottom = nPenY;
}
- if(bounds->left > nPenX) {
+ if (bounds->left > nPenX) {
bounds->left = nPenX;
}
- if(bounds->right < nPenX + width) {
+ if (bounds->right < nPenX + width) {
bounds->right = nPenX + width;
}
- if(bounds->top < nPenY + height) {
+ if (bounds->top < nPenY + height) {
bounds->top = nPenY + height;
}
}
@@ -102,7 +102,7 @@ void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
}
void Font::drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
- uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
+ uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
int nPenX = x + glyph->mBitmapLeft;
int nPenY = y + glyph->mBitmapTop;
@@ -116,7 +116,7 @@ void Font::drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
int32_t bX = 0, bY = 0;
for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
- if(bX < 0 || bY < 0 || bX >= (int32_t)bitmapW || bY >= (int32_t)bitmapH) {
+ if (bX < 0 || bY < 0 || bX >= (int32_t)bitmapW || bY >= (int32_t)bitmapH) {
LOGE("Skipping invalid index");
continue;
}
@@ -143,22 +143,19 @@ Font::CachedGlyphInfo* Font::getCachedUTFChar(SkPaint* paint, int32_t utfChar) {
}
void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y,
- uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
- if(bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
- renderUTF(paint, text, start, len, numGlyphs, x, y, BITMAP,
- bitmap, bitmapW, bitmapH, NULL);
- }
- else {
- renderUTF(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER,
- NULL, 0, 0, NULL);
+ int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
+ if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
+ renderUTF(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
+ bitmapW, bitmapH, NULL);
+ } else {
+ renderUTF(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, 0, 0, NULL);
}
}
void Font::measureUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
- int numGlyphs, Rect *bounds) {
- if(bounds == NULL) {
+ int numGlyphs, Rect *bounds) {
+ if (bounds == NULL) {
LOGE("No return rectangle provided to measure text");
return;
}
@@ -167,9 +164,8 @@ void Font::measureUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t
}
void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y, RenderMode mode,
- uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
- Rect *bounds) {
+ int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
+ uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) {
if (numGlyphs == 0 || text == NULL || len == 0) {
return;
}
@@ -185,7 +181,7 @@ void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t
while (glyphsLeft > 0) {
int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text);
- // Reached the end of the string or encountered
+ // Reached the end of the string
if (utfChar < 0) {
break;
}
@@ -292,6 +288,9 @@ FontRenderer::FontRenderer() {
mCurrentQuadIndex = 0;
mTextureId = 0;
+ mTextMeshPtr = NULL;
+ mTextTexture = NULL;
+
mIndexBufferID = 0;
mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
@@ -319,10 +318,12 @@ FontRenderer::~FontRenderer() {
}
mCacheLines.clear();
- delete[] mTextMeshPtr;
- delete[] mTextTexture;
+ if (mInitialized) {
+ delete[] mTextMeshPtr;
+ delete[] mTextTexture;
+ }
- if(mTextureId) {
+ if (mTextureId) {
glDeleteTextures(1, &mTextureId);
}
@@ -417,7 +418,7 @@ void FontRenderer::initTextTexture() {
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// Initialize texture dimentions
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
- GL_ALPHA, GL_UNSIGNED_BYTE, 0);
+ GL_ALPHA, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@@ -522,7 +523,6 @@ void FontRenderer::checkTextureUpdate() {
}
void FontRenderer::issueDrawCommand() {
-
checkTextureUpdate();
float* vtx = mTextMeshPtr;
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index aeda416f24c2..59fa0a7badcd 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -80,8 +80,7 @@ void GradientCache::operator()(SkShader*& shader, Texture*& texture) {
///////////////////////////////////////////////////////////////////////////////
Texture* GradientCache::get(SkShader* shader) {
- Texture* texture = mCache.get(shader);
- return texture;
+ return mCache.get(shader);
}
void GradientCache::remove(SkShader* shader) {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index db8c8637aa67..3c1fe2a8090a 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -160,7 +160,9 @@ OpenGLRenderer::~OpenGLRenderer() {
mTextureCache.clear();
mLayerCache.clear();
mGradientCache.clear();
+ mPathCache.clear();
mPatchCache.clear();
+ mProgramCache.clear();
}
///////////////////////////////////////////////////////////////////////////////
@@ -410,8 +412,14 @@ const Rect& OpenGLRenderer::getClipBounds() {
}
bool OpenGLRenderer::quickReject(float left, float top, float right, float bottom) {
- Rect r(left, top, right, bottom);
- mSnapshot->transform.mapRect(r);
+ SkRect sr;
+ sr.set(left, top, right, bottom);
+
+ SkMatrix m;
+ mSnapshot->transform.copyTo(m);
+ m.mapRect(&sr);
+
+ Rect r(sr.fLeft, sr.fTop, sr.fRight, sr.fBottom);
return !mSnapshot->clipRect.intersects(r);
}
@@ -435,7 +443,9 @@ void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, const S
return;
}
+ glActiveTexture(GL_TEXTURE0);
const Texture* texture = mTextureCache.get(bitmap);
+ if (!texture) return;
const AutoTexture autoCleanup(texture);
drawTextureRect(left, top, right, bottom, texture, paint);
@@ -450,7 +460,9 @@ void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, const SkMatrix* matrix, const
return;
}
+ glActiveTexture(GL_TEXTURE0);
const Texture* texture = mTextureCache.get(bitmap);
+ if (!texture) return;
const AutoTexture autoCleanup(texture);
drawTextureRect(r.left, r.top, r.right, r.bottom, texture, paint);
@@ -464,7 +476,9 @@ void OpenGLRenderer::drawBitmap(SkBitmap* bitmap,
return;
}
+ glActiveTexture(GL_TEXTURE0);
const Texture* texture = mTextureCache.get(bitmap);
+ if (!texture) return;
const AutoTexture autoCleanup(texture);
const float width = texture->width;
@@ -488,7 +502,9 @@ void OpenGLRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
return;
}
+ glActiveTexture(GL_TEXTURE0);
const Texture* texture = mTextureCache.get(bitmap);
+ if (!texture) return;
const AutoTexture autoCleanup(texture);
int alpha;
@@ -534,13 +550,17 @@ void OpenGLRenderer::drawRect(float left, float top, float right, float bottom,
drawColorRect(left, top, right, bottom, color, mode);
}
+#define kStdStrikeThru_Offset (-6.0f / 21.0f)
+#define kStdUnderline_Offset (1.0f / 9.0f)
+#define kStdUnderline_Thickness (1.0f / 18.0f)
+
void OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
float x, float y, SkPaint* paint) {
if (text == NULL || count == 0 || (paint->getAlpha() == 0 && paint->getXfermode() == NULL)) {
return;
}
- float length;
+ float length = -1.0f;
switch (paint->getTextAlign()) {
case SkPaint::kCenter_Align:
length = paint->measureText(text, bytesCount);
@@ -603,13 +623,56 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
mColorFilter->setupProgram(mCurrentProgram);
}
- // TODO: Implement scale properly
const Rect& clip = mSnapshot->getLocalClip();
mFontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()), paint->getTextSize());
mFontRenderer.renderText(paint, &clip, text, 0, bytesCount, count, x, y);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(texCoordsSlot);
+
+ // Handle underline and strike-through
+ uint32_t flags = paint->getFlags();
+ if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
+ float underlineWidth = length;
+ // If length is > 0.0f, we already measured the text for the text alignment
+ if (length <= 0.0f) {
+ underlineWidth = paint->measureText(text, bytesCount);
+ }
+
+ float offsetX = 0;
+ switch (paint->getTextAlign()) {
+ case SkPaint::kCenter_Align:
+ offsetX = underlineWidth * 0.5f;
+ break;
+ case SkPaint::kRight_Align:
+ offsetX = underlineWidth;
+ break;
+ default:
+ break;
+ }
+
+ if (underlineWidth > 0.0f) {
+ float textSize = paint->getTextSize();
+ float height = textSize * kStdUnderline_Thickness;
+
+ float left = x - offsetX;
+ float top = 0.0f;
+ float right = left + underlineWidth;
+ float bottom = 0.0f;
+
+ if (flags & SkPaint::kUnderlineText_Flag) {
+ top = y + textSize * kStdUnderline_Offset;
+ bottom = top + height;
+ drawRect(left, top, right, bottom, paint);
+ }
+
+ if (flags & SkPaint::kStrikeThruText_Flag) {
+ top = y + textSize * kStdStrikeThru_Offset;
+ bottom = top + height;
+ drawRect(left, top, right, bottom, paint);
+ }
+ }
+ }
}
void OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) {
@@ -617,6 +680,7 @@ void OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) {
glActiveTexture(gTextureUnits[textureUnit]);
const PathTexture* texture = mPathCache.get(path, paint);
+ if (!texture) return;
const AutoTexture autoCleanup(texture);
int alpha;
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index fa6ea2591cc8..9a22dc0a41e0 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -34,6 +34,10 @@ PathCache::PathCache(uint32_t maxByteSize):
mCache(GenerationCache<PathCacheEntry, PathTexture*>::kUnlimitedCapacity),
mSize(0), mMaxSize(maxByteSize) {
mCache.setOnEntryRemovedListener(this);
+
+ GLint maxTextureSize;
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
+ mMaxTextureSize = maxTextureSize;
}
PathCache::~PathCache() {
@@ -94,9 +98,18 @@ PathTexture* PathCache::get(SkPath* path, SkPaint* paint) {
PathTexture* PathCache::addTexture(const PathCacheEntry& entry,
const SkPath *path, const SkPaint* paint) {
const SkRect& bounds = path->getBounds();
+
+ const float pathWidth = bounds.width();
+ const float pathHeight = bounds.height();
+
+ if (pathWidth > mMaxTextureSize || pathHeight > mMaxTextureSize) {
+ LOGW("Path too large to be rendered into a texture");
+ return NULL;
+ }
+
const float offset = entry.strokeWidth * 1.5f;
- const uint32_t width = uint32_t(bounds.width() + offset * 2.0 + 0.5);
- const uint32_t height = uint32_t(bounds.height() + offset * 2.0 + 0.5);
+ const uint32_t width = uint32_t(pathWidth + offset * 2.0 + 0.5);
+ const uint32_t height = uint32_t(pathHeight + offset * 2.0 + 0.5);
const uint32_t size = width * height;
// Don't even try to cache a bitmap that's bigger than the cache
@@ -149,7 +162,8 @@ void PathCache::generateTexture(SkBitmap& bitmap, Texture* texture) {
glGenTextures(1, &texture->id);
glBindTexture(GL_TEXTURE_2D, texture->id);
- glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap.bytesPerPixel());
+ // Textures are Alpha8
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
texture->blend = true;
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, bitmap.rowBytesAsPixels(), texture->height, 0,
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index 206fb49ca4e7..d09789f03763 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -139,6 +139,7 @@ private:
uint32_t mSize;
uint32_t mMaxSize;
+ GLuint mMaxTextureSize;
}; // class PathCache
}; // namespace uirenderer
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index dbae38ecb185..6528d9150110 100644
--- a/libs/hwui/Program.cpp
+++ b/libs/hwui/Program.cpp
@@ -51,6 +51,8 @@ Program::Program(const char* vertex, const char* fragment) {
LOGE("Error while linking shaders: %s", log);
delete log;
}
+ glDeleteShader(vertexShader);
+ glDeleteShader(fragmentShader);
glDeleteProgram(id);
}
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 8a97b4cc3ae0..39fe85a740e1 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -137,7 +137,7 @@ const char* gFS_Footer =
// PorterDuff snippets
///////////////////////////////////////////////////////////////////////////////
-const char* gPorterDuff[12] = {
+const char* gBlendOps[18] = {
// Clear
"return vec4(0.0, 0.0, 0.0, 0.0);\n",
// Src
@@ -161,8 +161,26 @@ const char* gPorterDuff[12] = {
// DstAtop
"return vec4(dst.rgb * src.a + (1.0 - dst.a) * src.rgb, src.a);\n",
// Xor
- "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb, 1.0, "
+ "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb, "
"src.a + dst.a - 2.0 * src.a * dst.a);\n",
+ // Add
+ "return min(src + dst, 1.0);\n",
+ // Multiply
+ "return src * dst;\n",
+ // Screen
+ "return src + dst - src * dst;\n",
+ // Overlay
+ "return clamp(vec4(mix("
+ "2.0 * src.rgb * dst.rgb + src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a), "
+ "src.a * dst.a - 2.0 * (dst.a - dst.rgb) * (src.a - src.rgb) + src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a), "
+ "step(dst.a, 2.0 * dst.rgb)), "
+ "src.a + dst.a - src.a * dst.a), 0.0, 1.0);\n",
+ // Darken
+ "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb + "
+ "min(src.rgb * dst.a, dst.rgb * src.a), src.a + dst.a - src.a * dst.a);\n",
+ // Lighten
+ "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb + "
+ "max(src.rgb * dst.a, dst.rgb * src.a), src.a + dst.a - src.a * dst.a);\n",
};
///////////////////////////////////////////////////////////////////////////////
@@ -292,10 +310,10 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
// Generate required functions
if (description.hasGradient && description.hasBitmap) {
- generatePorterDuffBlend(shader, "blendShaders", description.shadersMode);
+ generateBlend(shader, "blendShaders", description.shadersMode);
}
if (description.colorOp == ProgramDescription::kColorBlend) {
- generatePorterDuffBlend(shader, "blendColors", description.colorMode);
+ generateBlend(shader, "blendColors", description.colorMode);
}
if (description.isBitmapNpot) {
generateTextureWrap(shader, description.bitmapWrapS, description.bitmapWrapT);
@@ -354,13 +372,12 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
return shader;
}
-void ProgramCache::generatePorterDuffBlend(String8& shader, const char* name,
- SkXfermode::Mode mode) {
+void ProgramCache::generateBlend(String8& shader, const char* name, SkXfermode::Mode mode) {
shader.append("\nvec4 ");
shader.append(name);
shader.append("(vec4 src, vec4 dst) {\n");
shader.append(" ");
- shader.append(gPorterDuff[mode]);
+ shader.append(gBlendOps[mode]);
shader.append("}\n");
}
@@ -376,6 +393,9 @@ void ProgramCache::generateTextureWrap(String8& shader, GLenum wrapS, GLenum wra
}
shader.append(" return vec2(");
switch (wrapS) {
+ case GL_CLAMP_TO_EDGE:
+ shader.append("texCoords.x");
+ break;
case GL_REPEAT:
shader.append("mod(texCoords.x, 1.0)");
break;
@@ -385,6 +405,9 @@ void ProgramCache::generateTextureWrap(String8& shader, GLenum wrapS, GLenum wra
}
shader.append(", ");
switch (wrapT) {
+ case GL_CLAMP_TO_EDGE:
+ shader.append("texCoords.y");
+ break;
case GL_REPEAT:
shader.append("mod(texCoords.y, 1.0)");
break;
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
index a1a4a0e1b879..fa4b8c4b3e47 100644
--- a/libs/hwui/ProgramCache.h
+++ b/libs/hwui/ProgramCache.h
@@ -57,9 +57,9 @@ namespace uirenderer {
#define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600
#define PROGRAM_KEY_BITMAP_WRAPT_MASK 0x1800
-// Support only the 12 Porter-Duff modes for now
-#define PROGRAM_MAX_XFERMODE 0xC
-#define PROGRAM_XFERMODE_SHADER_SHIFT 24
+// Encode the xfermodes on 6 bits
+#define PROGRAM_MAX_XFERMODE 0x1f
+#define PROGRAM_XFERMODE_SHADER_SHIFT 26
#define PROGRAM_XFERMODE_COLOR_OP_SHIFT 20
#define PROGRAM_BITMAP_WRAPS_SHIFT 9
@@ -177,7 +177,7 @@ private:
Program* generateProgram(const ProgramDescription& description, programid key);
String8 generateVertexShader(const ProgramDescription& description);
String8 generateFragmentShader(const ProgramDescription& description);
- void generatePorterDuffBlend(String8& shader, const char* name, SkXfermode::Mode mode);
+ void generateBlend(String8& shader, const char* name, SkXfermode::Mode mode);
void generateTextureWrap(String8& shader, GLenum wrapS, GLenum wrapT);
void printLongString(const String8& shader) const;
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index ffdb3485b3c0..3569d6a286ea 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -75,11 +75,13 @@ void SkiaShader::bindTexture(GLuint texture, GLenum wrapS, GLenum wrapT, GLuint
SkiaBitmapShader::SkiaBitmapShader(SkBitmap* bitmap, SkShader* key, SkShader::TileMode tileX,
SkShader::TileMode tileY, SkMatrix* matrix, bool blend):
- SkiaShader(kBitmap, key, tileX, tileY, matrix, blend), mBitmap(bitmap) {
+ SkiaShader(kBitmap, key, tileX, tileY, matrix, blend), mBitmap(bitmap), mTexture(NULL) {
}
void SkiaBitmapShader::describe(ProgramDescription& description, const Extensions& extensions) {
const Texture* texture = mTextureCache->get(mBitmap);
+ if (!texture) return;
+ mTexture = texture;
const float width = texture->width;
const float height = texture->height;
@@ -87,7 +89,8 @@ void SkiaBitmapShader::describe(ProgramDescription& description, const Extension
description.hasBitmap = true;
// The driver does not support non-power of two mirrored/repeated
// textures, so do it ourselves
- if (!extensions.hasNPot() && !isPowerOfTwo(width) && !isPowerOfTwo(height)) {
+ if (!extensions.hasNPot() && (!isPowerOfTwo(width) || !isPowerOfTwo(height)) &&
+ (mTileX != SkShader::kClamp_TileMode || mTileY != SkShader::kClamp_TileMode)) {
description.isBitmapNpot = true;
description.bitmapWrapS = gTileModes[mTileX];
description.bitmapWrapT = gTileModes[mTileY];
@@ -98,7 +101,11 @@ void SkiaBitmapShader::setupProgram(Program* program, const mat4& modelView,
const Snapshot& snapshot, GLuint* textureUnit) {
GLuint textureSlot = (*textureUnit)++;
glActiveTexture(gTextureUnitsMap[textureSlot]);
- const Texture* texture = mTextureCache->get(mBitmap);
+
+ const Texture* texture = mTexture;
+ mTexture = NULL;
+ if (!texture) return;
+ const AutoTexture autoCleanup(texture);
const float width = texture->width;
const float height = texture->height;
diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h
index 58f2870ac6c6..d95e3b04a42c 100644
--- a/libs/hwui/SkiaShader.h
+++ b/libs/hwui/SkiaShader.h
@@ -116,6 +116,7 @@ private:
}
SkBitmap* mBitmap;
+ const Texture* mTexture;
}; // struct SkiaBitmapShader
/**
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index 399ae684d962..342e5b157f68 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -96,9 +96,14 @@ public:
bool clip(float left, float top, float right, float bottom, SkRegion::Op op) {
bool clipped = false;
- Rect r(left, top, right, bottom);
- transform.mapRect(r);
+ SkRect sr;
+ sr.set(left, top, right, bottom);
+ SkMatrix m;
+ transform.copyTo(m);
+ m.mapRect(&sr);
+
+ Rect r(sr.fLeft, sr.fTop, sr.fRight, sr.fBottom);
switch (op) {
case SkRegion::kDifference_Op:
break;
@@ -137,8 +142,16 @@ public:
if (flags & Snapshot::kFlagDirtyLocalClip) {
mat4 inverse;
inverse.loadInverse(transform);
- localClip.set(clipRect);
- inverse.mapRect(localClip);
+
+ SkRect sr;
+ sr.set(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
+
+ SkMatrix m;
+ inverse.copyTo(m);
+ m.mapRect(&sr);
+
+ localClip.set(sr.fLeft, sr.fTop, sr.fRight, sr.fBottom);
+
flags &= ~Snapshot::kFlagDirtyLocalClip;
}
return localClip;
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 59903b70a6a1..3f9698db6032 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -31,6 +31,9 @@ TextureCache::TextureCache(uint32_t maxByteSize):
mCache(GenerationCache<SkBitmap*, Texture*>::kUnlimitedCapacity),
mSize(0), mMaxSize(maxByteSize) {
mCache.setOnEntryRemovedListener(this);
+
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
+ LOGD("Maximum texture dimension is %d pixels", mMaxTextureSize);
}
TextureCache::~TextureCache() {
@@ -79,6 +82,11 @@ void TextureCache::operator()(SkBitmap*& bitmap, Texture*& texture) {
Texture* TextureCache::get(SkBitmap* bitmap) {
Texture* texture = mCache.get(bitmap);
if (!texture) {
+ if (bitmap->width() > mMaxTextureSize || bitmap->height() > mMaxTextureSize) {
+ LOGW("Bitmap too large to be uploaded into a texture");
+ return NULL;
+ }
+
const uint32_t size = bitmap->rowBytes() * bitmap->height();
// Don't even try to cache a bitmap that's bigger than the cache
if (size < mMaxSize) {
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index bed11915c581..452716c947ff 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -82,6 +82,7 @@ private:
uint32_t mSize;
uint32_t mMaxSize;
+ GLint mMaxTextureSize;
}; // class TextureCache
}; // namespace uirenderer
diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/vertical_blur.rs b/libs/rs/java/ImageProcessing/src/com/android/rs/image/vertical_blur.rs
index 008c8b58dba6..3c69289ea780 100644
--- a/libs/rs/java/ImageProcessing/src/com/android/rs/image/vertical_blur.rs
+++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/vertical_blur.rs
@@ -8,52 +8,22 @@ void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32
const uchar4 *input = (const uchar4 *)rsGetElementAt(fs->ain, x, 0);
float3 blurredPixel = 0;
- float3 currentPixel = 0;
-
const float *gPtr = fs->gaussian;
if ((y > fs->radius) && (y < (fs->height - fs->radius))) {
const uchar4 *i = input + ((y - fs->radius) * fs->width);
for(int r = -fs->radius; r <= fs->radius; r ++) {
- currentPixel.x = (float)(i->x);
- currentPixel.y = (float)(i->y);
- currentPixel.z = (float)(i->z);
- blurredPixel += currentPixel * gPtr[0];
+ blurredPixel += convert_float3(i->xyz) * gPtr[0];
gPtr++;
i += fs->width;
}
} else {
for(int r = -fs->radius; r <= fs->radius; r ++) {
- #if 1
- int validH = y + r;
- // Clamp to zero and width
- if(validH < 0) {
- validH = 0;
- }
- if(validH > fs->height - 1) {
- validH = fs->height - 1;
- }
-
+ int validH = rsClamp(y + r, (uint)0, (uint)(fs->height - 1));
const uchar4 *i = input + validH * fs->width;
- //const uchar4 *i = (const uchar4 *)rsGetElementAt(fs->ain, x, validH);
-
- currentPixel.x = (float)(i->x);
- currentPixel.y = (float)(i->y);
- currentPixel.z = (float)(i->z);
- blurredPixel += currentPixel * gPtr[0];
+ blurredPixel += convert_float3(i->xyz) * gPtr[0];
gPtr++;
- #else
- int validH = rsClamp(y + r, 0, height - 1);
- validH -= y;
- uchar4 *i = input + validH * width + x;
- blurredPixel.xyz += convert_float3(i->xyz) * gPtr[0];
- gPtr++;
- #endif
}
}
-
- //output->xyz = convert_uchar3(blurredPixel.xyz);
- output->x = (uint8_t)blurredPixel.x;
- output->y = (uint8_t)blurredPixel.y;
- output->z = (uint8_t)blurredPixel.z;
+ output->xyz = convert_uchar3(blurredPixel);
}
diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp
index 6560101e6a89..7d31bd612633 100644
--- a/libs/rs/rsAllocation.cpp
+++ b/libs/rs/rsAllocation.cpp
@@ -234,6 +234,12 @@ void Allocation::data(const void *data, uint32_t sizeBytes)
LOGE("Allocation::data called with mismatched size expected %i, got %i", size, sizeBytes);
return;
}
+
+ if (mType->getElement()->getHasReferences()) {
+ incRefs(data, sizeBytes / mType->getElement()->getSizeBytes());
+ decRefs(mPtr, sizeBytes / mType->getElement()->getSizeBytes());
+ }
+
memcpy(mPtr, data, size);
sendDirty();
mUploadDefered = true;
@@ -256,6 +262,12 @@ void Allocation::subData(uint32_t xoff, uint32_t count, const void *data, uint32
mType->dumpLOGV("type info");
return;
}
+
+ if (mType->getElement()->getHasReferences()) {
+ incRefs(data, count);
+ decRefs(ptr, count);
+ }
+
memcpy(ptr, data, size);
sendDirty();
mUploadDefered = true;
@@ -279,6 +291,10 @@ void Allocation::subData(uint32_t xoff, uint32_t yoff,
for (uint32_t line=yoff; line < (yoff+h); line++) {
uint8_t * ptr = static_cast<uint8_t *>(mPtr);
+ if (mType->getElement()->getHasReferences()) {
+ incRefs(src, w);
+ decRefs(dst, w);
+ }
memcpy(dst, src, lineSize);
src += lineSize;
dst += destW * eSize;
@@ -375,7 +391,8 @@ Allocation *Allocation::createFromStream(Context *rsc, IStream *stream)
alloc->setName(name.string(), name.size());
// Read in all of our allocation data
- stream->loadByteArray(alloc->getPtr(), dataSize);
+ alloc->data(stream->getPtr() + stream->getPos(), dataSize);
+ stream->reset(stream->getPos() + dataSize);
return alloc;
}
@@ -387,6 +404,32 @@ void Allocation::sendDirty() const
}
}
+void Allocation::incRefs(const void *ptr, size_t ct) const
+{
+ const uint8_t *p = static_cast<const uint8_t *>(ptr);
+ const Element *e = mType->getElement();
+ uint32_t stride = e->getSizeBytes();
+
+ while (ct > 0) {
+ e->incRefs(p);
+ ct --;
+ p += stride;
+ }
+}
+
+void Allocation::decRefs(const void *ptr, size_t ct) const
+{
+ const uint8_t *p = static_cast<const uint8_t *>(ptr);
+ const Element *e = mType->getElement();
+ uint32_t stride = e->getSizeBytes();
+
+ while (ct > 0) {
+ e->decRefs(p);
+ ct --;
+ p += stride;
+ }
+}
+
/////////////////
//
diff --git a/libs/rs/rsAllocation.h b/libs/rs/rsAllocation.h
index 8273165c4d86..177d5a411cfc 100644
--- a/libs/rs/rsAllocation.h
+++ b/libs/rs/rsAllocation.h
@@ -81,6 +81,9 @@ public:
bool getIsTexture() const {return mIsTexture;}
bool getIsBufferObject() const {return mIsVertexBuffer;}
+ void incRefs(const void *ptr, size_t ct) const;
+ void decRefs(const void *ptr, size_t ct) const;
+
protected:
void sendDirty() const;
diff --git a/libs/rs/rsComponent.cpp b/libs/rs/rsComponent.cpp
index 8e509ad715d7..fbaa75f68c31 100644
--- a/libs/rs/rsComponent.cpp
+++ b/libs/rs/rsComponent.cpp
@@ -161,6 +161,10 @@ void Component::set(RsDataType dt, RsDataKind dk, bool norm, uint32_t vecSize)
mBits = mTypeBits * mVectorSize;
}
+bool Component::isReference() const
+{
+ return (mType >= RS_TYPE_ELEMENT);
+}
diff --git a/libs/rs/rsComponent.h b/libs/rs/rsComponent.h
index 15fd5ddc1523..a7750511454f 100644
--- a/libs/rs/rsComponent.h
+++ b/libs/rs/rsComponent.h
@@ -51,6 +51,8 @@ public:
void serialize(OStream *stream) const;
void loadFromStream(IStream *stream);
+ bool isReference() const;
+
protected:
RsDataType mType;
RsDataKind mKind;
diff --git a/libs/rs/rsElement.cpp b/libs/rs/rsElement.cpp
index 37b8bd6bd1a6..5dee1fb0ff29 100644
--- a/libs/rs/rsElement.cpp
+++ b/libs/rs/rsElement.cpp
@@ -34,6 +34,7 @@ Element::Element(Context *rsc) : ObjectBase(rsc)
mAllocLine = __LINE__;
mFields = NULL;
mFieldCount = 0;
+ mHasReference = false;
}
@@ -53,6 +54,7 @@ void Element::clear()
delete [] mFields;
mFields = NULL;
mFieldCount = 0;
+ mHasReference = false;
}
size_t Element::getSizeBits() const
@@ -68,15 +70,6 @@ size_t Element::getSizeBits() const
return total;
}
-size_t Element::getFieldOffsetBits(uint32_t componentNumber) const
-{
- size_t offset = 0;
- for (uint32_t ct = 0; ct < componentNumber; ct++) {
- offset += mFields[ct].e->mBits;
- }
- return offset;
-}
-
void Element::dumpLOGV(const char *prefix) const
{
ObjectBase::dumpLOGV(prefix);
@@ -121,14 +114,22 @@ Element *Element::createFromStream(Context *rsc, IStream *stream)
Element *elem = new Element(rsc);
elem->mComponent.loadFromStream(stream);
elem->mBits = elem->mComponent.getBits();
+ elem->mHasReference = elem->mComponent.isReference();
elem->mFieldCount = stream->loadU32();
if(elem->mFieldCount) {
+ uint32_t offset = 0;
elem->mFields = new ElementField_t [elem->mFieldCount];
for(uint32_t ct = 0; ct < elem->mFieldCount; ct ++) {
stream->loadString(&elem->mFields[ct].name);
Element *fieldElem = Element::createFromStream(rsc, stream);
elem->mFields[ct].e.set(fieldElem);
+ elem->mFields[ct].offsetBits = offset;
+ offset += fieldElem->getSizeBits();
+ // Check if our sub-elements have references
+ if(fieldElem->mHasReference) {
+ elem->mHasReference = true;
+ }
}
}
@@ -193,6 +194,7 @@ const Element * Element::create(Context *rsc, RsDataType dt, RsDataKind dk,
Element *e = new Element(rsc);
e->mComponent.set(dt, dk, isNorm, vecSize);
e->mBits = e->mComponent.getBits();
+ e->mHasReference = e->mComponent.isReference();
rsc->mStateElement.mElements.push(e);
return e;
}
@@ -223,9 +225,16 @@ const Element * Element::create(Context *rsc, size_t count, const Element **ein,
Element *e = new Element(rsc);
e->mFields = new ElementField_t [count];
e->mFieldCount = count;
+ size_t bits = 0;
for (size_t ct=0; ct < count; ct++) {
e->mFields[ct].e.set(ein[ct]);
e->mFields[ct].name.setTo(nin[ct], lengths[ct]);
+ e->mFields[ct].offsetBits = bits;
+ bits += ein[ct]->getSizeBits();
+
+ if (ein[ct]->mHasReference) {
+ e->mHasReference = true;
+ }
}
rsc->mStateElement.mElements.push(e);
@@ -251,6 +260,43 @@ String8 Element::getGLSLType(uint32_t indent) const
return s;
}
+void Element::incRefs(const void *ptr) const
+{
+ if (!mFieldCount) {
+ if (mComponent.isReference()) {
+ ObjectBase *const*obp = static_cast<ObjectBase *const*>(ptr);
+ ObjectBase *ob = obp[0];
+ ob->incSysRef();
+ }
+ return;
+ }
+
+ const uint8_t *p = static_cast<const uint8_t *>(ptr);
+ for (uint32_t i=0; i < mFieldCount; i++) {
+ if (mFields[i].e->mHasReference) {
+ mFields[i].e->incRefs(&p[mFields[i].offsetBits >> 3]);
+ }
+ }
+}
+
+void Element::decRefs(const void *ptr) const
+{
+ if (!mFieldCount) {
+ if (mComponent.isReference()) {
+ ObjectBase *const*obp = static_cast<ObjectBase *const*>(ptr);
+ ObjectBase *ob = obp[0];
+ ob->decSysRef();
+ }
+ return;
+ }
+
+ const uint8_t *p = static_cast<const uint8_t *>(ptr);
+ for (uint32_t i=0; i < mFieldCount; i++) {
+ if (mFields[i].e->mHasReference) {
+ mFields[i].e->decRefs(&p[mFields[i].offsetBits >> 3]);
+ }
+ }
+}
ElementState::ElementState()
diff --git a/libs/rs/rsElement.h b/libs/rs/rsElement.h
index 90e7cc8ff053..b5dad7a23c4a 100644
--- a/libs/rs/rsElement.h
+++ b/libs/rs/rsElement.h
@@ -40,9 +40,11 @@ public:
return (getSizeBits() + 7) >> 3;
}
- size_t getFieldOffsetBits(uint32_t componentNumber) const;
+ size_t getFieldOffsetBits(uint32_t componentNumber) const {
+ return mFields[componentNumber].offsetBits;
+ }
size_t getFieldOffsetBytes(uint32_t componentNumber) const {
- return (getFieldOffsetBits(componentNumber) + 7) >> 3;
+ return mFields[componentNumber].offsetBits >> 3;
}
uint32_t getFieldCount() const {return mFieldCount;}
@@ -66,6 +68,10 @@ public:
static const Element * create(Context *rsc, size_t count, const Element **,
const char **, const size_t * lengths);
+ void incRefs(const void *) const;
+ void decRefs(const void *) const;
+ bool getHasReferences() const {return mHasReference;}
+
protected:
// deallocate any components that are part of this element.
void clear();
@@ -73,9 +79,11 @@ protected:
typedef struct {
String8 name;
ObjectBaseRef<const Element> e;
+ uint32_t offsetBits;
} ElementField_t;
ElementField_t *mFields;
size_t mFieldCount;
+ bool mHasReference;
Element(Context *);
diff --git a/libs/rs/scriptc/rs_core.rsh b/libs/rs/scriptc/rs_core.rsh
index 85f3b2529ede..aa9aebcad6de 100644
--- a/libs/rs/scriptc/rs_core.rsh
+++ b/libs/rs/scriptc/rs_core.rsh
@@ -602,6 +602,171 @@ rsMatrixTranspose(rs_matrix2x2 *m) {
m->m[2] = temp;
}
+/////////////////////////////////////////////////////
+// quaternion ops
+/////////////////////////////////////////////////////
+
+static void __attribute__((overloadable))
+rsQuaternionSet(rs_quaternion *q, float w, float x, float y, float z) {
+ q->w = w;
+ q->x = x;
+ q->y = y;
+ q->z = z;
+}
+
+static void __attribute__((overloadable))
+rsQuaternionSet(rs_quaternion *q, const rs_quaternion *rhs) {
+ q->w = rhs->w;
+ q->x = rhs->x;
+ q->y = rhs->y;
+ q->z = rhs->z;
+}
+
+static void __attribute__((overloadable))
+rsQuaternionMultiply(rs_quaternion *q, float s) {
+ q->w *= s;
+ q->x *= s;
+ q->y *= s;
+ q->z *= s;
+}
+
+static void __attribute__((overloadable))
+rsQuaternionMultiply(rs_quaternion *q, const rs_quaternion *rhs) {
+ q->w = -q->x*rhs->x - q->y*rhs->y - q->z*rhs->z + q->w*rhs->w;
+ q->x = q->x*rhs->w + q->y*rhs->z - q->z*rhs->y + q->w*rhs->x;
+ q->y = -q->x*rhs->z + q->y*rhs->w + q->z*rhs->z + q->w*rhs->y;
+ q->z = q->x*rhs->y - q->y*rhs->x + q->z*rhs->w + q->w*rhs->z;
+}
+
+static void
+rsQuaternionAdd(rs_quaternion *q, const rs_quaternion *rhs) {
+ q->w *= rhs->w;
+ q->x *= rhs->x;
+ q->y *= rhs->y;
+ q->z *= rhs->z;
+}
+
+static void
+rsQuaternionLoadRotateUnit(rs_quaternion *q, float rot, float x, float y, float z) {
+ rot *= (float)(M_PI / 180.0f) * 0.5f;
+ float c = cos(rot);
+ float s = sin(rot);
+
+ q->w = c;
+ q->x = x * s;
+ q->y = y * s;
+ q->z = z * s;
+}
+
+static void
+rsQuaternionLoadRotate(rs_quaternion *q, float rot, float x, float y, float z) {
+ const float len = x*x + y*y + z*z;
+ if (len != 1) {
+ const float recipLen = 1.f / sqrt(len);
+ x *= recipLen;
+ y *= recipLen;
+ z *= recipLen;
+ }
+ rsQuaternionLoadRotateUnit(q, rot, x, y, z);
+}
+
+static void
+rsQuaternionConjugate(rs_quaternion *q) {
+ q->x = -q->x;
+ q->y = -q->y;
+ q->z = -q->z;
+}
+
+static float
+rsQuaternionDot(const rs_quaternion *q0, const rs_quaternion *q1) {
+ return q0->w*q1->w + q0->x*q1->x + q0->y*q1->y + q0->z*q1->z;
+}
+
+static void
+rsQuaternionNormalize(rs_quaternion *q) {
+ const float len = rsQuaternionDot(q, q);
+ if (len != 1) {
+ const float recipLen = 1.f / sqrt(len);
+ rsQuaternionMultiply(q, recipLen);
+ }
+}
+
+static void
+rsQuaternionSlerp(rs_quaternion *q, const rs_quaternion *q0, const rs_quaternion *q1, float t) {
+ if(t <= 0.0f) {
+ rsQuaternionSet(q, q0);
+ return;
+ }
+ if(t >= 1.0f) {
+ rsQuaternionSet(q, q1);
+ return;
+ }
+
+ rs_quaternion tempq0, tempq1;
+ rsQuaternionSet(&tempq0, q0);
+ rsQuaternionSet(&tempq1, q1);
+
+ float angle = rsQuaternionDot(q0, q1);
+ if(angle < 0) {
+ rsQuaternionMultiply(&tempq0, -1.0f);
+ angle *= -1.0f;
+ }
+
+ float scale, invScale;
+ if (angle + 1.0f > 0.05f) {
+ if (1.0f - angle >= 0.05f) {
+ float theta = acos(angle);
+ float invSinTheta = 1.0f / sin(theta);
+ scale = sin(theta * (1.0f - t)) * invSinTheta;
+ invScale = sin(theta * t) * invSinTheta;
+ }
+ else {
+ scale = 1.0f - t;
+ invScale = t;
+ }
+ }
+ else {
+ rsQuaternionSet(&tempq1, tempq0.z, -tempq0.y, tempq0.x, -tempq0.w);
+ scale = sin(M_PI * (0.5f - t));
+ invScale = sin(M_PI * t);
+ }
+
+ rsQuaternionSet(q, tempq0.w*scale + tempq1.w*invScale, tempq0.x*scale + tempq1.x*invScale,
+ tempq0.y*scale + tempq1.y*invScale, tempq0.z*scale + tempq1.z*invScale);
+}
+
+static void rsQuaternionGetMatrixUnit(rs_matrix4x4 *m, const rs_quaternion *q) {
+ float x2 = 2.0f * q->x * q->x;
+ float y2 = 2.0f * q->y * q->y;
+ float z2 = 2.0f * q->z * q->z;
+ float xy = 2.0f * q->x * q->y;
+ float wz = 2.0f * q->w * q->z;
+ float xz = 2.0f * q->x * q->z;
+ float wy = 2.0f * q->w * q->y;
+ float wx = 2.0f * q->w * q->x;
+ float yz = 2.0f * q->y * q->z;
+
+ m->m[0] = 1.0f - y2 - z2;
+ m->m[1] = xy - wz;
+ m->m[2] = xz + wy;
+ m->m[3] = 0.0f;
+
+ m->m[4] = xy + wz;
+ m->m[5] = 1.0f - x2 - z2;
+ m->m[6] = yz - wx;
+ m->m[7] = 0.0f;
+
+ m->m[8] = xz - wy;
+ m->m[9] = yz - wx;
+ m->m[10] = 1.0f - x2 - y2;
+ m->m[11] = 0.0f;
+
+ m->m[12] = 0.0f;
+ m->m[13] = 0.0f;
+ m->m[14] = 0.0f;
+ m->m[15] = 1.0f;
+}
+
/////////////////////////////////////////////////////
// int ops
diff --git a/libs/rs/scriptc/rs_types.rsh b/libs/rs/scriptc/rs_types.rsh
index 69e1aedef2f7..ddae7eb1b219 100644
--- a/libs/rs/scriptc/rs_types.rsh
+++ b/libs/rs/scriptc/rs_types.rsh
@@ -67,6 +67,10 @@ typedef struct {
float m[4];
} rs_matrix2x2;
+typedef struct {
+ float w, x, y, z;
+} rs_quaternion;
+
#define RS_PACKED __attribute__((packed, aligned(4)))
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index 661870212ffd..5f5a4ac4e644 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -1080,6 +1080,14 @@ void TrackballInputMapper::applyPolicyAndDispatch(nsecs_t when, int32_t motionEv
1, & pointerId, pointerCoords, mXPrecision, mYPrecision, downTime);
}
+int32_t TrackballInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+ if (scanCode >= BTN_MOUSE && scanCode < BTN_JOYSTICK) {
+ return getEventHub()->getScanCodeState(getDeviceId(), scanCode);
+ } else {
+ return AKEY_STATE_UNKNOWN;
+ }
+}
+
// --- TouchInputMapper ---
diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp
index 60a0d82e255d..e09e755861dc 100644
--- a/libs/utils/AssetManager.cpp
+++ b/libs/utils/AssetManager.cpp
@@ -232,6 +232,12 @@ void AssetManager::setConfiguration(const ResTable_config& config, const char* l
}
}
+void AssetManager::getConfiguration(ResTable_config* outConfig) const
+{
+ AutoMutex _l(mLock);
+ *outConfig = *mConfig;
+}
+
/*
* Open an asset.
*
diff --git a/libs/utils/ObbFile.cpp b/libs/utils/ObbFile.cpp
index fe49300eb25d..adedf0c5d0dc 100644
--- a/libs/utils/ObbFile.cpp
+++ b/libs/utils/ObbFile.cpp
@@ -156,9 +156,9 @@ bool ObbFile::parseObbFile(int fd)
return false;
}
- if (footerSize < kFooterMinSize) {
- LOGW("claimed footer size is too small (%08zx; minimum size is 0x%x)\n",
- footerSize, kFooterMinSize);
+ if (footerSize < (kFooterMinSize - kFooterTagSize)) {
+ LOGW("claimed footer size is too small (0x%zx; minimum size is 0x%x)\n",
+ footerSize, kFooterMinSize - kFooterTagSize);
return false;
}
}
@@ -169,6 +169,8 @@ bool ObbFile::parseObbFile(int fd)
return false;
}
+ mFooterStart = fileOffset;
+
char* scanBuf = (char*)malloc(footerSize);
if (scanBuf == NULL) {
LOGW("couldn't allocate scanBuf: %s\n", strerror(errno));
@@ -293,4 +295,38 @@ bool ObbFile::writeTo(int fd)
return true;
}
+bool ObbFile::removeFrom(const char* filename)
+{
+ int fd;
+ bool success = false;
+
+ fd = ::open(filename, O_RDWR);
+ if (fd < 0) {
+ goto out;
+ }
+ success = removeFrom(fd);
+ close(fd);
+
+out:
+ if (!success) {
+ LOGW("failed to remove signature from %s: %s\n", filename, strerror(errno));
+ }
+ return success;
+}
+
+bool ObbFile::removeFrom(int fd)
+{
+ if (fd < 0) {
+ return false;
+ }
+
+ if (!readFrom(fd)) {
+ return false;
+ }
+
+ ftruncate(fd, mFooterStart);
+
+ return true;
+}
+
}
diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk
index b9f206a9d89f..725de9c5bb8b 100644
--- a/libs/utils/tests/Android.mk
+++ b/libs/utils/tests/Android.mk
@@ -7,7 +7,8 @@ ifneq ($(TARGET_SIMULATOR),true)
# Build the unit tests.
test_src_files := \
ObbFile_test.cpp \
- PollLoop_test.cpp
+ PollLoop_test.cpp \
+ String8_test.cpp
shared_libraries := \
libz \
@@ -41,4 +42,4 @@ $(foreach file,$(test_src_files), \
$(eval include $(BUILD_EXECUTABLE)) \
)
-endif \ No newline at end of file
+endif
diff --git a/libs/utils/tests/String8_test.cpp b/libs/utils/tests/String8_test.cpp
new file mode 100644
index 000000000000..c42c68dcefad
--- /dev/null
+++ b/libs/utils/tests/String8_test.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "String8_test"
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+class String8Test : public testing::Test {
+protected:
+ virtual void SetUp() {
+ }
+
+ virtual void TearDown() {
+ }
+};
+
+TEST_F(String8Test, Cstr) {
+ String8 tmp("Hello, world!");
+
+ EXPECT_STREQ(tmp.string(), "Hello, world!");
+}
+
+TEST_F(String8Test, OperatorPlus) {
+ String8 src1("Hello, ");
+
+ // Test adding String8 + const char*
+ const char* ccsrc2 = "world!";
+ String8 dst1 = src1 + ccsrc2;
+ EXPECT_STREQ(dst1.string(), "Hello, world!");
+ EXPECT_STREQ(src1.string(), "Hello, ");
+ EXPECT_STREQ(ccsrc2, "world!");
+
+ // Test adding String8 + String8
+ String8 ssrc2("world!");
+ String8 dst2 = src1 + ssrc2;
+ EXPECT_STREQ(dst2.string(), "Hello, world!");
+ EXPECT_STREQ(src1.string(), "Hello, ");
+ EXPECT_STREQ(ssrc2.string(), "world!");
+}
+
+TEST_F(String8Test, OperatorPlusEquals) {
+ String8 src1("My voice");
+
+ // Testing String8 += String8
+ String8 src2(" is my passport.");
+ src1 += src2;
+ EXPECT_STREQ(src1.string(), "My voice is my passport.");
+ EXPECT_STREQ(src2.string(), " is my passport.");
+
+ // Adding const char* to the previous string.
+ const char* src3 = " Verify me.";
+ src1 += src3;
+ EXPECT_STREQ(src1.string(), "My voice is my passport. Verify me.");
+ EXPECT_STREQ(src2.string(), " is my passport.");
+ EXPECT_STREQ(src3, " Verify me.");
+}
+
+}
diff --git a/media/java/android/media/AudioEffect.java b/media/java/android/media/AudioEffect.java
index aed29c3379d2..35038fa5ae92 100644
--- a/media/java/android/media/AudioEffect.java
+++ b/media/java/android/media/AudioEffect.java
@@ -101,15 +101,15 @@ public class AudioEffect {
public static final int STATE_INITIALIZED = 1;
// to keep in sync with
- // frameworks/base/media/jni/audioeffect/android_media_AudioEffect.cpp
+ // frameworks/base/include/media/AudioEffect.h
/**
- * Event id for engine state change notification.
+ * Event id for engine control ownership change notification.
*/
- public static final int NATIVE_EVENT_ENABLED_STATUS = 0;
+ public static final int NATIVE_EVENT_CONTROL_STATUS = 0;
/**
- * Event id for engine control ownership change notification.
+ * Event id for engine state change notification.
*/
- public static final int NATIVE_EVENT_CONTROL_STATUS = 1;
+ public static final int NATIVE_EVENT_ENABLED_STATUS = 1;
/**
* Event id for engine parameter change notification.
*/
@@ -795,7 +795,7 @@ public class AudioEffect {
// Interface definitions
// --------------------
/**
- * The OnParameterChangeListener interface defines a method called by the AudioEffect
+ * The OnEnableStatusChangeListener interface defines a method called by the AudioEffect
* when a the enabled state of the effect engine was changed by the controlling application.
*/
public interface OnEnableStatusChangeListener {
@@ -922,7 +922,6 @@ public class AudioEffect {
if (effect == null) {
return;
}
-
if (effect.mNativeEventHandler != null) {
Message m = effect.mNativeEventHandler.obtainMessage(what, arg1,
arg2, obj);
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 9212708e6c13..41d2cc5697d4 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -239,6 +239,9 @@ public class AudioService extends IAudioService.Stub {
// independently change its priority)
private final BroadcastReceiver mMediaButtonReceiver = new MediaButtonBroadcastReceiver();
+ // Used to alter media button redirection when the phone is ringing.
+ private boolean mIsRinging = false;
+
// Devices currently connected
private HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>();
@@ -1956,11 +1959,16 @@ public class AudioService extends IAudioService.Stub {
private final static Object mAudioFocusLock = new Object();
+ private final static Object mRingingLock = new Object();
+
private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
if (state == TelephonyManager.CALL_STATE_RINGING) {
//Log.v(TAG, " CALL_STATE_RINGING");
+ synchronized(mRingingLock) {
+ mIsRinging = true;
+ }
int ringVolume = AudioService.this.getStreamVolume(AudioManager.STREAM_RING);
if (ringVolume > 0) {
requestAudioFocus(AudioManager.STREAM_RING,
@@ -1970,12 +1978,18 @@ public class AudioService extends IAudioService.Stub {
}
} else if (state == TelephonyManager.CALL_STATE_OFFHOOK) {
//Log.v(TAG, " CALL_STATE_OFFHOOK");
+ synchronized(mRingingLock) {
+ mIsRinging = false;
+ }
requestAudioFocus(AudioManager.STREAM_RING,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,
null, null /* both allowed to be null only for this clientId */,
IN_VOICE_COMM_FOCUS_ID /*clientId*/);
} else if (state == TelephonyManager.CALL_STATE_IDLE) {
//Log.v(TAG, " CALL_STATE_IDLE");
+ synchronized(mRingingLock) {
+ mIsRinging = false;
+ }
abandonAudioFocus(null, IN_VOICE_COMM_FOCUS_ID);
}
}
@@ -2243,9 +2257,11 @@ public class AudioService extends IAudioService.Stub {
// if in a call or ringing, do not break the current phone app behavior
// TODO modify this to let the phone app specifically get the RC focus
// add modify the phone app to take advantage of the new API
- if ((getMode() == AudioSystem.MODE_IN_CALL) ||
- (getMode() == AudioSystem.MODE_RINGTONE)) {
- return;
+ synchronized(mRingingLock) {
+ if (mIsRinging || (getMode() == AudioSystem.MODE_IN_CALL) ||
+ (getMode() == AudioSystem.MODE_RINGTONE) ) {
+ return;
+ }
}
synchronized(mRCStack) {
if (!mRCStack.empty()) {
diff --git a/media/java/android/media/MtpDatabase.java b/media/java/android/media/MtpDatabase.java
index 37f9f2c7dd6f..88cce467bce3 100644
--- a/media/java/android/media/MtpDatabase.java
+++ b/media/java/android/media/MtpDatabase.java
@@ -220,6 +220,50 @@ public class MtpDatabase {
return -1;
}
+ private int[] getSupportedPlaybackFormats() {
+ return new int[] {
+ Mtp.Object.FORMAT_ASSOCIATION,
+ Mtp.Object.FORMAT_MP3,
+ Mtp.Object.FORMAT_MPEG,
+ Mtp.Object.FORMAT_EXIF_JPEG,
+ Mtp.Object.FORMAT_TIFF_EP,
+ Mtp.Object.FORMAT_GIF,
+ Mtp.Object.FORMAT_JFIF,
+ Mtp.Object.FORMAT_PNG,
+ Mtp.Object.FORMAT_TIFF,
+ Mtp.Object.FORMAT_WMA,
+ Mtp.Object.FORMAT_OGG,
+ Mtp.Object.FORMAT_AAC,
+ Mtp.Object.FORMAT_MP4_CONTAINER,
+ Mtp.Object.FORMAT_MP2,
+ Mtp.Object.FORMAT_3GP_CONTAINER,
+ Mtp.Object.FORMAT_ABSTRACT_AV_PLAYLIST,
+ Mtp.Object.FORMAT_WPL_PLAYLIST,
+ Mtp.Object.FORMAT_M3U_PLAYLIST,
+ Mtp.Object.FORMAT_PLS_PLAYLIST,
+ };
+ }
+
+ private int[] getSupportedCaptureFormats() {
+ // no capture formats yet
+ return null;
+ }
+
+ private int[] getSupportedObjectProperties(int handle) {
+ return new int[] {
+ Mtp.Object.PROPERTY_STORAGE_ID,
+ Mtp.Object.PROPERTY_OBJECT_FORMAT,
+ Mtp.Object.PROPERTY_OBJECT_SIZE,
+ Mtp.Object.PROPERTY_OBJECT_FILE_NAME,
+ Mtp.Object.PROPERTY_PARENT_OBJECT,
+ };
+ }
+
+ private int[] getSupportedDeviceProperties() {
+ // no device properties yet
+ return null;
+ }
+
private int getObjectProperty(int handle, int property,
long[] outIntValue, char[] outStringValue) {
Log.d(TAG, "getObjectProperty: " + property);
diff --git a/media/jni/android_media_MtpDatabase.cpp b/media/jni/android_media_MtpDatabase.cpp
index d4539fe0f15e..abbea30ff575 100644
--- a/media/jni/android_media_MtpDatabase.cpp
+++ b/media/jni/android_media_MtpDatabase.cpp
@@ -40,6 +40,10 @@ static jmethodID method_beginSendObject;
static jmethodID method_endSendObject;
static jmethodID method_getObjectList;
static jmethodID method_getNumObjects;
+static jmethodID method_getSupportedPlaybackFormats;
+static jmethodID method_getSupportedCaptureFormats;
+static jmethodID method_getSupportedObjectProperties;
+static jmethodID method_getSupportedDeviceProperties;
static jmethodID method_getObjectProperty;
static jmethodID method_getObjectInfo;
static jmethodID method_getObjectFilePath;
@@ -87,6 +91,13 @@ public:
MtpObjectFormat format,
MtpObjectHandle parent);
+ // callee should delete[] the results from these
+ // results can be NULL
+ virtual MtpObjectFormatList* getSupportedPlaybackFormats();
+ virtual MtpObjectFormatList* getSupportedCaptureFormats();
+ virtual MtpObjectPropertyList* getSupportedObjectProperties(MtpObjectFormat format);
+ virtual MtpDevicePropertyList* getSupportedDeviceProperties();
+
virtual MtpResponseCode getObjectProperty(MtpObjectHandle handle,
MtpObjectProperty property,
MtpDataPacket& packet);
@@ -190,6 +201,66 @@ int MyMtpDatabase::getNumObjects(MtpStorageID storageID,
(jint)storageID, (jint)format, (jint)parent);
}
+MtpObjectFormatList* MyMtpDatabase::getSupportedPlaybackFormats() {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jintArray array = (jintArray)env->CallObjectMethod(mDatabase,
+ method_getSupportedPlaybackFormats);
+ if (!array)
+ return NULL;
+ MtpObjectFormatList* list = new MtpObjectFormatList();
+ jint* formats = env->GetIntArrayElements(array, 0);
+ jsize length = env->GetArrayLength(array);
+ for (int i = 0; i < length; i++)
+ list->push(formats[i]);
+ env->ReleaseIntArrayElements(array, formats, 0);
+ return list;
+}
+
+MtpObjectFormatList* MyMtpDatabase::getSupportedCaptureFormats() {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jintArray array = (jintArray)env->CallObjectMethod(mDatabase,
+ method_getSupportedCaptureFormats);
+ if (!array)
+ return NULL;
+ MtpObjectFormatList* list = new MtpObjectFormatList();
+ jint* formats = env->GetIntArrayElements(array, 0);
+ jsize length = env->GetArrayLength(array);
+ for (int i = 0; i < length; i++)
+ list->push(formats[i]);
+ env->ReleaseIntArrayElements(array, formats, 0);
+ return list;
+}
+
+MtpObjectPropertyList* MyMtpDatabase::getSupportedObjectProperties(MtpObjectFormat format) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jintArray array = (jintArray)env->CallObjectMethod(mDatabase,
+ method_getSupportedObjectProperties, (jint)format);
+ if (!array)
+ return NULL;
+ MtpObjectPropertyList* list = new MtpObjectPropertyList();
+ jint* properties = env->GetIntArrayElements(array, 0);
+ jsize length = env->GetArrayLength(array);
+ for (int i = 0; i < length; i++)
+ list->push(properties[i]);
+ env->ReleaseIntArrayElements(array, properties, 0);
+ return list;
+}
+
+MtpDevicePropertyList* MyMtpDatabase::getSupportedDeviceProperties() {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jintArray array = (jintArray)env->CallObjectMethod(mDatabase,
+ method_getSupportedDeviceProperties);
+ if (!array)
+ return NULL;
+ MtpDevicePropertyList* list = new MtpDevicePropertyList();
+ jint* properties = env->GetIntArrayElements(array, 0);
+ jsize length = env->GetArrayLength(array);
+ for (int i = 0; i < length; i++)
+ list->push(properties[i]);
+ env->ReleaseIntArrayElements(array, properties, 0);
+ return list;
+}
+
MtpResponseCode MyMtpDatabase::getObjectProperty(MtpObjectHandle handle,
MtpObjectProperty property,
MtpDataPacket& packet) {
@@ -333,7 +404,7 @@ struct PropertyTableEntry {
static const PropertyTableEntry kPropertyTable[] = {
{ MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32 },
{ MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT32 },
- { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT32 },
+ { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16 },
{ MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR },
{ MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64 },
{ MTP_PROPERTY_DATE_MODIFIED, MTP_TYPE_STR },
@@ -460,6 +531,26 @@ int register_android_media_MtpDatabase(JNIEnv *env)
LOGE("Can't find getNumObjects");
return -1;
}
+ method_getSupportedPlaybackFormats = env->GetMethodID(clazz, "getSupportedPlaybackFormats", "()[I");
+ if (method_getSupportedPlaybackFormats == NULL) {
+ LOGE("Can't find getSupportedPlaybackFormats");
+ return -1;
+ }
+ method_getSupportedCaptureFormats = env->GetMethodID(clazz, "getSupportedCaptureFormats", "()[I");
+ if (method_getSupportedCaptureFormats == NULL) {
+ LOGE("Can't find getSupportedCaptureFormats");
+ return -1;
+ }
+ method_getSupportedObjectProperties = env->GetMethodID(clazz, "getSupportedObjectProperties", "(I)[I");
+ if (method_getSupportedObjectProperties == NULL) {
+ LOGE("Can't find getSupportedObjectProperties");
+ return -1;
+ }
+ method_getSupportedDeviceProperties = env->GetMethodID(clazz, "getSupportedDeviceProperties", "()[I");
+ if (method_getSupportedDeviceProperties == NULL) {
+ LOGE("Can't find getSupportedDeviceProperties");
+ return -1;
+ }
method_getObjectProperty = env->GetMethodID(clazz, "getObjectProperty", "(II[J[C)I");
if (method_getObjectProperty == NULL) {
LOGE("Can't find getObjectProperty");
diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp
index 3cdf48a4d51b..0f3e24532631 100644
--- a/media/libmedia/AudioEffect.cpp
+++ b/media/libmedia/AudioEffect.cpp
@@ -218,7 +218,7 @@ status_t AudioEffect::setEnabled(bool enabled)
return mIEffect->disable();
}
}
- return INVALID_OPERATION;
+ return NO_ERROR;
}
status_t AudioEffect::command(uint32_t cmdCode,
@@ -231,7 +231,22 @@ status_t AudioEffect::command(uint32_t cmdCode,
return INVALID_OPERATION;
}
- return mIEffect->command(cmdCode, cmdSize, cmdData, replySize, replyData);
+ status_t status = mIEffect->command(cmdCode, cmdSize, cmdData, replySize, replyData);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ status = *(status_t *)replyData;
+ if (status != NO_ERROR) {
+ return status;
+ }
+
+ if (cmdCode == EFFECT_CMD_ENABLE) {
+ android_atomic_or(1, &mEnabled);
+ }
+ if (cmdCode == EFFECT_CMD_DISABLE) {
+ android_atomic_and(~1, &mEnabled);
+ }
+ return status;
}
@@ -347,7 +362,11 @@ void AudioEffect::enableStatusChanged(bool enabled)
{
LOGV("enableStatusChanged %p enabled %d mCbf %p", this, enabled, mCbf);
if (mStatus == ALREADY_EXISTS) {
- mEnabled = enabled;
+ if (enabled) {
+ android_atomic_or(1, &mEnabled);
+ } else {
+ android_atomic_and(~1, &mEnabled);
+ }
if (mCbf) {
mCbf(EVENT_ENABLE_STATUS_CHANGED, mUserData, &enabled);
}
diff --git a/media/libstagefright/AMRExtractor.cpp b/media/libstagefright/AMRExtractor.cpp
index 9fc1d27938d1..70af2da4850e 100644
--- a/media/libstagefright/AMRExtractor.cpp
+++ b/media/libstagefright/AMRExtractor.cpp
@@ -263,6 +263,7 @@ status_t AMRSource::read(
buffer->set_range(0, frameSize);
buffer->meta_data()->setInt64(kKeyTime, mCurrentTimeUs);
+ buffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
mOffset += frameSize;
mCurrentTimeUs += 20000; // Each frame is 20ms
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 77a14766bd06..404762fd1886 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -81,6 +81,7 @@ LOCAL_STATIC_LIBRARIES := \
libstagefright_httplive \
libstagefright_rtsp \
libstagefright_id3 \
+ libstagefright_g711dec \
LOCAL_SHARED_LIBRARIES += \
libstagefright_amrnb_common \
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index e426fca16314..efdad4362a2e 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -854,18 +854,6 @@ void AwesomePlayer::setVideoSource(sp<MediaSource> source) {
status_t AwesomePlayer::initVideoDecoder() {
uint32_t flags = 0;
-#if 0
- if (mRTPSession != NULL) {
- // XXX hack.
-
- const char *mime;
- CHECK(mVideoTrack->getFormat()->findCString(kKeyMIMEType, &mime));
- if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
- flags |= OMXCodec::kPreferSoftwareCodecs;
- }
- }
-#endif
-
mVideoSource = OMXCodec::Create(
mClient.interface(), mVideoTrack->getFormat(),
false, // createEncoder
@@ -1019,6 +1007,12 @@ void AwesomePlayer::onVideoEvent() {
int64_t latenessUs = nowUs - timeUs;
+ if (mRTPSession != NULL) {
+ // We'll completely ignore timestamps for gtalk videochat
+ // and we'll play incoming video as fast as we get it.
+ latenessUs = 0;
+ }
+
if (latenessUs > 40000) {
// We're more than 40ms late.
LOGV("we're late by %lld us (%.2f secs)", latenessUs, latenessUs / 1E6);
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 5e7dd5c1a38f..9ccd140a19a0 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -260,6 +260,7 @@ void CameraSource::releaseOneRecordingFrame(const sp<IMemory>& frame) {
void CameraSource::signalBufferReturned(MediaBuffer *buffer) {
LOGV("signalBufferReturned: %p", buffer->data());
+ Mutex::Autolock autoLock(mLock);
for (List<sp<IMemory> >::iterator it = mFramesBeingEncoded.begin();
it != mFramesBeingEncoded.end(); ++it) {
if ((*it)->pointer() == buffer->data()) {
@@ -327,6 +328,7 @@ status_t CameraSource::read(
(*buffer)->setObserver(this);
(*buffer)->add_ref();
(*buffer)->meta_data()->setInt64(kKeyTime, frameTime);
+
return OK;
}
}
diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp
index 23d8f5629821..ba99501c6e0e 100644
--- a/media/libstagefright/CameraSourceTimeLapse.cpp
+++ b/media/libstagefright/CameraSourceTimeLapse.cpp
@@ -149,10 +149,10 @@ void CameraSourceTimeLapse::threadTimeLapseEntry() {
LOGV("threadTimeLapseEntry: taking picture");
CHECK_EQ(OK, mCamera->takePicture());
mCameraIdle = false;
- sleep(mTimeBetweenTimeLapseFrameCaptureUs/1E6);
+ usleep(mTimeBetweenTimeLapseFrameCaptureUs);
} else {
LOGV("threadTimeLapseEntry: camera busy with old takePicture. Sleeping a little.");
- sleep(.01);
+ usleep(1E4);
}
}
}
@@ -187,6 +187,7 @@ void CameraSourceTimeLapse::stopCameraRecording() {
if (mUseStillCameraForTimeLapse) {
void *dummy;
pthread_join(mThreadTimeLapse, &dummy);
+ CHECK_EQ(OK, mCamera->startPreview());
} else {
mCamera->stopRecording();
}
diff --git a/media/libstagefright/HTTPStream.cpp b/media/libstagefright/HTTPStream.cpp
index 9c99866f9e02..ccc6a340fca6 100644
--- a/media/libstagefright/HTTPStream.cpp
+++ b/media/libstagefright/HTTPStream.cpp
@@ -68,7 +68,7 @@ status_t HTTPStream::connect(const char *server, int port) {
return UNKNOWN_ERROR;
}
- setReceiveTimeout(5); // Time out reads after 5 secs by default
+ setReceiveTimeout(30); // Time out reads after 30 secs by default
mState = CONNECTING;
@@ -158,7 +158,7 @@ status_t HTTPStream::send(const char *data) {
// The workaround accepts both behaviours but could potentially break
// legitimate responses that use a single newline to "fold" headers, which is
// why it's not yet on by default.
-#define WORKAROUND_FOR_MISSING_CR 0
+#define WORKAROUND_FOR_MISSING_CR 1
status_t HTTPStream::receive_line(char *line, size_t size) {
if (mState != CONNECTED) {
diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp
index 2248e237d447..4058fbc2c9e0 100644
--- a/media/libstagefright/MP3Extractor.cpp
+++ b/media/libstagefright/MP3Extractor.cpp
@@ -683,6 +683,7 @@ status_t MP3Source::read(
buffer->set_range(0, frame_size);
buffer->meta_data()->setInt64(kKeyTime, mCurrentTimeUs);
+ buffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
mCurrentPos += frame_size;
mCurrentTimeUs += frame_size * 8000ll / bitrate;
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 7cea6298c29a..6af3a7fd369e 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -492,7 +492,6 @@ status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
case FOURCC('m', 'o', 'o', 'f'):
case FOURCC('t', 'r', 'a', 'f'):
case FOURCC('m', 'f', 'r', 'a'):
- case FOURCC('s', 'k', 'i' ,'p'):
case FOURCC('u', 'd', 't', 'a'):
case FOURCC('i', 'l', 's', 't'):
{
@@ -1552,13 +1551,14 @@ status_t MPEG4Source::read(
off_t offset;
size_t size;
uint32_t dts;
+ bool isSyncSample;
bool newBuffer = false;
if (mBuffer == NULL) {
newBuffer = true;
status_t err =
mSampleTable->getMetaDataForSample(
- mCurrentSampleIndex, &offset, &size, &dts);
+ mCurrentSampleIndex, &offset, &size, &dts, &isSyncSample);
if (err != OK) {
return err;
@@ -1595,6 +1595,10 @@ status_t MPEG4Source::read(
kKeyTargetTime, targetSampleTimeUs);
}
+ if (isSyncSample) {
+ mBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
+ }
+
++mCurrentSampleIndex;
}
@@ -1697,6 +1701,10 @@ status_t MPEG4Source::read(
kKeyTargetTime, targetSampleTimeUs);
}
+ if (isSyncSample) {
+ mBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
+ }
+
++mCurrentSampleIndex;
*out = mBuffer;
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 0d8c3c6afa16..c860c5c50eb3 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -38,6 +38,9 @@
namespace android {
+static const uint8_t kNalUnitTypeSeqParamSet = 0x07;
+static const uint8_t kNalUnitTypePicParamSet = 0x08;
+
class MPEG4Writer::Track {
public:
Track(MPEG4Writer *owner, const sp<MediaSource> &source);
@@ -111,6 +114,20 @@ private:
};
List<SttsTableEntry> mSttsTableEntries;
+ // Sequence parameter set or picture parameter set
+ struct AVCParamSet {
+ AVCParamSet(uint16_t length, const uint8_t *data)
+ : mLength(length), mData(data) {}
+
+ uint16_t mLength;
+ const uint8_t *mData;
+ };
+ List<AVCParamSet> mSeqParamSets;
+ List<AVCParamSet> mPicParamSets;
+ uint8_t mProfileIdc;
+ uint8_t mProfileCompatible;
+ uint8_t mLevelIdc;
+
void *mCodecSpecificData;
size_t mCodecSpecificDataSize;
bool mGotAllCodecSpecificData;
@@ -124,8 +141,15 @@ private:
static void *ThreadWrapper(void *me);
void threadEntry();
+ const uint8_t *parseParamSet(
+ const uint8_t *data, size_t length, int type, size_t *paramSetLen);
+
status_t makeAVCCodecSpecificData(
const uint8_t *data, size_t size);
+ status_t copyAVCCodecSpecificData(
+ const uint8_t *data, size_t size);
+ status_t parseAVCCodecSpecificData(
+ const uint8_t *data, size_t size);
// Track authoring progress status
void trackProgressStatus(int64_t timeUs, status_t err = OK);
@@ -1038,6 +1062,174 @@ static void hexdump(const void *_data, size_t size) {
}
}
+static void getNalUnitType(uint8_t byte, uint8_t* type) {
+ LOGV("getNalUnitType: %d", byte);
+
+ // nal_unit_type: 5-bit unsigned integer
+ *type = (byte & 0x1F);
+}
+
+static const uint8_t *findNextStartCode(
+ const uint8_t *data, size_t length) {
+
+ LOGV("findNextStartCode: %p %d", data, length);
+
+ size_t bytesLeft = length;
+ while (bytesLeft > 4 &&
+ memcmp("\x00\x00\x00\x01", &data[length - bytesLeft], 4)) {
+ --bytesLeft;
+ }
+ if (bytesLeft <= 4) {
+ bytesLeft = 0; // Last parameter set
+ }
+ return &data[length - bytesLeft];
+}
+
+const uint8_t *MPEG4Writer::Track::parseParamSet(
+ const uint8_t *data, size_t length, int type, size_t *paramSetLen) {
+
+ LOGV("parseParamSet");
+ CHECK(type == kNalUnitTypeSeqParamSet ||
+ type == kNalUnitTypePicParamSet);
+
+ const uint8_t *nextStartCode = findNextStartCode(data, length);
+ *paramSetLen = nextStartCode - data;
+ if (*paramSetLen == 0) {
+ LOGE("Param set is malformed, since its length is 0");
+ return NULL;
+ }
+
+ AVCParamSet paramSet(*paramSetLen, data);
+ if (type == kNalUnitTypeSeqParamSet) {
+ if (*paramSetLen < 4) {
+ LOGE("Seq parameter set malformed");
+ return NULL;
+ }
+ if (mSeqParamSets.empty()) {
+ mProfileIdc = data[1];
+ mProfileCompatible = data[2];
+ mLevelIdc = data[3];
+ } else {
+ if (mProfileIdc != data[1] ||
+ mProfileCompatible != data[2] ||
+ mLevelIdc != data[3]) {
+ LOGE("Inconsistent profile/level found in seq parameter sets");
+ return NULL;
+ }
+ }
+ mSeqParamSets.push_back(paramSet);
+ } else {
+ mPicParamSets.push_back(paramSet);
+ }
+ return nextStartCode;
+}
+
+status_t MPEG4Writer::Track::copyAVCCodecSpecificData(
+ const uint8_t *data, size_t size) {
+ LOGV("copyAVCCodecSpecificData");
+
+ // 2 bytes for each of the parameter set length field
+ // plus the 7 bytes for the header
+ if (size < 4 + 7) {
+ LOGE("Codec specific data length too short: %d", size);
+ return ERROR_MALFORMED;
+ }
+
+ mCodecSpecificDataSize = size;
+ mCodecSpecificData = malloc(size);
+ memcpy(mCodecSpecificData, data, size);
+ return OK;
+}
+
+status_t MPEG4Writer::Track::parseAVCCodecSpecificData(
+ const uint8_t *data, size_t size) {
+
+ LOGV("parseAVCCodecSpecificData");
+ // Data starts with a start code.
+ // SPS and PPS are separated with start codes.
+ // Also, SPS must come before PPS
+ uint8_t type = kNalUnitTypeSeqParamSet;
+ bool gotSps = false;
+ bool gotPps = false;
+ const uint8_t *tmp = data;
+ const uint8_t *nextStartCode = data;
+ size_t bytesLeft = size;
+ size_t paramSetLen = 0;
+ mCodecSpecificDataSize = 0;
+ while (bytesLeft > 4 && !memcmp("\x00\x00\x00\x01", tmp, 4)) {
+ getNalUnitType(*(tmp + 4), &type);
+ if (type == kNalUnitTypeSeqParamSet) {
+ if (gotPps) {
+ LOGE("SPS must come before PPS");
+ return ERROR_MALFORMED;
+ }
+ if (!gotSps) {
+ gotSps = true;
+ }
+ nextStartCode = parseParamSet(tmp + 4, bytesLeft - 4, type, &paramSetLen);
+ } else if (type == kNalUnitTypePicParamSet) {
+ if (!gotSps) {
+ LOGE("SPS must come before PPS");
+ return ERROR_MALFORMED;
+ }
+ if (!gotPps) {
+ gotPps = true;
+ }
+ nextStartCode = parseParamSet(tmp + 4, bytesLeft - 4, type, &paramSetLen);
+ } else {
+ LOGE("Only SPS and PPS Nal units are expected");
+ return ERROR_MALFORMED;
+ }
+
+ if (nextStartCode == NULL) {
+ return ERROR_MALFORMED;
+ }
+
+ // Move on to find the next parameter set
+ bytesLeft -= nextStartCode - tmp;
+ tmp = nextStartCode;
+ mCodecSpecificDataSize += (2 + paramSetLen);
+ }
+
+ {
+ // Check on the number of seq parameter sets
+ size_t nSeqParamSets = mSeqParamSets.size();
+ if (nSeqParamSets == 0) {
+ LOGE("Cound not find sequence parameter set");
+ return ERROR_MALFORMED;
+ }
+
+ if (nSeqParamSets > 0x1F) {
+ LOGE("Too many seq parameter sets (%d) found", nSeqParamSets);
+ return ERROR_MALFORMED;
+ }
+ }
+
+ {
+ // Check on the number of pic parameter sets
+ size_t nPicParamSets = mPicParamSets.size();
+ if (nPicParamSets == 0) {
+ LOGE("Cound not find picture parameter set");
+ return ERROR_MALFORMED;
+ }
+ if (nPicParamSets > 0xFF) {
+ LOGE("Too many pic parameter sets (%d) found", nPicParamSets);
+ return ERROR_MALFORMED;
+ }
+ }
+
+ {
+ // Check on the profiles
+ // These profiles requires additional parameter set extensions
+ if (mProfileIdc == 100 || mProfileIdc == 110 ||
+ mProfileIdc == 122 || mProfileIdc == 144) {
+ LOGE("Sorry, no support for profile_idc: %d!", mProfileIdc);
+ return BAD_VALUE;
+ }
+ }
+
+ return OK;
+}
status_t MPEG4Writer::Track::makeAVCCodecSpecificData(
const uint8_t *data, size_t size) {
@@ -1048,50 +1240,67 @@ status_t MPEG4Writer::Track::makeAVCCodecSpecificData(
return ERROR_MALFORMED;
}
- if (size < 4 || memcmp("\x00\x00\x00\x01", data, 4)) {
- LOGE("Must start with a start code");
+ if (size < 4) {
+ LOGE("Codec specific data length too short: %d", size);
return ERROR_MALFORMED;
}
- size_t picParamOffset = 4;
- while (picParamOffset + 3 < size
- && memcmp("\x00\x00\x00\x01", &data[picParamOffset], 4)) {
- ++picParamOffset;
+ // Data is in the form of AVCCodecSpecificData
+ if (memcmp("\x00\x00\x00\x01", data, 4)) {
+ return copyAVCCodecSpecificData(data, size);
}
- if (picParamOffset + 3 >= size) {
- LOGE("Could not find start-code for pictureParameterSet");
+ if (parseAVCCodecSpecificData(data, size) != OK) {
return ERROR_MALFORMED;
}
- size_t seqParamSetLength = picParamOffset - 4;
- size_t picParamSetLength = size - picParamOffset - 4;
-
- mCodecSpecificDataSize =
- 6 + 1 + seqParamSetLength + 2 + picParamSetLength + 2;
-
+ // ISO 14496-15: AVC file format
+ mCodecSpecificDataSize += 7; // 7 more bytes in the header
mCodecSpecificData = malloc(mCodecSpecificDataSize);
uint8_t *header = (uint8_t *)mCodecSpecificData;
- header[0] = 1;
- header[1] = 0x42; // profile
- header[2] = 0x80;
- header[3] = 0x1e; // level
+ header[0] = 1; // version
+ header[1] = mProfileIdc; // profile indication
+ header[2] = mProfileCompatible; // profile compatibility
+ header[3] = mLevelIdc;
+ // 6-bit '111111' followed by 2-bit to lengthSizeMinuusOne
#if USE_NALLEN_FOUR
header[4] = 0xfc | 3; // length size == 4 bytes
#else
header[4] = 0xfc | 1; // length size == 2 bytes
#endif
- header[5] = 0xe0 | 1;
- header[6] = seqParamSetLength >> 8;
- header[7] = seqParamSetLength & 0xff;
- memcpy(&header[8], &data[4], seqParamSetLength);
- header += 8 + seqParamSetLength;
- header[0] = 1;
- header[1] = picParamSetLength >> 8;
- header[2] = picParamSetLength & 0xff;
- memcpy(&header[3], &data[picParamOffset + 4], picParamSetLength);
+ // 3-bit '111' followed by 5-bit numSequenceParameterSets
+ int nSequenceParamSets = mSeqParamSets.size();
+ header[5] = 0xe0 | nSequenceParamSets;
+ header += 6;
+ for (List<AVCParamSet>::iterator it = mSeqParamSets.begin();
+ it != mSeqParamSets.end(); ++it) {
+ // 16-bit sequence parameter set length
+ uint16_t seqParamSetLength = it->mLength;
+ header[0] = seqParamSetLength >> 8;
+ header[1] = seqParamSetLength & 0xff;
+
+ // SPS NAL unit (sequence parameter length bytes)
+ memcpy(&header[2], it->mData, seqParamSetLength);
+ header += (2 + seqParamSetLength);
+ }
+
+ // 8-bit nPictureParameterSets
+ int nPictureParamSets = mPicParamSets.size();
+ header[0] = nPictureParamSets;
+ header += 1;
+ for (List<AVCParamSet>::iterator it = mPicParamSets.begin();
+ it != mPicParamSets.end(); ++it) {
+ // 16-bit picture parameter set length
+ uint16_t picParamSetLength = it->mLength;
+ header[0] = picParamSetLength >> 8;
+ header[1] = picParamSetLength & 0xff;
+
+ // PPS Nal unit (picture parameter set length bytes)
+ memcpy(&header[2], it->mData, picParamSetLength);
+ header += (2 + picParamSetLength);
+ }
return OK;
}
@@ -1168,91 +1377,6 @@ void MPEG4Writer::Track::threadEntry() {
mGotAllCodecSpecificData = true;
continue;
- } else if (!mGotAllCodecSpecificData &&
- count == 1 && mIsMPEG4 && mCodecSpecificData == NULL) {
- // The TI mpeg4 encoder does not properly set the
- // codec-specific-data flag.
-
- 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;
- }
-
- // CHECK(offset + 3 < size);
- if (offset + 3 >= size) {
- // XXX assume the entire first chunk of data is the codec specific
- // data.
- offset = size;
- }
-
- mCodecSpecificDataSize = offset;
- mCodecSpecificData = malloc(offset);
- memcpy(mCodecSpecificData, data, offset);
-
- buffer->set_range(buffer->range_offset() + offset, size - offset);
-
- if (size == offset) {
- buffer->release();
- buffer = NULL;
-
- continue;
- }
-
- mGotAllCodecSpecificData = true;
- } else if (!mGotAllCodecSpecificData && mIsAvc && count < 3) {
- // The TI video encoder does not flag codec specific data
- // as such and also splits up SPS and PPS across two buffers.
-
- const uint8_t *data =
- (const uint8_t *)buffer->data() + buffer->range_offset();
-
- size_t size = buffer->range_length();
-
- CHECK(count == 2 || mCodecSpecificData == NULL);
-
- size_t offset = mCodecSpecificDataSize;
- mCodecSpecificDataSize += size + 4;
- mCodecSpecificData =
- realloc(mCodecSpecificData, mCodecSpecificDataSize);
-
- memcpy((uint8_t *)mCodecSpecificData + offset,
- "\x00\x00\x00\x01", 4);
-
- memcpy((uint8_t *)mCodecSpecificData + offset + 4, data, size);
-
- buffer->release();
- buffer = NULL;
-
- if (count == 2) {
- void *tmp = mCodecSpecificData;
- size = mCodecSpecificDataSize;
- mCodecSpecificData = NULL;
- mCodecSpecificDataSize = 0;
-
- status_t err = makeAVCCodecSpecificData(
- (const uint8_t *)tmp, size);
- free(tmp);
- tmp = NULL;
- CHECK_EQ(OK, err);
-
- mGotAllCodecSpecificData = true;
- }
-
- continue;
- }
-
- if (!mGotAllCodecSpecificData) {
- mGotAllCodecSpecificData = true;
}
// Make a deep copy of the MediaBuffer and Metadata and release
@@ -1753,6 +1877,8 @@ void MPEG4Writer::Track::writeTrackHeader(
mOwner->writeInt32(samplerate << 16);
if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
mOwner->beginBox("esds");
+ CHECK(mCodecSpecificData);
+ CHECK(mCodecSpecificDataSize > 0);
mOwner->writeInt32(0); // version=0, flags=0
mOwner->writeInt8(0x03); // ES_DescrTag
@@ -1830,6 +1956,8 @@ void MPEG4Writer::Track::writeTrackHeader(
mOwner->writeInt16(0x18); // depth
mOwner->writeInt16(-1); // predefined
+ CHECK(mCodecSpecificData);
+ CHECK(mCodecSpecificDataSize > 0);
CHECK(23 + mCodecSpecificDataSize < 128);
if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
@@ -1877,6 +2005,8 @@ void MPEG4Writer::Track::writeTrackHeader(
mOwner->endBox(); // d263
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
+ CHECK(mCodecSpecificData);
+ CHECK(mCodecSpecificDataSize > 0);
mOwner->beginBox("avcC");
mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
mOwner->endBox(); // avcC
diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp
index 39d264c9c0db..7648d427341d 100644
--- a/media/libstagefright/MediaDefs.cpp
+++ b/media/libstagefright/MediaDefs.cpp
@@ -32,6 +32,8 @@ const char *MEDIA_MIMETYPE_AUDIO_MPEG = "audio/mpeg";
const char *MEDIA_MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
const char *MEDIA_MIMETYPE_AUDIO_QCELP = "audio/qcelp";
const char *MEDIA_MIMETYPE_AUDIO_VORBIS = "audio/vorbis";
+const char *MEDIA_MIMETYPE_AUDIO_G711_ALAW = "audio/g711-alaw";
+const char *MEDIA_MIMETYPE_AUDIO_G711_MLAW = "audio/g711-mlaw";
const char *MEDIA_MIMETYPE_AUDIO_RAW = "audio/raw";
const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mpeg4";
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 4f3bffd94c67..4741b1d84e80 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -26,6 +26,7 @@
#include "include/AMRWBEncoder.h"
#include "include/AVCDecoder.h"
#include "include/AVCEncoder.h"
+#include "include/G711Decoder.h"
#include "include/M4vH263Decoder.h"
#include "include/M4vH263Encoder.h"
#include "include/MP3Decoder.h"
@@ -77,6 +78,7 @@ FACTORY_CREATE(AMRNBDecoder)
FACTORY_CREATE(AMRWBDecoder)
FACTORY_CREATE(AACDecoder)
FACTORY_CREATE(AVCDecoder)
+FACTORY_CREATE(G711Decoder)
FACTORY_CREATE(M4vH263Decoder)
FACTORY_CREATE(VorbisDecoder)
FACTORY_CREATE(VPXDecoder)
@@ -124,6 +126,7 @@ static sp<MediaSource> InstantiateSoftwareCodec(
FACTORY_REF(AMRWBDecoder)
FACTORY_REF(AACDecoder)
FACTORY_REF(AVCDecoder)
+ FACTORY_REF(G711Decoder)
FACTORY_REF(M4vH263Decoder)
FACTORY_REF(VorbisDecoder)
FACTORY_REF(VPXDecoder)
@@ -155,6 +158,8 @@ static const CodecInfo kDecoderInfo[] = {
{ MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.decode" },
{ MEDIA_MIMETYPE_AUDIO_AAC, "AACDecoder" },
// { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.PV.aacdec" },
+ { MEDIA_MIMETYPE_AUDIO_G711_ALAW, "G711Decoder" },
+ { MEDIA_MIMETYPE_AUDIO_G711_MLAW, "G711Decoder" },
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.7x30.video.decoder.mpeg4" },
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.decoder.mpeg4" },
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.Decoder" },
@@ -1680,6 +1685,14 @@ void OMXCodec::on_message(const omx_message &msg) {
MediaBuffer *buffer = info->mMediaBuffer;
+ if (msg.u.extended_buffer_data.range_offset
+ + msg.u.extended_buffer_data.range_length
+ > buffer->size()) {
+ CODEC_LOGE(
+ "Codec lied about its buffer size requirements, "
+ "sending a buffer larger than the originally "
+ "advertised size in FILL_BUFFER_DONE!");
+ }
buffer->set_range(
msg.u.extended_buffer_data.range_offset,
msg.u.extended_buffer_data.range_length);
diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp
index 641a876288ea..b699d8f51e6b 100644
--- a/media/libstagefright/OggExtractor.cpp
+++ b/media/libstagefright/OggExtractor.cpp
@@ -181,6 +181,8 @@ status_t OggSource::read(
}
#endif
+ packet->meta_data()->setInt32(kKeyIsSyncFrame, 1);
+
*out = packet;
return OK;
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index 2e62f9ff136b..27faf4fbf5d2 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -55,6 +55,8 @@ SampleTable::SampleTable(const sp<DataSource> &source)
mTimeToSample(NULL),
mSyncSampleOffset(-1),
mNumSyncSamples(0),
+ mSyncSamples(NULL),
+ mLastSyncSampleIndex(0),
mSampleToChunkEntries(NULL) {
mSampleIterator = new SampleIterator(this);
}
@@ -63,6 +65,9 @@ SampleTable::~SampleTable() {
delete[] mSampleToChunkEntries;
mSampleToChunkEntries = NULL;
+ delete[] mSyncSamples;
+ mSyncSamples = NULL;
+
delete[] mTimeToSample;
mTimeToSample = NULL;
@@ -278,6 +283,18 @@ status_t SampleTable::setSyncSampleParams(off_t data_offset, size_t data_size) {
if (mNumSyncSamples < 2) {
LOGW("Table of sync samples is empty or has only a single entry!");
}
+
+ mSyncSamples = new uint32_t[mNumSyncSamples];
+ size_t size = mNumSyncSamples * sizeof(uint32_t);
+ if (mDataSource->readAt(mSyncSampleOffset + 8, mSyncSamples, size)
+ != (ssize_t)size) {
+ return ERROR_IO;
+ }
+
+ for (size_t i = 0; i < mNumSyncSamples; ++i) {
+ mSyncSamples[i] = ntohl(mSyncSamples[i]) - 1;
+ }
+
return OK;
}
@@ -394,14 +411,7 @@ status_t SampleTable::findSyncSampleNear(
uint32_t left = 0;
while (left < mNumSyncSamples) {
- uint32_t x;
- if (mDataSource->readAt(
- mSyncSampleOffset + 8 + left * 4, &x, 4) != 4) {
- return ERROR_IO;
- }
-
- x = ntohl(x);
- --x;
+ uint32_t x = mSyncSamples[left];
if (x >= start_sample_index) {
break;
@@ -421,14 +431,7 @@ status_t SampleTable::findSyncSampleNear(
--x;
if (left + 1 < mNumSyncSamples) {
- uint32_t y;
- if (mDataSource->readAt(
- mSyncSampleOffset + 8 + (left + 1) * 4, &y, 4) != 4) {
- return ERROR_IO;
- }
-
- y = ntohl(y);
- --y;
+ uint32_t y = mSyncSamples[left + 1];
// our sample lies between sync samples x and y.
@@ -486,13 +489,7 @@ status_t SampleTable::findSyncSampleNear(
return ERROR_OUT_OF_RANGE;
}
- if (mDataSource->readAt(
- mSyncSampleOffset + 8 + (left + 1) * 4, &x, 4) != 4) {
- return ERROR_IO;
- }
-
- x = ntohl(x);
- --x;
+ x = mSyncSamples[left + 1];
CHECK(x >= start_sample_index);
}
@@ -532,13 +529,7 @@ status_t SampleTable::findThumbnailSample(uint32_t *sample_index) {
}
for (size_t i = 0; i < numSamplesToScan; ++i) {
- uint32_t x;
- if (mDataSource->readAt(
- mSyncSampleOffset + 8 + i * 4, &x, 4) != 4) {
- return ERROR_IO;
- }
- x = ntohl(x);
- --x;
+ uint32_t x = mSyncSamples[i];
// Now x is a sample index.
size_t sampleSize;
@@ -568,7 +559,8 @@ status_t SampleTable::getMetaDataForSample(
uint32_t sampleIndex,
off_t *offset,
size_t *size,
- uint32_t *decodingTime) {
+ uint32_t *decodingTime,
+ bool *isSyncSample) {
Mutex::Autolock autoLock(mLock);
status_t err;
@@ -588,6 +580,28 @@ status_t SampleTable::getMetaDataForSample(
*decodingTime = mSampleIterator->getSampleTime();
}
+ if (isSyncSample) {
+ *isSyncSample = false;
+ if (mSyncSampleOffset < 0) {
+ // Every sample is a sync sample.
+ *isSyncSample = true;
+ } else {
+ size_t i = (mLastSyncSampleIndex < mNumSyncSamples)
+ && (mSyncSamples[mLastSyncSampleIndex] <= sampleIndex)
+ ? mLastSyncSampleIndex : 0;
+
+ while (i < mNumSyncSamples && mSyncSamples[i] < sampleIndex) {
+ ++i;
+ }
+
+ if (i < mNumSyncSamples && mSyncSamples[i] == sampleIndex) {
+ *isSyncSample = true;
+ }
+
+ mLastSyncSampleIndex = i;
+ }
+ }
+
return OK;
}
diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp
index 7c2b07e4d30d..8d820c0dd69d 100644
--- a/media/libstagefright/WAVExtractor.cpp
+++ b/media/libstagefright/WAVExtractor.cpp
@@ -31,7 +31,11 @@
namespace android {
-static uint16_t WAVE_FORMAT_PCM = 1;
+enum {
+ WAVE_FORMAT_PCM = 1,
+ WAVE_FORMAT_ALAW = 6,
+ WAVE_FORMAT_MULAW = 7,
+};
static uint32_t U32_LE_AT(const uint8_t *ptr) {
return ptr[3] << 24 | ptr[2] << 16 | ptr[1] << 8 | ptr[0];
@@ -45,6 +49,7 @@ struct WAVSource : public MediaSource {
WAVSource(
const sp<DataSource> &dataSource,
const sp<MetaData> &meta,
+ uint16_t waveFormat,
int32_t bitsPerSample,
off_t offset, size_t size);
@@ -63,6 +68,7 @@ private:
sp<DataSource> mDataSource;
sp<MetaData> mMeta;
+ uint16_t mWaveFormat;
int32_t mSampleRate;
int32_t mNumChannels;
int32_t mBitsPerSample;
@@ -108,7 +114,7 @@ sp<MediaSource> WAVExtractor::getTrack(size_t index) {
return new WAVSource(
mDataSource, mTrackMeta,
- mBitsPerSample, mDataOffset, mDataSize);
+ mWaveFormat, mBitsPerSample, mDataOffset, mDataSize);
}
sp<MetaData> WAVExtractor::getTrackMetaData(
@@ -160,8 +166,10 @@ status_t WAVExtractor::init() {
return NO_INIT;
}
- uint16_t format = U16_LE_AT(formatSpec);
- if (format != WAVE_FORMAT_PCM) {
+ mWaveFormat = U16_LE_AT(formatSpec);
+ if (mWaveFormat != WAVE_FORMAT_PCM
+ && mWaveFormat != WAVE_FORMAT_ALAW
+ && mWaveFormat != WAVE_FORMAT_MULAW) {
return ERROR_UNSUPPORTED;
}
@@ -178,9 +186,17 @@ status_t WAVExtractor::init() {
mBitsPerSample = U16_LE_AT(&formatSpec[14]);
- if (mBitsPerSample != 8 && mBitsPerSample != 16
- && mBitsPerSample != 24) {
- return ERROR_UNSUPPORTED;
+ if (mWaveFormat == WAVE_FORMAT_PCM) {
+ if (mBitsPerSample != 8 && mBitsPerSample != 16
+ && mBitsPerSample != 24) {
+ return ERROR_UNSUPPORTED;
+ }
+ } else {
+ CHECK(mWaveFormat == WAVE_FORMAT_MULAW
+ || mWaveFormat == WAVE_FORMAT_ALAW);
+ if (mBitsPerSample != 8) {
+ return ERROR_UNSUPPORTED;
+ }
}
mValidFormat = true;
@@ -190,7 +206,23 @@ status_t WAVExtractor::init() {
mDataSize = chunkSize;
mTrackMeta = new MetaData;
- mTrackMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
+
+ switch (mWaveFormat) {
+ case WAVE_FORMAT_PCM:
+ mTrackMeta->setCString(
+ kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
+ break;
+ case WAVE_FORMAT_ALAW:
+ mTrackMeta->setCString(
+ kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_G711_ALAW);
+ break;
+ default:
+ CHECK_EQ(mWaveFormat, WAVE_FORMAT_MULAW);
+ mTrackMeta->setCString(
+ kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_G711_MLAW);
+ break;
+ }
+
mTrackMeta->setInt32(kKeyChannelCount, mNumChannels);
mTrackMeta->setInt32(kKeySampleRate, mSampleRate);
@@ -217,10 +249,12 @@ const size_t WAVSource::kMaxFrameSize = 32768;
WAVSource::WAVSource(
const sp<DataSource> &dataSource,
const sp<MetaData> &meta,
+ uint16_t waveFormat,
int32_t bitsPerSample,
off_t offset, size_t size)
: mDataSource(dataSource),
mMeta(meta),
+ mWaveFormat(waveFormat),
mSampleRate(0),
mNumChannels(0),
mBitsPerSample(bitsPerSample),
@@ -312,43 +346,45 @@ status_t WAVSource::read(
buffer->set_range(0, n);
- if (mBitsPerSample == 8) {
- // Convert 8-bit unsigned samples to 16-bit signed.
+ if (mWaveFormat == WAVE_FORMAT_PCM) {
+ if (mBitsPerSample == 8) {
+ // Convert 8-bit unsigned samples to 16-bit signed.
- MediaBuffer *tmp;
- CHECK_EQ(mGroup->acquire_buffer(&tmp), OK);
+ MediaBuffer *tmp;
+ CHECK_EQ(mGroup->acquire_buffer(&tmp), OK);
- // The new buffer holds the sample number of samples, but each
- // one is 2 bytes wide.
- tmp->set_range(0, 2 * n);
+ // The new buffer holds the sample number of samples, but each
+ // one is 2 bytes wide.
+ tmp->set_range(0, 2 * n);
- int16_t *dst = (int16_t *)tmp->data();
- const uint8_t *src = (const uint8_t *)buffer->data();
- while (n-- > 0) {
- *dst++ = ((int16_t)(*src) - 128) * 256;
- ++src;
- }
+ int16_t *dst = (int16_t *)tmp->data();
+ const uint8_t *src = (const uint8_t *)buffer->data();
+ while (n-- > 0) {
+ *dst++ = ((int16_t)(*src) - 128) * 256;
+ ++src;
+ }
- buffer->release();
- buffer = tmp;
- } else if (mBitsPerSample == 24) {
- // Convert 24-bit signed samples to 16-bit signed.
-
- const uint8_t *src =
- (const uint8_t *)buffer->data() + buffer->range_offset();
- int16_t *dst = (int16_t *)src;
-
- size_t numSamples = buffer->range_length() / 3;
- for (size_t i = 0; i < numSamples; ++i) {
- int32_t x = (int32_t)(src[0] | src[1] << 8 | src[2] << 16);
- x = (x << 8) >> 8; // sign extension
-
- x = x >> 8;
- *dst++ = (int16_t)x;
- src += 3;
- }
+ buffer->release();
+ buffer = tmp;
+ } else if (mBitsPerSample == 24) {
+ // Convert 24-bit signed samples to 16-bit signed.
+
+ const uint8_t *src =
+ (const uint8_t *)buffer->data() + buffer->range_offset();
+ int16_t *dst = (int16_t *)src;
+
+ size_t numSamples = buffer->range_length() / 3;
+ for (size_t i = 0; i < numSamples; ++i) {
+ int32_t x = (int32_t)(src[0] | src[1] << 8 | src[2] << 16);
+ x = (x << 8) >> 8; // sign extension
- buffer->set_range(buffer->range_offset(), 2 * numSamples);
+ x = x >> 8;
+ *dst++ = (int16_t)x;
+ src += 3;
+ }
+
+ buffer->set_range(buffer->range_offset(), 2 * numSamples);
+ }
}
size_t bytesPerSample = mBitsPerSample >> 3;
@@ -358,6 +394,7 @@ status_t WAVSource::read(
1000000LL * (mCurrentPos - mOffset)
/ (mNumChannels * bytesPerSample) / mSampleRate);
+ buffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
*out = buffer;
diff --git a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
index d5eb15628789..6e7427918630 100644
--- a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
+++ b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp
@@ -337,14 +337,18 @@ status_t AVCEncoder::read(
MediaBuffer *outputBuffer;
CHECK_EQ(OK, mGroup->acquire_buffer(&outputBuffer));
- uint8_t *outPtr = (uint8_t *) outputBuffer->data();
- uint32_t dataLength = outputBuffer->size();
+
+ // Add 4 bytes for the start code 0x00000001
+ uint8_t *outPtr = (uint8_t *) outputBuffer->data() + 4;
+ uint32_t dataLength = outputBuffer->size() - 4;
int32_t type;
AVCEnc_Status encoderStatus = AVCENC_SUCCESS;
- // Return SPS and PPS for the first two buffers
- if (!mSpsPpsHeaderReceived) {
+ // Combine SPS and PPS and place them in the very first output buffer
+ // SPS and PPS are separated by start code 0x00000001
+ // Assume that we have exactly one SPS and exactly one PPS.
+ while (!mSpsPpsHeaderReceived && mNumInputFrames <= 0) {
encoderStatus = PVAVCEncodeNAL(mHandle, outPtr, &dataLength, &type);
if (encoderStatus == AVCENC_WRONG_STATE) {
mSpsPpsHeaderReceived = true;
@@ -352,11 +356,22 @@ status_t AVCEncoder::read(
} else {
switch (type) {
case AVC_NALTYPE_SPS:
+ ++mNumInputFrames;
+ memcpy(outputBuffer->data(), "\x00\x00\x00\x01", 4);
+ outputBuffer->set_range(0, dataLength + 4);
+ outPtr += (dataLength + 4); // 4 bytes for next start code
+ dataLength = outputBuffer->size() -
+ (outputBuffer->range_length() + 4);
+ break;
case AVC_NALTYPE_PPS:
- LOGV("%s received",
- (type == AVC_NALTYPE_SPS)? "SPS": "PPS");
++mNumInputFrames;
- outputBuffer->set_range(0, dataLength);
+ memcpy(((uint8_t *) outputBuffer->data()) +
+ outputBuffer->range_length(),
+ "\x00\x00\x00\x01", 4);
+ outputBuffer->set_range(0,
+ dataLength + outputBuffer->range_length() + 4);
+ outputBuffer->meta_data()->setInt32(kKeyIsCodecConfig, 1);
+ outputBuffer->meta_data()->setInt64(kKeyTime, 0);
*out = outputBuffer;
return OK;
default:
@@ -376,8 +391,18 @@ status_t AVCEncoder::read(
if (err != OK) {
LOGE("Failed to read input video frame: %d", err);
outputBuffer->release();
+ mInputBuffer->release();
+ mInputBuffer = NULL;
return err;
}
+
+ if (mInputBuffer->size() - ((mVideoWidth * mVideoHeight * 3) >> 1) != 0) {
+ outputBuffer->release();
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ return UNKNOWN_ERROR;
+ }
+
int64_t timeUs;
CHECK(mInputBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
outputBuffer->meta_data()->setInt64(kKeyTime, timeUs);
diff --git a/media/libstagefright/codecs/g711/Android.mk b/media/libstagefright/codecs/g711/Android.mk
new file mode 100644
index 000000000000..2e431205aa6d
--- /dev/null
+++ b/media/libstagefright/codecs/g711/Android.mk
@@ -0,0 +1,4 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/media/libstagefright/codecs/g711/dec/Android.mk b/media/libstagefright/codecs/g711/dec/Android.mk
new file mode 100644
index 000000000000..cfb9fe465868
--- /dev/null
+++ b/media/libstagefright/codecs/g711/dec/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ G711Decoder.cpp
+
+LOCAL_C_INCLUDES := \
+ frameworks/base/media/libstagefright/include \
+
+LOCAL_MODULE := libstagefright_g711dec
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/media/libstagefright/codecs/g711/dec/G711Decoder.cpp b/media/libstagefright/codecs/g711/dec/G711Decoder.cpp
new file mode 100644
index 000000000000..4414e4ed4b48
--- /dev/null
+++ b/media/libstagefright/codecs/g711/dec/G711Decoder.cpp
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "G711Decoder"
+#include <utils/Log.h>
+
+#include "G711Decoder.h"
+
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+
+static const size_t kMaxNumSamplesPerFrame = 16384;
+
+namespace android {
+
+G711Decoder::G711Decoder(const sp<MediaSource> &source)
+ : mSource(source),
+ mStarted(false),
+ mBufferGroup(NULL) {
+}
+
+G711Decoder::~G711Decoder() {
+ if (mStarted) {
+ stop();
+ }
+}
+
+status_t G711Decoder::start(MetaData *params) {
+ CHECK(!mStarted);
+
+ const char *mime;
+ CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
+
+ mIsMLaw = false;
+ if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_G711_MLAW)) {
+ mIsMLaw = true;
+ } else if (strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_G711_ALAW)) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ mBufferGroup = new MediaBufferGroup;
+ mBufferGroup->add_buffer(
+ new MediaBuffer(kMaxNumSamplesPerFrame * sizeof(int16_t)));
+
+ mSource->start();
+
+ mStarted = true;
+
+ return OK;
+}
+
+status_t G711Decoder::stop() {
+ CHECK(mStarted);
+
+ delete mBufferGroup;
+ mBufferGroup = NULL;
+
+ mSource->stop();
+
+ mStarted = false;
+
+ return OK;
+}
+
+sp<MetaData> G711Decoder::getFormat() {
+ sp<MetaData> srcFormat = mSource->getFormat();
+
+ int32_t numChannels;
+ int32_t sampleRate;
+
+ CHECK(srcFormat->findInt32(kKeyChannelCount, &numChannels));
+ CHECK(srcFormat->findInt32(kKeySampleRate, &sampleRate));
+
+ sp<MetaData> meta = new MetaData;
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
+ meta->setInt32(kKeyChannelCount, numChannels);
+ meta->setInt32(kKeySampleRate, sampleRate);
+
+ int64_t durationUs;
+ if (srcFormat->findInt64(kKeyDuration, &durationUs)) {
+ meta->setInt64(kKeyDuration, durationUs);
+ }
+
+ meta->setCString(kKeyDecoderComponent, "G711Decoder");
+
+ return meta;
+}
+
+status_t G711Decoder::read(
+ MediaBuffer **out, const ReadOptions *options) {
+ status_t err;
+
+ *out = NULL;
+
+ int64_t seekTimeUs;
+ ReadOptions::SeekMode mode;
+ if (options && options->getSeekTo(&seekTimeUs, &mode)) {
+ CHECK(seekTimeUs >= 0);
+ } else {
+ seekTimeUs = -1;
+ }
+
+ MediaBuffer *inBuffer;
+ err = mSource->read(&inBuffer, options);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (inBuffer->range_length() > kMaxNumSamplesPerFrame) {
+ LOGE("input buffer too large (%d).", inBuffer->range_length());
+
+ inBuffer->release();
+ inBuffer = NULL;
+
+ return ERROR_UNSUPPORTED;
+ }
+
+ int64_t timeUs;
+ CHECK(inBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
+
+ const uint8_t *inputPtr =
+ (const uint8_t *)inBuffer->data() + inBuffer->range_offset();
+
+ MediaBuffer *outBuffer;
+ CHECK_EQ(mBufferGroup->acquire_buffer(&outBuffer), OK);
+
+ if (mIsMLaw) {
+ DecodeMLaw(
+ static_cast<int16_t *>(outBuffer->data()),
+ inputPtr, inBuffer->range_length());
+ } else {
+ DecodeALaw(
+ static_cast<int16_t *>(outBuffer->data()),
+ inputPtr, inBuffer->range_length());
+ }
+
+ // Each 8-bit byte is converted into a 16-bit sample.
+ outBuffer->set_range(0, inBuffer->range_length() * 2);
+
+ outBuffer->meta_data()->setInt64(kKeyTime, timeUs);
+
+ inBuffer->release();
+ inBuffer = NULL;
+
+ *out = outBuffer;
+
+ return OK;
+}
+
+// static
+void G711Decoder::DecodeALaw(
+ int16_t *out, const uint8_t *in, size_t inSize) {
+ while (inSize-- > 0) {
+ int32_t x = *in++;
+
+ int32_t ix = x ^ 0x55;
+ ix &= 0x7f;
+
+ int32_t iexp = ix >> 4;
+ int32_t mant = ix & 0x0f;
+
+ if (iexp > 0) {
+ mant += 16;
+ }
+
+ mant = (mant << 4) + 8;
+
+ if (iexp > 1) {
+ mant = mant << (iexp - 1);
+ }
+
+ *out++ = (x > 127) ? mant : -mant;
+ }
+}
+
+// static
+void G711Decoder::DecodeMLaw(
+ int16_t *out, const uint8_t *in, size_t inSize) {
+ while (inSize-- > 0) {
+ int32_t x = *in++;
+
+ int32_t mantissa = ~x;
+ int32_t exponent = (mantissa >> 4) & 7;
+ int32_t segment = exponent + 1;
+ mantissa &= 0x0f;
+
+ int32_t step = 4 << segment;
+
+ int32_t abs = (0x80l << exponent) + step * mantissa + step / 2 - 4 * 33;
+
+ *out++ = (x < 0x80) ? -abs : abs;
+ }
+}
+
+} // namespace android
diff --git a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
index 5002442e991b..1bef0e91d09f 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
+++ b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
@@ -292,8 +292,18 @@ status_t M4vH263Encoder::read(
if (OK != mSource->read(&mInputBuffer, options)) {
LOGE("Failed to read from data source");
outputBuffer->release();
+ mInputBuffer->release();
+ mInputBuffer = NULL;
return UNKNOWN_ERROR;
}
+
+ if (mInputBuffer->size() - ((mVideoWidth * mVideoHeight * 3) >> 1) != 0) {
+ outputBuffer->release();
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ return UNKNOWN_ERROR;
+ }
+
int64_t timeUs;
CHECK(mInputBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
if (mNextModTimeUs > timeUs) {
diff --git a/media/libstagefright/include/G711Decoder.h b/media/libstagefright/include/G711Decoder.h
new file mode 100644
index 000000000000..8b5143ac896b
--- /dev/null
+++ b/media/libstagefright/include/G711Decoder.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef G711_DECODER_H_
+
+#define G711_DECODER_H_
+
+#include <media/stagefright/MediaSource.h>
+
+namespace android {
+
+struct MediaBufferGroup;
+
+struct G711Decoder : public MediaSource {
+ G711Decoder(const sp<MediaSource> &source);
+
+ virtual status_t start(MetaData *params);
+ virtual status_t stop();
+
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options);
+
+protected:
+ virtual ~G711Decoder();
+
+private:
+ sp<MediaSource> mSource;
+ bool mStarted;
+ bool mIsMLaw;
+
+ MediaBufferGroup *mBufferGroup;
+
+ static void DecodeALaw(int16_t *out, const uint8_t *in, size_t inSize);
+ static void DecodeMLaw(int16_t *out, const uint8_t *in, size_t inSize);
+
+ G711Decoder(const G711Decoder &);
+ G711Decoder &operator=(const G711Decoder &);
+};
+
+} // namespace android
+
+#endif // G711_DECODER_H_
diff --git a/media/libstagefright/include/SampleTable.h b/media/libstagefright/include/SampleTable.h
index a2b2c998a0b5..f83069032425 100644
--- a/media/libstagefright/include/SampleTable.h
+++ b/media/libstagefright/include/SampleTable.h
@@ -60,7 +60,8 @@ public:
uint32_t sampleIndex,
off_t *offset,
size_t *size,
- uint32_t *decodingTime);
+ uint32_t *decodingTime,
+ bool *isSyncSample = NULL);
enum {
kFlagBefore,
@@ -105,6 +106,8 @@ private:
off_t mSyncSampleOffset;
uint32_t mNumSyncSamples;
+ uint32_t *mSyncSamples;
+ size_t mLastSyncSampleIndex;
SampleIterator *mSampleIterator;
diff --git a/media/libstagefright/include/WAVExtractor.h b/media/libstagefright/include/WAVExtractor.h
index 93849429ff19..3e847b9e5e94 100644
--- a/media/libstagefright/include/WAVExtractor.h
+++ b/media/libstagefright/include/WAVExtractor.h
@@ -43,6 +43,7 @@ private:
sp<DataSource> mDataSource;
status_t mInitCheck;
bool mValidFormat;
+ uint16_t mWaveFormat;
uint16_t mNumChannels;
uint32_t mSampleRate;
uint16_t mBitsPerSample;
diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp
index 3739be1f3bbd..71f6587637b3 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.cpp
+++ b/media/libstagefright/matroska/MatroskaExtractor.cpp
@@ -296,6 +296,7 @@ status_t MatroskaSource::read(
MediaBuffer *buffer = new MediaBuffer(size + 2);
buffer->meta_data()->setInt64(kKeyTime, timeUs);
+ buffer->meta_data()->setInt32(kKeyIsSyncFrame, block->IsKey());
long res = block->Read(
mExtractor->mReader, (unsigned char *)buffer->data() + 2);
diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp
index a5777045f4db..395cd28c8828 100644
--- a/media/libstagefright/rtsp/APacketSource.cpp
+++ b/media/libstagefright/rtsp/APacketSource.cpp
@@ -356,24 +356,10 @@ status_t APacketSource::read(
if (!mBuffers.empty()) {
const sp<ABuffer> buffer = *mBuffers.begin();
- uint64_t ntpTime;
- CHECK(buffer->meta()->findInt64(
- "ntp-time", (int64_t *)&ntpTime));
-
MediaBuffer *mediaBuffer = new MediaBuffer(buffer->size());
- mediaBuffer->meta_data()->setInt64(kKeyNTPTime, ntpTime);
-
- if (mFirstAccessUnit) {
- mFirstAccessUnit = false;
- mFirstAccessUnitNTP = ntpTime;
- }
- if (ntpTime > mFirstAccessUnitNTP) {
- ntpTime -= mFirstAccessUnitNTP;
- } else {
- ntpTime = 0;
- }
- int64_t timeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32));
+ int64_t timeUs;
+ CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs);
@@ -390,10 +376,29 @@ status_t APacketSource::read(
void APacketSource::queueAccessUnit(const sp<ABuffer> &buffer) {
int32_t damaged;
if (buffer->meta()->findInt32("damaged", &damaged) && damaged) {
- // LOG(VERBOSE) << "discarding damaged AU";
+ LOG(INFO) << "discarding damaged AU";
return;
}
+ uint64_t ntpTime;
+ CHECK(buffer->meta()->findInt64(
+ "ntp-time", (int64_t *)&ntpTime));
+
+ if (mFirstAccessUnit) {
+ mFirstAccessUnit = false;
+ mFirstAccessUnitNTP = ntpTime;
+ }
+
+ if (ntpTime > mFirstAccessUnitNTP) {
+ ntpTime -= mFirstAccessUnitNTP;
+ } else {
+ ntpTime = 0;
+ }
+
+ int64_t timeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32));
+
+ buffer->meta()->setInt64("timeUs", timeUs);
+
Mutex::Autolock autoLock(mLock);
mBuffers.push_back(buffer);
mCondition.signal();
diff --git a/media/libstagefright/rtsp/ARTPConnection.cpp b/media/libstagefright/rtsp/ARTPConnection.cpp
index 5bd306b41a08..469af3e256c3 100644
--- a/media/libstagefright/rtsp/ARTPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTPConnection.cpp
@@ -28,8 +28,6 @@
#include <arpa/inet.h>
#include <sys/socket.h>
-#define IGNORE_RTCP_TIME 0
-
namespace android {
static const size_t kMaxUDPSize = 1500;
@@ -61,8 +59,9 @@ struct ARTPConnection::StreamInfo {
struct sockaddr_in mRemoteRTCPAddr;
};
-ARTPConnection::ARTPConnection()
- : mPollEventPending(false),
+ARTPConnection::ARTPConnection(uint32_t flags)
+ : mFlags(flags),
+ mPollEventPending(false),
mLastReceiverReportTimeUs(-1) {
}
@@ -280,7 +279,10 @@ void ARTPConnection::onPollStreams() {
sp<ARTPSource> source = s->mSources.valueAt(i);
source->addReceiverReport(buffer);
- source->addFIR(buffer);
+
+ if (mFlags & kRegularlyRequestFIR) {
+ source->addFIR(buffer);
+ }
}
if (buffer->size() > 0) {
@@ -405,13 +407,11 @@ status_t ARTPConnection::parseRTP(StreamInfo *s, const sp<ABuffer> &buffer) {
buffer->setInt32Data(u16at(&data[2]));
buffer->setRange(payloadOffset, size - payloadOffset);
-#if IGNORE_RTCP_TIME
- if (!source->timeEstablished()) {
+ if ((mFlags & kFakeTimestamps) && !source->timeEstablished()) {
source->timeUpdate(rtpTime, 0);
- source->timeUpdate(rtpTime + 20, 0x100000000ll);
+ source->timeUpdate(rtpTime + 90000, 0x100000000ll);
CHECK(source->timeEstablished());
}
-#endif
source->processRTPPacket(buffer);
@@ -533,9 +533,9 @@ status_t ARTPConnection::parseSR(
sp<ARTPSource> source = findSource(s, id);
-#if !IGNORE_RTCP_TIME
- source->timeUpdate(rtpTime, ntpTime);
-#endif
+ if ((mFlags & kFakeTimestamps) == 0) {
+ source->timeUpdate(rtpTime, ntpTime);
+ }
return 0;
}
diff --git a/media/libstagefright/rtsp/ARTPConnection.h b/media/libstagefright/rtsp/ARTPConnection.h
index 49839ad66b03..c53519977f0a 100644
--- a/media/libstagefright/rtsp/ARTPConnection.h
+++ b/media/libstagefright/rtsp/ARTPConnection.h
@@ -28,7 +28,12 @@ struct ARTPSource;
struct ASessionDescription;
struct ARTPConnection : public AHandler {
- ARTPConnection();
+ enum Flags {
+ kFakeTimestamps = 1,
+ kRegularlyRequestFIR = 2,
+ };
+
+ ARTPConnection(uint32_t flags = 0);
void addStream(
int rtpSocket, int rtcpSocket,
@@ -56,6 +61,8 @@ private:
static const int64_t kSelectTimeoutUs;
+ uint32_t mFlags;
+
struct StreamInfo;
List<StreamInfo> mStreams;
diff --git a/media/libstagefright/rtsp/ARTPSession.cpp b/media/libstagefright/rtsp/ARTPSession.cpp
index 0e0f45ad1a17..e0820782a9c9 100644
--- a/media/libstagefright/rtsp/ARTPSession.cpp
+++ b/media/libstagefright/rtsp/ARTPSession.cpp
@@ -40,7 +40,10 @@ status_t ARTPSession::setup(const sp<ASessionDescription> &desc) {
mDesc = desc;
- mRTPConn = new ARTPConnection;
+ mRTPConn = new ARTPConnection(
+ ARTPConnection::kFakeTimestamps
+ | ARTPConnection::kRegularlyRequestFIR);
+
looper()->registerHandler(mRTPConn);
for (size_t i = 1; i < mDesc->countTracks(); ++i) {
diff --git a/media/libstagefright/rtsp/ARTPSource.cpp b/media/libstagefright/rtsp/ARTPSource.cpp
index e08183e50259..225f6e8d1f78 100644
--- a/media/libstagefright/rtsp/ARTPSource.cpp
+++ b/media/libstagefright/rtsp/ARTPSource.cpp
@@ -98,7 +98,7 @@ void ARTPSource::timeUpdate(uint32_t rtpTime, uint64_t ntpTime) {
mNTPTime[mNumTimes] = ntpTime;
mRTPTime[mNumTimes++] = rtpTime;
- if (mNumTimes == 2) {
+ if (timeEstablished()) {
for (List<sp<ABuffer> >::iterator it = mQueue.begin();
it != mQueue.end(); ++it) {
sp<AMessage> meta = (*it)->meta();
@@ -112,13 +112,6 @@ void ARTPSource::timeUpdate(uint32_t rtpTime, uint64_t ntpTime) {
}
bool ARTPSource::queuePacket(const sp<ABuffer> &buffer) {
-#if 1
- if (mNumTimes != 2) {
- // Drop incoming packets until we've established a time base.
- return false;
- }
-#endif
-
uint32_t seqNum = (uint32_t)buffer->int32Data();
if (mNumTimes == 2) {
diff --git a/media/mtp/MtpDataPacket.cpp b/media/mtp/MtpDataPacket.cpp
index c159e2095d2c..9bfd00f44be8 100644
--- a/media/mtp/MtpDataPacket.cpp
+++ b/media/mtp/MtpDataPacket.cpp
@@ -266,6 +266,13 @@ void MtpDataPacket::putAUInt16(const uint16_t* values, int count) {
putUInt16(*values++);
}
+void MtpDataPacket::putAUInt16(const UInt16List* values) {
+ size_t count = (values ? values->size() : 0);
+ putUInt32(count);
+ for (size_t i = 0; i < count; i++)
+ putUInt16((*values)[i]);
+}
+
void MtpDataPacket::putAInt32(const int32_t* values, int count) {
putUInt32(count);
for (int i = 0; i < count; i++)
diff --git a/media/mtp/MtpDataPacket.h b/media/mtp/MtpDataPacket.h
index e8314d7237a8..b458286ab89e 100644
--- a/media/mtp/MtpDataPacket.h
+++ b/media/mtp/MtpDataPacket.h
@@ -74,6 +74,7 @@ public:
void putAUInt8(const uint8_t* values, int count);
void putAInt16(const int16_t* values, int count);
void putAUInt16(const uint16_t* values, int count);
+ void putAUInt16(const UInt16List* values);
void putAInt32(const int32_t* values, int count);
void putAUInt32(const uint32_t* values, int count);
void putAUInt32(const UInt32List* list);
diff --git a/media/mtp/MtpDatabase.h b/media/mtp/MtpDatabase.h
index 02bb0d9f88c0..17823dfac156 100644
--- a/media/mtp/MtpDatabase.h
+++ b/media/mtp/MtpDatabase.h
@@ -51,6 +51,13 @@ public:
MtpObjectFormat format,
MtpObjectHandle parent) = 0;
+ // callee should delete[] the results from these
+ // results can be NULL
+ virtual MtpObjectFormatList* getSupportedPlaybackFormats() = 0;
+ virtual MtpObjectFormatList* getSupportedCaptureFormats() = 0;
+ virtual MtpObjectPropertyList* getSupportedObjectProperties(MtpObjectFormat format) = 0;
+ virtual MtpDevicePropertyList* getSupportedDeviceProperties() = 0;
+
virtual MtpResponseCode getObjectProperty(MtpObjectHandle handle,
MtpObjectProperty property,
MtpDataPacket& packet) = 0;
diff --git a/media/mtp/MtpDebug.cpp b/media/mtp/MtpDebug.cpp
index 9ded6e2e4085..d6b107d7434c 100644
--- a/media/mtp/MtpDebug.cpp
+++ b/media/mtp/MtpDebug.cpp
@@ -18,12 +18,12 @@
namespace android {
-struct OperationCodeEntry {
+struct CodeEntry {
const char* name;
- MtpOperationCode code;
+ uint16_t code;
};
-static const OperationCodeEntry sOperationCodes[] = {
+static const CodeEntry sOperationCodes[] = {
{ "MTP_OPERATION_GET_DEVICE_INFO", 0x1001 },
{ "MTP_OPERATION_OPEN_SESSION", 0x1002 },
{ "MTP_OPERATION_CLOSE_SESSION", 0x1003 },
@@ -62,15 +62,326 @@ static const OperationCodeEntry sOperationCodes[] = {
{ 0, 0 },
};
+static const CodeEntry sFormatCodes[] = {
+ { "MTP_OPERATION_GET_DEVICE_INFO", 0x1001 },
+ { "MTP_FORMAT_UNDEFINED", 0x3000 },
+ { "MTP_FORMAT_ASSOCIATION", 0x3001 },
+ { "MTP_FORMAT_SCRIPT", 0x3002 },
+ { "MTP_FORMAT_EXECUTABLE", 0x3003 },
+ { "MTP_FORMAT_TEXT", 0x3004 },
+ { "MTP_FORMAT_HTML", 0x3005 },
+ { "MTP_FORMAT_DPOF", 0x3006 },
+ { "MTP_FORMAT_AIFF", 0x3007 },
+ { "MTP_FORMAT_WAV", 0x3008 },
+ { "MTP_FORMAT_MP3", 0x3009 },
+ { "MTP_FORMAT_AVI", 0x300A },
+ { "MTP_FORMAT_MPEG", 0x300B },
+ { "MTP_FORMAT_ASF", 0x300C },
+ { "MTP_FORMAT_DEFINED", 0x3800 },
+ { "MTP_FORMAT_EXIF_JPEG", 0x3801 },
+ { "MTP_FORMAT_TIFF_EP", 0x3802 },
+ { "MTP_FORMAT_FLASHPIX", 0x3803 },
+ { "MTP_FORMAT_BMP", 0x3804 },
+ { "MTP_FORMAT_CIFF", 0x3805 },
+ { "MTP_FORMAT_GIF", 0x3807 },
+ { "MTP_FORMAT_JFIF", 0x3808 },
+ { "MTP_FORMAT_CD", 0x3809 },
+ { "MTP_FORMAT_PICT", 0x380A },
+ { "MTP_FORMAT_PNG", 0x380B },
+ { "MTP_FORMAT_TIFF", 0x380D },
+ { "MTP_FORMAT_TIFF_IT", 0x380E },
+ { "MTP_FORMAT_JP2", 0x380F },
+ { "MTP_FORMAT_JPX", 0x3810 },
+ { "MTP_FORMAT_UNDEFINED_FIRMWARE", 0xB802 },
+ { "MTP_FORMAT_WINDOWS_IMAGE_FORMAT", 0xB881 },
+ { "MTP_FORMAT_UNDEFINED_AUDIO", 0xB900 },
+ { "MTP_FORMAT_WMA", 0xB901 },
+ { "MTP_FORMAT_OGG", 0xB902 },
+ { "MTP_FORMAT_AAC", 0xB903 },
+ { "MTP_FORMAT_AUDIBLE", 0xB904 },
+ { "MTP_FORMAT_FLAC", 0xB906 },
+ { "MTP_FORMAT_UNDEFINED_VIDEO", 0xB980 },
+ { "MTP_FORMAT_WMV", 0xB981 },
+ { "MTP_FORMAT_MP4_CONTAINER", 0xB982 },
+ { "MTP_FORMAT_MP2", 0xB983 },
+ { "MTP_FORMAT_3GP_CONTAINER", 0xB984 },
+ { "MTP_FORMAT_UNDEFINED_COLLECTION", 0xBA00 },
+ { "MTP_FORMAT_ABSTRACT_MULTIMEDIA_ALBUM", 0xBA01 },
+ { "MTP_FORMAT_ABSTRACT_IMAGE_ALBUM", 0xBA02 },
+ { "MTP_FORMAT_ABSTRACT_AUDIO_ALBUM", 0xBA03 },
+ { "MTP_FORMAT_ABSTRACT_VIDEO_ALBUM", 0xBA04 },
+ { "MTP_FORMAT_ABSTRACT_AV_PLAYLIST", 0xBA05 },
+ { "MTP_FORMAT_ABSTRACT_CONTACT_GROUP", 0xBA06 },
+ { "MTP_FORMAT_ABSTRACT_MESSAGE_FOLDER", 0xBA07 },
+ { "MTP_FORMAT_ABSTRACT_CHAPTERED_PRODUCTION", 0xBA08 },
+ { "MTP_FORMAT_ABSTRACT_AUDIO_PLAYLIST", 0xBA09 },
+ { "MTP_FORMAT_ABSTRACT_VIDEO_PLAYLIST", 0xBA0A },
+ { "MTP_FORMAT_ABSTRACT_MEDIACAST", 0xBA0B },
+ { "MTP_FORMAT_WPL_PLAYLIST", 0xBA10 },
+ { "MTP_FORMAT_M3U_PLAYLIST", 0xBA11 },
+ { "MTP_FORMAT_MPL_PLAYLIST", 0xBA12 },
+ { "MTP_FORMAT_ASX_PLAYLIST", 0xBA13 },
+ { "MTP_FORMAT_PLS_PLAYLIST", 0xBA14 },
+ { "MTP_FORMAT_UNDEFINED_DOCUMENT", 0xBA80 },
+ { "MTP_FORMAT_ABSTRACT_DOCUMENT", 0xBA81 },
+ { "MTP_FORMAT_XML_DOCUMENT", 0xBA82 },
+ { "MTP_FORMAT_MS_WORD_DOCUMENT", 0xBA83 },
+ { "MTP_FORMAT_MHT_COMPILED_HTML_DOCUMENT", 0xBA84 },
+ { "MTP_FORMAT_MS_EXCEL_SPREADSHEET", 0xBA85 },
+ { "MTP_FORMAT_MS_POWERPOINT_PRESENTATION", 0xBA86 },
+ { "MTP_FORMAT_UNDEFINED_MESSAGE", 0xBB00 },
+ { "MTP_FORMAT_ABSTRACT_MESSSAGE", 0xBB01 },
+ { "MTP_FORMAT_UNDEFINED_CONTACT", 0xBB80 },
+ { "MTP_FORMAT_ABSTRACT_CONTACT", 0xBB81 },
+ { "MTP_FORMAT_VCARD_2", 0xBB82 },
+ { 0, 0 },
+};
-const char* MtpDebug::getOperationCodeName(MtpOperationCode code) {
- const OperationCodeEntry* entry = sOperationCodes;
+static const CodeEntry sObjectPropCodes[] = {
+ { "MTP_PROPERTY_STORAGE_ID", 0xDC01 },
+ { "MTP_PROPERTY_OBJECT_FORMAT", 0xDC02 },
+ { "MTP_PROPERTY_PROTECTION_STATUS", 0xDC03 },
+ { "MTP_PROPERTY_OBJECT_SIZE", 0xDC04 },
+ { "MTP_PROPERTY_ASSOCIATION_TYPE", 0xDC05 },
+ { "MTP_PROPERTY_ASSOCIATION_DESC", 0xDC06 },
+ { "MTP_PROPERTY_OBJECT_FILE_NAME", 0xDC07 },
+ { "MTP_PROPERTY_DATE_CREATED", 0xDC08 },
+ { "MTP_PROPERTY_DATE_MODIFIED", 0xDC09 },
+ { "MTP_PROPERTY_KEYWORDS", 0xDC0A },
+ { "MTP_PROPERTY_PARENT_OBJECT", 0xDC0B },
+ { "MTP_PROPERTY_ALLOWED_FOLDER_CONTENTS", 0xDC0C },
+ { "MTP_PROPERTY_HIDDEN", 0xDC0D },
+ { "MTP_PROPERTY_SYSTEM_OBJECT", 0xDC0E },
+ { "MTP_PROPERTY_PERSISTENT_UID", 0xDC41 },
+ { "MTP_PROPERTY_SYNC_ID", 0xDC42 },
+ { "MTP_PROPERTY_PROPERTY_BAG", 0xDC43 },
+ { "MTP_PROPERTY_NAME", 0xDC44 },
+ { "MTP_PROPERTY_CREATED_BY", 0xDC45 },
+ { "MTP_PROPERTY_ARTIST", 0xDC46 },
+ { "MTP_PROPERTY_DATE_AUTHORED", 0xDC47 },
+ { "MTP_PROPERTY_DESCRIPTION", 0xDC48 },
+ { "MTP_PROPERTY_URL_REFERENCE", 0xDC49 },
+ { "MTP_PROPERTY_LANGUAGE_LOCALE", 0xDC4A },
+ { "MTP_PROPERTY_COPYRIGHT_INFORMATION", 0xDC4B },
+ { "MTP_PROPERTY_SOURCE", 0xDC4C },
+ { "MTP_PROPERTY_ORIGIN_LOCATION", 0xDC4D },
+ { "MTP_PROPERTY_DATE_ADDED", 0xDC4E },
+ { "MTP_PROPERTY_NON_CONSUMABLE", 0xDC4F },
+ { "MTP_PROPERTY_CORRUPT_UNPLAYABLE", 0xDC50 },
+ { "MTP_PROPERTY_PRODUCER_SERIAL_NUMBER", 0xDC51 },
+ { "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_FORMAT", 0xDC81 },
+ { "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_SIZE", 0xDC82 },
+ { "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_HEIGHT", 0xDC83 },
+ { "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_WIDTH", 0xDC84 },
+ { "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_DURATION", 0xDC85 },
+ { "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_DATA", 0xDC86 },
+ { "MTP_PROPERTY_WIDTH", 0xDC87 },
+ { "MTP_PROPERTY_HEIGHT", 0xDC88 },
+ { "MTP_PROPERTY_DURATION", 0xDC89 },
+ { "MTP_PROPERTY_RATING", 0xDC8A },
+ { "MTP_PROPERTY_TRACK", 0xDC8B },
+ { "MTP_PROPERTY_GENRE", 0xDC8C },
+ { "MTP_PROPERTY_CREDITS", 0xDC8D },
+ { "MTP_PROPERTY_LYRICS", 0xDC8E },
+ { "MTP_PROPERTY_SUBSCRIPTION_CONTENT_ID", 0xDC8F },
+ { "MTP_PROPERTY_PRODUCED_BY", 0xDC90 },
+ { "MTP_PROPERTY_USE_COUNT", 0xDC91 },
+ { "MTP_PROPERTY_SKIP_COUNT", 0xDC92 },
+ { "MTP_PROPERTY_LAST_ACCESSED", 0xDC93 },
+ { "MTP_PROPERTY_PARENTAL_RATING", 0xDC94 },
+ { "MTP_PROPERTY_META_GENRE", 0xDC95 },
+ { "MTP_PROPERTY_COMPOSER", 0xDC96 },
+ { "MTP_PROPERTY_EFFECTIVE_RATING", 0xDC97 },
+ { "MTP_PROPERTY_SUBTITLE", 0xDC98 },
+ { "MTP_PROPERTY_ORIGINAL_RELEASE_DATE", 0xDC99 },
+ { "MTP_PROPERTY_ALBUM_NAME", 0xDC9A },
+ { "MTP_PROPERTY_ALBUM_ARTIST", 0xDC9B },
+ { "MTP_PROPERTY_MOOD", 0xDC9C },
+ { "MTP_PROPERTY_DRM_STATUS", 0xDC9D },
+ { "MTP_PROPERTY_SUB_DESCRIPTION", 0xDC9E },
+ { "MTP_PROPERTY_IS_CROPPED", 0xDCD1 },
+ { "MTP_PROPERTY_IS_COLOUR_CORRECTED", 0xDCD2 },
+ { "MTP_PROPERTY_IMAGE_BIT_DEPTH", 0xDCD3 },
+ { "MTP_PROPERTY_F_NUMBER", 0xDCD4 },
+ { "MTP_PROPERTY_EXPOSURE_TIME", 0xDCD5 },
+ { "MTP_PROPERTY_EXPOSURE_INDEX", 0xDCD6 },
+ { "MTP_PROPERTY_TOTAL_BITRATE", 0xDE91 },
+ { "MTP_PROPERTY_BITRATE_TYPE", 0xDE92 },
+ { "MTP_PROPERTY_SAMPLE_RATE", 0xDE93 },
+ { "MTP_PROPERTY_NUMBER_OF_CHANNELS", 0xDE94 },
+ { "MTP_PROPERTY_AUDIO_BIT_DEPTH", 0xDE95 },
+ { "MTP_PROPERTY_SCAN_TYPE", 0xDE97 },
+ { "MTP_PROPERTY_AUDIO_WAVE_CODEC", 0xDE99 },
+ { "MTP_PROPERTY_AUDIO_BITRATE", 0xDE9A },
+ { "MTP_PROPERTY_VIDEO_FOURCC_CODEC", 0xDE9B },
+ { "MTP_PROPERTY_VIDEO_BITRATE", 0xDE9C },
+ { "MTP_PROPERTY_FRAMES_PER_THOUSAND_SECONDS", 0xDE9D },
+ { "MTP_PROPERTY_KEYFRAME_DISTANCE", 0xDE9E },
+ { "MTP_PROPERTY_BUFFER_SIZE", 0xDE9F },
+ { "MTP_PROPERTY_ENCODING_QUALITY", 0xDEA0 },
+ { "MTP_PROPERTY_ENCODING_PROFILE", 0xDEA1 },
+ { "MTP_PROPERTY_DISPLAY_NAME", 0xDCE0 },
+ { "MTP_PROPERTY_BODY_TEXT", 0xDCE1 },
+ { "MTP_PROPERTY_SUBJECT", 0xDCE2 },
+ { "MTP_PROPERTY_PRIORITY", 0xDCE3 },
+ { "MTP_PROPERTY_GIVEN_NAME", 0xDD00 },
+ { "MTP_PROPERTY_MIDDLE_NAMES", 0xDD01 },
+ { "MTP_PROPERTY_FAMILY_NAME", 0xDD02 },
+ { "MTP_PROPERTY_PREFIX", 0xDD03 },
+ { "MTP_PROPERTY_SUFFIX", 0xDD04 },
+ { "MTP_PROPERTY_PHONETIC_GIVEN_NAME", 0xDD05 },
+ { "MTP_PROPERTY_PHONETIC_FAMILY_NAME", 0xDD06 },
+ { "MTP_PROPERTY_EMAIL_PRIMARY", 0xDD07 },
+ { "MTP_PROPERTY_EMAIL_PERSONAL_1", 0xDD08 },
+ { "MTP_PROPERTY_EMAIL_PERSONAL_2", 0xDD09 },
+ { "MTP_PROPERTY_EMAIL_BUSINESS_1", 0xDD0A },
+ { "MTP_PROPERTY_EMAIL_BUSINESS_2", 0xDD0B },
+ { "MTP_PROPERTY_EMAIL_OTHERS", 0xDD0C },
+ { "MTP_PROPERTY_PHONE_NUMBER_PRIMARY", 0xDD0D },
+ { "MTP_PROPERTY_PHONE_NUMBER_PERSONAL", 0xDD0E },
+ { "MTP_PROPERTY_PHONE_NUMBER_PERSONAL_2", 0xDD0F },
+ { "MTP_PROPERTY_PHONE_NUMBER_BUSINESS", 0xDD10 },
+ { "MTP_PROPERTY_PHONE_NUMBER_BUSINESS_2", 0xDD11 },
+ { "MTP_PROPERTY_PHONE_NUMBER_MOBILE", 0xDD12 },
+ { "MTP_PROPERTY_PHONE_NUMBER_MOBILE_2", 0xDD13 },
+ { "MTP_PROPERTY_FAX_NUMBER_PRIMARY", 0xDD14 },
+ { "MTP_PROPERTY_FAX_NUMBER_PERSONAL", 0xDD15 },
+ { "MTP_PROPERTY_FAX_NUMBER_BUSINESS", 0xDD16 },
+ { "MTP_PROPERTY_PAGER_NUMBER", 0xDD17 },
+ { "MTP_PROPERTY_PHONE_NUMBER_OTHERS", 0xDD18 },
+ { "MTP_PROPERTY_PRIMARY_WEB_ADDRESS", 0xDD19 },
+ { "MTP_PROPERTY_PERSONAL_WEB_ADDRESS", 0xDD1A },
+ { "MTP_PROPERTY_BUSINESS_WEB_ADDRESS", 0xDD1B },
+ { "MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS", 0xDD1C },
+ { "MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS_2", 0xDD1D },
+ { "MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS_3", 0xDD1E },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_FULL", 0xDD1F },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_LINE_1", 0xDD20 },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_LINE_2", 0xDD21 },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_CITY", 0xDD22 },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_REGION", 0xDD23 },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_POSTAL_CODE", 0xDD24 },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_COUNTRY", 0xDD25 },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_FULL", 0xDD26 },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_LINE_1", 0xDD27 },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_LINE_2", 0xDD28 },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_CITY", 0xDD29 },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_REGION", 0xDD2A },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_POSTAL_CODE", 0xDD2B },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_COUNTRY", 0xDD2C },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_FULL", 0xDD2D },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_LINE_1", 0xDD2E },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_LINE_2", 0xDD2F },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_CITY", 0xDD30 },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_REGION", 0xDD31 },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_POSTAL_CODE", 0xDD32 },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_COUNTRY", 0xDD33 },
+ { "MTP_PROPERTY_ORGANIZATION_NAME", 0xDD34 },
+ { "MTP_PROPERTY_PHONETIC_ORGANIZATION_NAME", 0xDD35 },
+ { "MTP_PROPERTY_ROLE", 0xDD36 },
+ { "MTP_PROPERTY_BIRTHDATE", 0xDD37 },
+ { "MTP_PROPERTY_MESSAGE_TO", 0xDD40 },
+ { "MTP_PROPERTY_MESSAGE_CC", 0xDD41 },
+ { "MTP_PROPERTY_MESSAGE_BCC", 0xDD42 },
+ { "MTP_PROPERTY_MESSAGE_READ", 0xDD43 },
+ { "MTP_PROPERTY_MESSAGE_RECEIVED_TIME", 0xDD44 },
+ { "MTP_PROPERTY_MESSAGE_SENDER", 0xDD45 },
+ { "MTP_PROPERTY_ACTIVITY_BEGIN_TIME", 0xDD50 },
+ { "MTP_PROPERTY_ACTIVITY_END_TIME", 0xDD51 },
+ { "MTP_PROPERTY_ACTIVITY_LOCATION", 0xDD52 },
+ { "MTP_PROPERTY_ACTIVITY_REQUIRED_ATTENDEES", 0xDD54 },
+ { "MTP_PROPERTY_ACTIVITY_OPTIONAL_ATTENDEES", 0xDD55 },
+ { "MTP_PROPERTY_ACTIVITY_RESOURCES", 0xDD56 },
+ { "MTP_PROPERTY_ACTIVITY_ACCEPTED", 0xDD57 },
+ { "MTP_PROPERTY_ACTIVITY_TENTATIVE", 0xDD58 },
+ { "MTP_PROPERTY_ACTIVITY_DECLINED", 0xDD59 },
+ { "MTP_PROPERTY_ACTIVITY_REMAINDER_TIME", 0xDD5A },
+ { "MTP_PROPERTY_ACTIVITY_OWNER", 0xDD5B },
+ { "MTP_PROPERTY_ACTIVITY_STATUS", 0xDD5C },
+ { "MTP_PROPERTY_OWNER", 0xDD5D },
+ { "MTP_PROPERTY_EDITOR", 0xDD5E },
+ { "MTP_PROPERTY_WEBMASTER", 0xDD5F },
+ { "MTP_PROPERTY_URL_SOURCE", 0xDD60 },
+ { "MTP_PROPERTY_URL_DESTINATION", 0xDD61 },
+ { "MTP_PROPERTY_TIME_BOOKMARK", 0xDD62 },
+ { "MTP_PROPERTY_OBJECT_BOOKMARK", 0xDD63 },
+ { "MTP_PROPERTY_BYTE_BOOKMARK", 0xDD64 },
+ { "MTP_PROPERTY_LAST_BUILD_DATE", 0xDD70 },
+ { "MTP_PROPERTY_TIME_TO_LIVE", 0xDD71 },
+ { "MTP_PROPERTY_MEDIA_GUID", 0xDD72 },
+ { 0, 0 },
+};
+
+static const CodeEntry sDevicePropCodes[] = {
+ { "MTP_DEVICE_PROPERTY_UNDEFINED", 0x5000 },
+ { "MTP_DEVICE_PROPERTY_BATTERY_LEVEL", 0x5001 },
+ { "MTP_DEVICE_PROPERTY_FUNCTIONAL_MODE", 0x5002 },
+ { "MTP_DEVICE_PROPERTY_IMAGE_SIZE", 0x5003 },
+ { "MTP_DEVICE_PROPERTY_COMPRESSION_SETTING", 0x5004 },
+ { "MTP_DEVICE_PROPERTY_WHITE_BALANCE", 0x5005 },
+ { "MTP_DEVICE_PROPERTY_RGB_GAIN", 0x5006 },
+ { "MTP_DEVICE_PROPERTY_F_NUMBER", 0x5007 },
+ { "MTP_DEVICE_PROPERTY_FOCAL_LENGTH", 0x5008 },
+ { "MTP_DEVICE_PROPERTY_FOCUS_DISTANCE", 0x5009 },
+ { "MTP_DEVICE_PROPERTY_FOCUS_MODE", 0x500A },
+ { "MTP_DEVICE_PROPERTY_EXPOSURE_METERING_MODE", 0x500B },
+ { "MTP_DEVICE_PROPERTY_FLASH_MODE", 0x500C },
+ { "MTP_DEVICE_PROPERTY_EXPOSURE_TIME", 0x500D },
+ { "MTP_DEVICE_PROPERTY_EXPOSURE_PROGRAM_MODE", 0x500E },
+ { "MTP_DEVICE_PROPERTY_EXPOSURE_INDEX", 0x500F },
+ { "MTP_DEVICE_PROPERTY_EXPOSURE_BIAS_COMPENSATION", 0x5010 },
+ { "MTP_DEVICE_PROPERTY_DATETIME", 0x5011 },
+ { "MTP_DEVICE_PROPERTY_CAPTURE_DELAY", 0x5012 },
+ { "MTP_DEVICE_PROPERTY_STILL_CAPTURE_MODE", 0x5013 },
+ { "MTP_DEVICE_PROPERTY_CONTRAST", 0x5014 },
+ { "MTP_DEVICE_PROPERTY_SHARPNESS", 0x5015 },
+ { "MTP_DEVICE_PROPERTY_DIGITAL_ZOOM", 0x5016 },
+ { "MTP_DEVICE_PROPERTY_EFFECT_MODE", 0x5017 },
+ { "MTP_DEVICE_PROPERTY_BURST_NUMBER", 0x5018 },
+ { "MTP_DEVICE_PROPERTY_BURST_INTERVAL", 0x5019 },
+ { "MTP_DEVICE_PROPERTY_TIMELAPSE_NUMBER", 0x501A },
+ { "MTP_DEVICE_PROPERTY_TIMELAPSE_INTERVAL", 0x501B },
+ { "MTP_DEVICE_PROPERTY_FOCUS_METERING_MODE", 0x501C },
+ { "MTP_DEVICE_PROPERTY_UPLOAD_URL", 0x501D },
+ { "MTP_DEVICE_PROPERTY_ARTIST", 0x501E },
+ { "MTP_DEVICE_PROPERTY_COPYRIGHT_INFO", 0x501F },
+ { "MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER", 0xD401 },
+ { "MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME", 0xD402 },
+ { "MTP_DEVICE_PROPERTY_VOLUME", 0xD403 },
+ { "MTP_DEVICE_PROPERTY_SUPPORTED_FORMATS_ORDERED", 0xD404 },
+ { "MTP_DEVICE_PROPERTY_DEVICE_ICON", 0xD405 },
+ { "MTP_DEVICE_PROPERTY_PLAYBACK_RATE", 0xD410 },
+ { "MTP_DEVICE_PROPERTY_PLAYBACK_OBJECT", 0xD411 },
+ { "MTP_DEVICE_PROPERTY_PLAYBACK_CONTAINER_INDEX", 0xD412 },
+ { "MTP_DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO", 0xD406 },
+ { "MTP_DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE", 0xD407 },
+ { 0, 0 },
+};
+
+static const char* getCodeName(uint16_t code, const CodeEntry* table) {
+ const CodeEntry* entry = table;
while (entry->name) {
if (entry->code == code)
return entry->name;
entry++;
}
- return "*** UNKNOWN OPERATION ***";
+ return "UNKNOWN";
+}
+
+const char* MtpDebug::getOperationCodeName(MtpOperationCode code) {
+ return getCodeName(code, sOperationCodes);
+}
+
+const char* MtpDebug::getFormatCodeName(MtpOperationCode code) {
+ return getCodeName(code, sFormatCodes);
+}
+
+const char* MtpDebug::getObjectPropCodeName(MtpPropertyCode code) {
+ return getCodeName(code, sObjectPropCodes);
+}
+
+const char* MtpDebug::getDevicePropCodeName(MtpPropertyCode code) {
+ return getCodeName(code, sDevicePropCodes);
}
} // namespace android
diff --git a/media/mtp/MtpDebug.h b/media/mtp/MtpDebug.h
index 87eff7416512..5b53e317f616 100644
--- a/media/mtp/MtpDebug.h
+++ b/media/mtp/MtpDebug.h
@@ -27,6 +27,9 @@ namespace android {
class MtpDebug {
public:
static const char* getOperationCodeName(MtpOperationCode code);
+ static const char* getFormatCodeName(MtpObjectFormat code);
+ static const char* getObjectPropCodeName(MtpPropertyCode code);
+ static const char* getDevicePropCodeName(MtpPropertyCode code);
};
}; // namespace android
diff --git a/media/mtp/MtpProperty.cpp b/media/mtp/MtpProperty.cpp
index d6beb8a20a37..932ad6a0df13 100644
--- a/media/mtp/MtpProperty.cpp
+++ b/media/mtp/MtpProperty.cpp
@@ -31,6 +31,7 @@ MtpProperty::MtpProperty()
mDefaultArrayValues(NULL),
mCurrentArrayLength(0),
mCurrentArrayValues(NULL),
+ mGroupCode(0),
mFormFlag(kFormNone),
mEnumLength(0),
mEnumValues(NULL)
@@ -52,6 +53,7 @@ MtpProperty::MtpProperty(MtpPropertyCode propCode,
mDefaultArrayValues(NULL),
mCurrentArrayLength(0),
mCurrentArrayValues(NULL),
+ mGroupCode(0),
mFormFlag(kFormNone),
mEnumLength(0),
mEnumValues(NULL)
@@ -142,6 +144,7 @@ void MtpProperty::read(MtpDataPacket& packet, bool deviceProp) {
if (deviceProp)
readValue(packet, mCurrentValue);
}
+ mGroupCode = packet.getUInt32();
mFormFlag = packet.getUInt8();
if (mFormFlag == kFormRange) {
@@ -178,6 +181,7 @@ void MtpProperty::write(MtpDataPacket& packet) {
default:
writeValue(packet, mDefaultValue);
}
+ packet.putUInt32(mGroupCode);
packet.putUInt8(mFormFlag);
if (mFormFlag == kFormRange) {
writeValue(packet, mMinimumValue);
diff --git a/media/mtp/MtpProperty.h b/media/mtp/MtpProperty.h
index 4923d40276fc..c5b4e287d612 100644
--- a/media/mtp/MtpProperty.h
+++ b/media/mtp/MtpProperty.h
@@ -42,6 +42,8 @@ public:
kFormRange = 1,
kFormEnum = 2,
};
+
+ uint32_t mGroupCode;
uint8_t mFormFlag;
// for range form
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index 5f5cadfd98ac..30abfb8c5ef4 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -78,46 +78,6 @@ static const MtpEventCode kSupportedEventCodes[] = {
MTP_EVENT_OBJECT_REMOVED,
};
-static const MtpObjectProperty kSupportedObjectProperties[] = {
- MTP_PROPERTY_STORAGE_ID,
- MTP_PROPERTY_OBJECT_FORMAT,
- MTP_PROPERTY_OBJECT_SIZE,
- MTP_PROPERTY_OBJECT_FILE_NAME,
- MTP_PROPERTY_PARENT_OBJECT,
-};
-
-static const MtpObjectFormat kSupportedPlaybackFormats[] = {
- // MTP_FORMAT_UNDEFINED,
- MTP_FORMAT_ASSOCIATION,
- // MTP_FORMAT_TEXT,
- // MTP_FORMAT_HTML,
- MTP_FORMAT_MP3,
- //MTP_FORMAT_AVI,
- MTP_FORMAT_MPEG,
- // MTP_FORMAT_ASF,
- MTP_FORMAT_EXIF_JPEG,
- MTP_FORMAT_TIFF_EP,
- // MTP_FORMAT_BMP,
- MTP_FORMAT_GIF,
- MTP_FORMAT_JFIF,
- MTP_FORMAT_PNG,
- MTP_FORMAT_TIFF,
- MTP_FORMAT_WMA,
- MTP_FORMAT_OGG,
- MTP_FORMAT_AAC,
- // MTP_FORMAT_FLAC,
- // MTP_FORMAT_WMV,
- MTP_FORMAT_MP4_CONTAINER,
- MTP_FORMAT_MP2,
- MTP_FORMAT_3GP_CONTAINER,
- // MTP_FORMAT_ABSTRACT_AUDIO_ALBUM,
- MTP_FORMAT_ABSTRACT_AV_PLAYLIST,
- MTP_FORMAT_WPL_PLAYLIST,
- MTP_FORMAT_M3U_PLAYLIST,
- // MTP_FORMAT_MPL_PLAYLIST,
- MTP_FORMAT_PLS_PLAYLIST,
-};
-
MtpServer::MtpServer(int fd, MtpDatabase* database,
int fileGroup, int filePerm, int directoryPerm)
: mFD(fd),
@@ -268,7 +228,7 @@ void MtpServer::sendObjectRemoved(MtpObjectHandle handle) {
}
void MtpServer::initObjectProperties() {
- mObjectProperties.push(new MtpProperty(MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT16));
+ mObjectProperties.push(new MtpProperty(MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT32));
mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16));
mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64));
mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR));
@@ -354,6 +314,10 @@ MtpResponseCode MtpServer::doGetDeviceInfo() {
MtpStringBuffer string;
char prop_value[PROPERTY_VALUE_MAX];
+ MtpObjectFormatList* playbackFormats = mDatabase->getSupportedPlaybackFormats();
+ MtpObjectFormatList* captureFormats = mDatabase->getSupportedCaptureFormats();
+ MtpDevicePropertyList* deviceProperties = mDatabase->getSupportedDeviceProperties();
+
// fill in device info
mData.putUInt16(MTP_STANDARD_VERSION);
mData.putUInt32(6); // MTP Vendor Extension ID
@@ -365,10 +329,9 @@ MtpResponseCode MtpServer::doGetDeviceInfo() {
sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
mData.putAUInt16(kSupportedEventCodes,
sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported
- mData.putEmptyArray(); // Device Properties Supported
- mData.putEmptyArray(); // Capture Formats
- mData.putAUInt16(kSupportedPlaybackFormats,
- sizeof(kSupportedPlaybackFormats) / sizeof(uint16_t)); // Playback Formats
+ mData.putAUInt16(deviceProperties); // Device Properties Supported
+ mData.putAUInt16(captureFormats); // Capture Formats
+ mData.putAUInt16(playbackFormats); // Playback Formats
// FIXME
string.set("Google, Inc.");
mData.putString(string); // Manufacturer
@@ -383,6 +346,10 @@ MtpResponseCode MtpServer::doGetDeviceInfo() {
string.set(prop_value);
mData.putString(string); // Serial Number
+ delete playbackFormats;
+ delete captureFormats;
+ delete deviceProperties;
+
return MTP_RESPONSE_OK;
}
@@ -443,8 +410,9 @@ MtpResponseCode MtpServer::doGetObjectPropsSupported() {
if (!mSessionOpen)
return MTP_RESPONSE_SESSION_NOT_OPEN;
MtpObjectFormat format = mRequest.getParameter(1);
- mData.putAUInt16(kSupportedObjectProperties,
- sizeof(kSupportedObjectProperties) / sizeof(uint16_t));
+ MtpDevicePropertyList* properties = mDatabase->getSupportedObjectProperties(format);
+ mData.putAUInt16(properties);
+ delete properties;
return MTP_RESPONSE_OK;
}
diff --git a/media/mtp/MtpTypes.h b/media/mtp/MtpTypes.h
index 2a895a7ea380..7e3c009e57bf 100644
--- a/media/mtp/MtpTypes.h
+++ b/media/mtp/MtpTypes.h
@@ -78,6 +78,7 @@ typedef Vector<int16_t> Int16List;
typedef Vector<int32_t> Int32List;
typedef Vector<int64_t> Int64List;
+typedef UInt16List MtpObjectPropertyList;
typedef UInt16List MtpDevicePropertyList;
typedef UInt16List MtpObjectFormatList;
typedef UInt32List MtpObjectHandleList;
diff --git a/media/mtp/mtp.h b/media/mtp/mtp.h
index 224cfb9c26ec..b7afa6639326 100644
--- a/media/mtp/mtp.h
+++ b/media/mtp/mtp.h
@@ -465,8 +465,8 @@
// Storage Access Capability
#define MTP_STORAGE_READ_WRITE 0x0000
-#define MTP_STORAGE_READ_ONLY_WITHOUT_DELETE 0x0000
-#define MTP_STORAGE_READ_ONLY_WITH_DELETE 0x0000
+#define MTP_STORAGE_READ_ONLY_WITHOUT_DELETE 0x0001
+#define MTP_STORAGE_READ_ONLY_WITH_DELETE 0x0002
// Association Type
#define MTP_ASSOCIATION_TYPE_UNDEFINED 0x0000
diff --git a/media/tests/MediaFrameworkTest/AndroidManifest.xml b/media/tests/MediaFrameworkTest/AndroidManifest.xml
index 246f9fc528ab..f70a1451c91c 100644
--- a/media/tests/MediaFrameworkTest/AndroidManifest.xml
+++ b/media/tests/MediaFrameworkTest/AndroidManifest.xml
@@ -51,4 +51,9 @@
android:label="MediaRecorder stress tests InstrumentationRunner">
</instrumentation>
+ <instrumentation android:name=".MediaFrameworkPowerTestRunner"
+ android:targetPackage="com.android.mediaframeworktest"
+ android:label="Media Power tests InstrumentationRunner">
+ </instrumentation>
+
</manifest>
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPowerTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPowerTestRunner.java
new file mode 100755
index 000000000000..34db4dbd3bde
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPowerTestRunner.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest;
+
+import com.android.mediaframeworktest.power.MediaPlayerPowerTest;
+
+import junit.framework.TestSuite;
+
+import android.test.InstrumentationTestRunner;
+import android.test.InstrumentationTestSuite;
+
+
+/**
+ * Instrumentation Test Runner for all MediaPlayer tests.
+ *
+ * Running all tests:
+ *
+ * adb shell am instrument \
+ * -w com.android.mediaframeworktest/.MediaFrameworkPowerTestRunner
+ */
+
+public class MediaFrameworkPowerTestRunner extends InstrumentationTestRunner {
+
+ @Override
+ public TestSuite getAllTests() {
+ TestSuite suite = new InstrumentationTestSuite(this);
+ suite.addTestSuite(MediaPlayerPowerTest.class);
+ return suite;
+ }
+
+ @Override
+ public ClassLoader getLoader() {
+ return MediaFrameworkPowerTestRunner.class.getClassLoader();
+ }
+}
+
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java
index 3e33951e7a66..c7f461ecf558 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java
@@ -25,6 +25,11 @@ import com.android.mediaframeworktest.functional.MediaRecorderTest;
import com.android.mediaframeworktest.functional.SimTonesTest;
import com.android.mediaframeworktest.functional.MediaPlayerInvokeTest;
import com.android.mediaframeworktest.functional.MediaAudioManagerTest;
+import com.android.mediaframeworktest.functional.MediaAudioEffectTest;
+import com.android.mediaframeworktest.functional.MediaBassBoostTest;
+import com.android.mediaframeworktest.functional.MediaEqualizerTest;
+import com.android.mediaframeworktest.functional.MediaVirtualizerTest;
+import com.android.mediaframeworktest.functional.MediaVisualizerTest;
import junit.framework.TestSuite;
import android.test.InstrumentationTestRunner;
@@ -55,6 +60,11 @@ public class MediaFrameworkTestRunner extends InstrumentationTestRunner {
suite.addTestSuite(MediaMimeTest.class);
suite.addTestSuite(MediaPlayerInvokeTest.class);
suite.addTestSuite(MediaAudioManagerTest.class);
+ suite.addTestSuite(MediaAudioEffectTest.class);
+ suite.addTestSuite(MediaBassBoostTest.class);
+ suite.addTestSuite(MediaEqualizerTest.class);
+ suite.addTestSuite(MediaVirtualizerTest.class);
+ suite.addTestSuite(MediaVisualizerTest.class);
return suite;
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
index 9a48c923994d..ca6e99927042 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
@@ -35,6 +35,7 @@ public class MediaNames {
public static final String WAV = "/sdcard/media_api/music/rings_2ch.wav";
public static final String AMR = "/sdcard/media_api/music/test_amr_ietf.amr";
public static final String OGG = "/sdcard/media_api/music/Revelation.ogg";
+ public static final String SINE_200_1000 = "/sdcard/media_api/music/sine_200+1000Hz_44K_mo.wav";
public static final int MP3CBR_LENGTH = 71000;
public static final int MP3VBR_LENGTH = 71000;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioEffectTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioEffectTest.java
new file mode 100644
index 000000000000..fd939ae08ab3
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioEffectTest.java
@@ -0,0 +1,1492 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.functional;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+import android.content.res.AssetFileDescriptor;
+import android.media.AudioEffect;
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.AudioTrack;
+import android.media.EnvironmentalReverb;
+import android.media.Equalizer;
+import android.media.MediaPlayer;
+
+import android.os.Looper;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.util.UUID;
+
+/**
+ * Junit / Instrumentation test case for the media AudioTrack api
+
+ */
+public class MediaAudioEffectTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+ private String TAG = "MediaAudioEffectTest";
+
+ private AudioEffect mEffect = null;
+ private boolean mHasControl = false;
+ private boolean mIsEnabled = false;
+ private int mParameterChanged = -1;
+ private MediaPlayer mMediaPlayer = null;
+ private boolean mInitialized = false;
+ private Looper mLooper = null;
+ private int mError = 0;
+ private final Object lock = new Object();
+
+ public MediaAudioEffectTest() {
+ super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ private static void assumeTrue(String message, boolean cond) {
+ assertTrue("(assume)"+message, cond);
+ }
+
+ private void log(String testName, String message) {
+ Log.v(TAG, "["+testName+"] "+message);
+ }
+
+ private void loge(String testName, String message) {
+ Log.e(TAG, "["+testName+"] "+message);
+ }
+
+ //-----------------------------------------------------------------
+ // AUDIOEFFECT TESTS:
+ //----------------------------------
+
+ //-----------------------------------------------------------------
+ // 0 - static methods
+ //----------------------------------
+
+ //Test case 0.0: test queryEffects() and available effects
+ @LargeTest
+ public void test0_0QueryEffects() throws Exception {
+
+ AudioEffect.Descriptor[] desc = AudioEffect.queryEffects();
+
+ assertTrue("test0_0QueryEffects: number of effects < 4: "+desc.length, (desc.length >= 4));
+
+ boolean hasEQ = false;
+ boolean hasBassBoost = false;
+ boolean hasVirtualizer = false;
+ boolean hasEnvReverb = false;
+
+ for (int i = 0; i < desc.length; i++) {
+ if (desc[i].mType.equals(AudioEffect.EFFECT_TYPE_EQUALIZER)) {
+ hasEQ = true;
+ } if (desc[i].mType.equals(AudioEffect.EFFECT_TYPE_BASS_BOOST)) {
+ hasBassBoost = true;
+ } else if (desc[i].mType.equals(AudioEffect.EFFECT_TYPE_VIRTUALIZER)) {
+ hasVirtualizer = true;
+ }
+ else if (desc[i].mType.equals(AudioEffect.EFFECT_TYPE_ENV_REVERB)) {
+ hasEnvReverb = true;
+ }
+ }
+ assertTrue("test0_0QueryEffects: equalizer not found", hasEQ);
+ assertTrue("test0_0QueryEffects: bass boost not found", hasBassBoost);
+ assertTrue("test0_0QueryEffects: virtualizer not found", hasVirtualizer);
+ assertTrue("test0_0QueryEffects: environmental reverb not found", hasEnvReverb);
+ }
+
+ //-----------------------------------------------------------------
+ // 1 - constructor
+ //----------------------------------
+
+ //Test case 1.0: test constructor from effect type and get effect ID
+ @LargeTest
+ public void test1_0ConstructorFromType() throws Exception {
+ boolean result = true;
+ String msg = "test1_0ConstructorFromType()";
+ AudioEffect.Descriptor[] desc = AudioEffect.queryEffects();
+ assertTrue(msg+": no effects found", (desc.length != 0));
+ try {
+ AudioEffect effect = new AudioEffect(desc[0].mType,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ try {
+ assertTrue(msg +": invalid effect ID", (effect.getId() != 0));
+ } catch (IllegalStateException e) {
+ msg = msg.concat(": AudioEffect not initialized");
+ result = false;
+ } finally {
+ effect.release();
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Effect not found: "+desc[0].mName);
+ result = false;
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ result = false;
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.1: test constructor from effect uuid
+ @LargeTest
+ public void test1_1ConstructorFromUuid() throws Exception {
+ boolean result = true;
+ String msg = "test1_1ConstructorFromUuid()";
+ AudioEffect.Descriptor[] desc = AudioEffect.queryEffects();
+ assertTrue(msg+"no effects found", (desc.length != 0));
+ try {
+ AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_NULL,
+ desc[0].mUuid,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ effect.release();
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Effect not found: "+desc[0].mName);
+ result = false;
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ result = false;
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.2: test constructor failure from unknown type
+ @LargeTest
+ public void test1_2ConstructorUnknownType() throws Exception {
+ boolean result = false;
+ String msg = "test1_2ConstructorUnknownType()";
+
+ try {
+ AudioEffect effect = new AudioEffect(UUID.randomUUID(),
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ msg = msg.concat(": could create random AudioEffect");
+ if (effect != null) {
+ effect.release();
+ }
+ } catch (IllegalArgumentException e) {
+ result = true;
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.3: test getEnabled() failure when called on released effect
+ @LargeTest
+ public void test1_3GetEnabledAfterRelease() throws Exception {
+ boolean result = false;
+ String msg = "test1_3GetEnabledAfterRelease()";
+
+ try {
+ AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ effect.release();
+ try {
+ effect.getEnabled();
+ } catch (IllegalStateException e) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.4: test contructor on mediaPlayer audio session
+ @LargeTest
+ public void test1_4InsertOnMediaPlayer() throws Exception {
+ boolean result = false;
+ String msg = "test1_4InsertOnMediaPlayer()";
+
+ try {
+ MediaPlayer mp = new MediaPlayer();
+ mp.setDataSource(MediaNames.SHORTMP3);
+
+ AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ mp.getAudioSessionId());
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ try {
+ loge(msg, ": effect.setEnabled");
+ effect.setEnabled(true);
+ } catch (IllegalStateException e) {
+ msg = msg.concat(": AudioEffect not initialized");
+ }
+
+ result = true;
+ effect.release();
+ mp.release();
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ loge(msg, ": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ loge(msg, ": Effect library not loaded");
+ } catch (Exception e){
+ loge(msg, "Could not create media player:" + e);
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.5: test auxiliary effect attachement on MediaPlayer
+ @LargeTest
+ public void test1_5AuxiliaryOnMediaPlayer() throws Exception {
+ boolean result = false;
+ String msg = "test1_5AuxiliaryOnMediaPlayer()";
+
+ try {
+ MediaPlayer mp = new MediaPlayer();
+ mp.setDataSource(MediaNames.SHORTMP3);
+
+ AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ mp.attachAuxEffect(effect.getId());
+ mp.setAuxEffectSendLevel(1.0f);
+ result = true;
+ effect.release();
+ mp.release();
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ loge(msg, ": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ loge(msg, ": Effect library not loaded");
+ } catch (Exception e){
+ loge(msg, "Could not create media player:" + e);
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.6: test auxiliary effect attachement failure before setDatasource
+ @LargeTest
+ public void test1_6AuxiliaryOnMediaPlayerFailure() throws Exception {
+ boolean result = false;
+ String msg = "test1_6AuxiliaryOnMediaPlayerFailure()";
+
+ try {
+ createMediaPlayerLooper();
+ synchronized(lock) {
+ try {
+ lock.wait(1000);
+ } catch(Exception e) {
+ Log.e(TAG, "Looper creation: wait was interrupted.");
+ }
+ }
+ assertTrue(mInitialized); // mMediaPlayer has been initialized?
+ mError = 0;
+
+ AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ synchronized(lock) {
+ try {
+ mMediaPlayer.attachAuxEffect(effect.getId());
+ lock.wait(1000);
+ } catch(Exception e) {
+ Log.e(TAG, "Attach effect: wait was interrupted.");
+ }
+ }
+ assertTrue(msg + ": no error on attachAuxEffect", mError != 0);
+ result = true;
+ effect.release();
+ terminateMediaPlayerLooper();
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ loge(msg, ": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ loge(msg, ": Effect library not loaded");
+ } catch (Exception e){
+ loge(msg, "Could not create media player:" + e);
+ }
+ assertTrue(msg, result);
+ }
+
+
+ //Test case 1.7: test auxiliary effect attachement on AudioTrack
+ @LargeTest
+ public void test1_7AuxiliaryOnAudioTrack() throws Exception {
+ boolean result = false;
+ String msg = "test1_7AuxiliaryOnAudioTrack()";
+
+ try {
+ AudioTrack track = new AudioTrack(
+ AudioManager.STREAM_MUSIC,
+ 44100,
+ AudioFormat.CHANNEL_OUT_MONO,
+ AudioFormat.ENCODING_PCM_16BIT,
+ AudioTrack.getMinBufferSize(44100,
+ AudioFormat.CHANNEL_OUT_MONO,
+ AudioFormat.ENCODING_PCM_16BIT),
+ AudioTrack.MODE_STREAM);
+ assertNotNull(msg + ": could not create AudioTrack", track);
+ AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+
+ track.attachAuxEffect(effect.getId());
+ track.setAuxEffectSendLevel(1.0f);
+ result = true;
+ effect.release();
+ track.release();
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ loge(msg, ": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ loge(msg, ": Effect library not loaded");
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // 2 - enable/ disable
+ //----------------------------------
+
+
+ //Test case 2.0: test setEnabled() and getEnabled() in valid state
+ @LargeTest
+ public void test2_0SetEnabledGetEnabled() throws Exception {
+ boolean result = false;
+ String msg = "test2_0SetEnabledGetEnabled()";
+
+ try {
+ AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ try {
+ effect.setEnabled(true);
+ assertTrue(msg + ": invalid state from getEnabled", effect.getEnabled());
+ effect.setEnabled(false);
+ assertFalse(msg + ": invalid state to getEnabled", effect.getEnabled());
+ result = true;
+ } catch (IllegalStateException e) {
+ msg = msg.concat(": setEnabled() in wrong state");
+ } finally {
+ effect.release();
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ loge(msg, ": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ loge(msg, ": Effect library not loaded");
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 2.1: test setEnabled() throws exception after release
+ @LargeTest
+ public void test2_1SetEnabledAfterRelease() throws Exception {
+ boolean result = false;
+ String msg = "test2_1SetEnabledAfterRelease()";
+
+ try {
+ AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ effect.release();
+ try {
+ effect.setEnabled(true);
+ } catch (IllegalStateException e) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ loge(msg, ": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ loge(msg, ": Effect library not loaded");
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // 3 - set parameters
+ //----------------------------------
+
+ //Test case 3.0: test setParameter(byte[], byte[])
+ @LargeTest
+ public void test3_0SetParameterByteArrayByteArray() throws Exception {
+ boolean result = false;
+ String msg = "test3_0SetParameterByteArrayByteArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ byte[] param = intToByteArray(Equalizer.PARAM_CURRENT_PRESET);
+ byte[] value = shortToByteArray((short)0);
+ if (effect.setParameter(param, value) == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": setParameter() rejected");
+ loge(msg, "setParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("setParameter() called in wrong state");
+ loge(msg, "setParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 3.1: test setParameter(int, int)
+ @LargeTest
+ public void test3_1SetParameterIntInt() throws Exception {
+ boolean result = false;
+ String msg = "test3_1SetParameterIntInt()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ if (effect.setParameter(EnvironmentalReverb.PARAM_DECAY_TIME, 0)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": setParameter() rejected");
+ loge(msg, "setParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("setParameter() called in wrong state");
+ loge(msg, "setParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 3.2: test setParameter(int, short)
+ @LargeTest
+ public void test3_2SetParameterIntShort() throws Exception {
+ boolean result = false;
+ String msg = "test3_2SetParameterIntShort()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ if (effect.setParameter(Equalizer.PARAM_CURRENT_PRESET, (short)0)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": setParameter() rejected");
+ loge(msg, "setParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("setParameter() called in wrong state");
+ loge(msg, "setParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 3.3: test setParameter(int, byte[])
+ @LargeTest
+ public void test3_3SetParameterIntByteArray() throws Exception {
+ boolean result = false;
+ String msg = "test3_3SetParameterIntByteArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ byte[] value = shortToByteArray((short)0);
+ if (effect.setParameter(Equalizer.PARAM_CURRENT_PRESET, value)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": setParameter() rejected");
+ loge(msg, "setParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("setParameter() called in wrong state");
+ loge(msg, "setParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 3.4: test setParameter(int[], int[])
+ @LargeTest
+ public void test3_4SetParameterIntArrayIntArray() throws Exception {
+ boolean result = false;
+ String msg = "test3_4SetParameterIntArrayIntArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ int[] param = new int[1];
+ int[] value = new int[1];
+ param[0] = EnvironmentalReverb.PARAM_DECAY_TIME;
+ value[0] = 0;
+ if (effect.setParameter(param, value)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": setParameter() rejected");
+ loge(msg, "setParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("setParameter() called in wrong state");
+ loge(msg, "setParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 3.5: test setParameter(int[], short[])
+ @LargeTest
+ public void test3_5SetParameterIntArrayShortArray() throws Exception {
+ boolean result = false;
+ String msg = "test3_5SetParameterIntArrayShortArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ int[] param = new int[1];
+ short[] value = new short[1];
+ param[0] = Equalizer.PARAM_CURRENT_PRESET;
+ value[0] = (short)0;
+ if (effect.setParameter(param, value)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": setParameter() rejected");
+ loge(msg, "setParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("setParameter() called in wrong state");
+ loge(msg, "setParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 3.6: test setParameter(int[], byte[])
+ @LargeTest
+ public void test3_6SetParameterIntArrayByteArray() throws Exception {
+ boolean result = false;
+ String msg = "test3_6SetParameterIntArrayByteArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ int[] param = new int[1];
+ byte[] value = shortToByteArray((short)0);
+ param[0] = Equalizer.PARAM_CURRENT_PRESET;
+ if (effect.setParameter(param, value)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": setParameter() rejected");
+ loge(msg, "setParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("setParameter() called in wrong state");
+ loge(msg, "setParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 3.7: test setParameter() throws exception after release()
+ @LargeTest
+ public void test3_7SetParameterAfterRelease() throws Exception {
+ boolean result = false;
+ String msg = "test3_7SetParameterAfterRelease()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ effect.release();
+ effect.setParameter(Equalizer.PARAM_CURRENT_PRESET, (short)0);
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": setParameter() rejected");
+ loge(msg, "setParameter() rejected");
+ } catch (IllegalStateException e) {
+ result = true;
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // 4 - get parameters
+ //----------------------------------
+
+ //Test case 4.0: test getParameter(byte[], byte[])
+ @LargeTest
+ public void test4_0GetParameterByteArrayByteArray() throws Exception {
+ boolean result = false;
+ String msg = "test4_0GetParameterByteArrayByteArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ byte[] param = intToByteArray(Equalizer.PARAM_CURRENT_PRESET);
+ byte[] value = new byte[2];
+ if (effect.getParameter(param, value) == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": getParameter() rejected");
+ loge(msg, "getParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("getParameter() called in wrong state");
+ loge(msg, "getParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 4.1: test getParameter(int, int[])
+ @LargeTest
+ public void test4_1GetParameterIntIntArray() throws Exception {
+ boolean result = false;
+ String msg = "test4_1GetParameterIntIntArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ int[] value = new int[1];
+ if (effect.getParameter(EnvironmentalReverb.PARAM_DECAY_TIME, value)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": getParameter() rejected");
+ loge(msg, "getParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("getParameter() called in wrong state");
+ loge(msg, "getParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 4.2: test getParameter(int, short[])
+ @LargeTest
+ public void test4_2GetParameterIntShortArray() throws Exception {
+ boolean result = false;
+ String msg = "test4_2GetParameterIntShortArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ short[] value = new short[1];
+ if (effect.getParameter(Equalizer.PARAM_CURRENT_PRESET, value)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": getParameter() rejected");
+ loge(msg, "getParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("getParameter() called in wrong state");
+ loge(msg, "getParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 4.3: test getParameter(int, byte[])
+ @LargeTest
+ public void test4_3GetParameterIntByteArray() throws Exception {
+ boolean result = false;
+ String msg = "test4_3GetParameterIntByteArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ byte[] value = new byte[2];
+ if (effect.getParameter(Equalizer.PARAM_CURRENT_PRESET, value)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": getParameter() rejected");
+ loge(msg, "getParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("getParameter() called in wrong state");
+ loge(msg, "getParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 4.4: test getParameter(int[], int[])
+ @LargeTest
+ public void test4_4GetParameterIntArrayIntArray() throws Exception {
+ boolean result = false;
+ String msg = "test4_4GetParameterIntArrayIntArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ int[] param = new int[1];
+ int[] value = new int[1];
+ param[0] = EnvironmentalReverb.PARAM_DECAY_TIME;
+ if (effect.getParameter(param, value)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": getParameter() rejected");
+ loge(msg, "getParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("getParameter() called in wrong state");
+ loge(msg, "getParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 4.5: test getParameter(int[], short[])
+ @LargeTest
+ public void test4_5GetParameterIntArrayShortArray() throws Exception {
+ boolean result = false;
+ String msg = "test4_5GetParameterIntArrayShortArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ int[] param = new int[1];
+ short[] value = new short[1];
+ param[0] = Equalizer.PARAM_CURRENT_PRESET;
+ if (effect.getParameter(param, value)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": getParameter() rejected");
+ loge(msg, "getParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("getParameter() called in wrong state");
+ loge(msg, "getParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 4.6: test getParameter(int[], byte[])
+ @LargeTest
+ public void test4_6GetParameterIntArrayByteArray() throws Exception {
+ boolean result = false;
+ String msg = "test4_6GetParameterIntArrayByteArray()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ int[] param = new int[1];
+ byte[] value = new byte[2];
+ param[0] = Equalizer.PARAM_CURRENT_PRESET;
+ if (effect.getParameter(param, value)
+ == AudioEffect.SUCCESS) {
+ result = true;
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": getParameter() rejected");
+ loge(msg, "getParameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("getParameter() called in wrong state");
+ loge(msg, "getParameter() called in wrong state");
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 4.7: test getParameter() throws exception after release()
+ @LargeTest
+ public void test4_7GetParameterAfterRelease() throws Exception {
+ boolean result = false;
+ String msg = "test4_7GetParameterAfterRelease()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ effect.release();
+ short[] value = new short[1];
+ effect.getParameter(Equalizer.PARAM_CURRENT_PRESET, value);
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": getParameter() rejected");
+ loge(msg, "getParameter() rejected");
+ } catch (IllegalStateException e) {
+ result = true;
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // 5 priority and listeners
+ //----------------------------------
+
+ //Test case 5.0: test control passed to higher priority client
+ @LargeTest
+ public void test5_0setEnabledLowerPriority() throws Exception {
+ boolean result = false;
+ String msg = "test5_0setEnabledLowerPriority()";
+ AudioEffect effect1 = null;
+ AudioEffect effect2 = null;
+ try {
+ effect1 = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ effect2 = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 1,
+ 0);
+
+ assertNotNull(msg + ": could not create AudioEffect", effect1);
+ assertNotNull(msg + ": could not create AudioEffect", effect2);
+
+ assertTrue(msg + ": Effect2 does not have control", effect2.hasControl());
+ assertFalse(msg + ": Effect1 has control", effect1.hasControl());
+ assertTrue(msg + ": Effect1 can enable",
+ effect1.setEnabled(true) == AudioEffect.ERROR_INVALID_OPERATION);
+ assertFalse(msg + ": Effect1 has enabled", effect2.getEnabled());
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Effect not found");
+ result = false;
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ result = false;
+ } finally {
+ if (effect1 != null) {
+ effect1.release();
+ }
+ if (effect2 != null) {
+ effect2.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 5.1: test control passed to higher priority client
+ @LargeTest
+ public void test5_1setParameterLowerPriority() throws Exception {
+ boolean result = false;
+ String msg = "test5_1setParameterLowerPriority()";
+ AudioEffect effect1 = null;
+ AudioEffect effect2 = null;
+ try {
+ effect1 = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ effect2 = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 1,
+ 0);
+
+ assertNotNull(msg + ": could not create AudioEffect", effect1);
+ assertNotNull(msg + ": could not create AudioEffect", effect2);
+
+ int status = effect2.setParameter(Equalizer.PARAM_CURRENT_PRESET, (short)0);
+ assertEquals(msg + ": Effect2 setParameter failed",
+ AudioEffect.SUCCESS, status);
+
+ status = effect1.setParameter(Equalizer.PARAM_CURRENT_PRESET, (short)1);
+ assertEquals(msg + ": Effect1 setParameter did not fail",
+ AudioEffect.ERROR_INVALID_OPERATION, status);
+
+ short[] value = new short[1];
+ status = effect2.getParameter(Equalizer.PARAM_CURRENT_PRESET, value);
+ assertEquals(msg + ": Effect2 getParameter failed",
+ AudioEffect.SUCCESS, status);
+ assertEquals(msg + ": Effect1 changed parameter",
+ (short)0, value[0]);
+
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Effect not found");
+ result = false;
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ result = false;
+ } finally {
+ if (effect1 != null) {
+ effect1.release();
+ }
+ if (effect2 != null) {
+ effect2.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 5.2: test control status listener
+ @LargeTest
+ public void test5_2ControlStatusListener() throws Exception {
+ boolean result = false;
+ String msg = "test5_2ControlStatusListener()";
+ mEffect = null;
+ AudioEffect effect2 = null;
+ try {
+ mHasControl = true;
+ createListenerLooper(true, false, false);
+ synchronized(lock) {
+ try {
+ lock.wait(1000);
+ } catch(Exception e) {
+ Log.e(TAG, "Looper creation: wait was interrupted.");
+ }
+ }
+ assertTrue(mInitialized);
+ synchronized(lock) {
+ try {
+ effect2 = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 1,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect2);
+ lock.wait(1000);
+ } catch(Exception e) {
+ Log.e(TAG, "Create second effect: wait was interrupted.");
+ }
+ }
+ assertFalse(msg + ": effect control not lost by effect1", mHasControl);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ loge(msg, ": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ loge(msg, ": Effect library not loaded");
+ } catch (Exception e){
+ loge(msg, "Could not create media player:" + e);
+ } finally {
+ terminateListenerLooper();
+ if (effect2 != null) {
+ effect2.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 5.3: test enable status listener
+ @LargeTest
+ public void test5_3EnableStatusListener() throws Exception {
+ boolean result = false;
+ String msg = "test5_3EnableStatusListener()";
+ mEffect = null;
+ AudioEffect effect2 = null;
+ try {
+ createListenerLooper(false, true, false);
+ synchronized(lock) {
+ try {
+ lock.wait(1000);
+ } catch(Exception e) {
+ Log.e(TAG, "Looper creation: wait was interrupted.");
+ }
+ }
+ assertTrue(mInitialized);
+ mEffect.setEnabled(true);
+ mIsEnabled = true;
+ effect2 = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 1,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect2);
+ assertTrue(msg + ": effect not enabled", effect2.getEnabled());
+ synchronized(lock) {
+ try {
+ effect2.setEnabled(false);
+ lock.wait(1000);
+ } catch(Exception e) {
+ Log.e(TAG, "Create second effect: wait was interrupted.");
+ }
+ }
+ assertFalse(msg + ": enable status not updated", mIsEnabled);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ loge(msg, ": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ loge(msg, ": Effect library not loaded");
+ } catch (Exception e){
+ loge(msg, "Could not create media player:" + e);
+ } finally {
+ terminateListenerLooper();
+ if (effect2 != null) {
+ effect2.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 5.4: test parameter changed listener
+ @LargeTest
+ public void test5_4ParameterChangedListener() throws Exception {
+ boolean result = false;
+ String msg = "test5_4ParameterChangedListener()";
+ mEffect = null;
+ AudioEffect effect2 = null;
+ try {
+ createListenerLooper(false, false, true);
+ synchronized(lock) {
+ try {
+ lock.wait(1000);
+ } catch(Exception e) {
+ Log.e(TAG, "Looper creation: wait was interrupted.");
+ }
+ }
+ assertTrue(mInitialized);
+ effect2 = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 1,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect2);
+ synchronized(lock) {
+ try {
+ mParameterChanged = -1;
+ effect2.setParameter(Equalizer.PARAM_CURRENT_PRESET, (short)0);
+ lock.wait(1000);
+ } catch(Exception e) {
+ Log.e(TAG, "Create second effect: wait was interrupted.");
+ }
+ }
+ assertEquals(msg + ": parameter change not received",
+ Equalizer.PARAM_CURRENT_PRESET, mParameterChanged);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ loge(msg, ": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ loge(msg, ": Effect library not loaded");
+ } catch (Exception e){
+ loge(msg, "Could not create media player:" + e);
+ } finally {
+ terminateListenerLooper();
+ if (effect2 != null) {
+ effect2.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // 6 command method
+ //----------------------------------
+
+
+ //Test case 6.0: test command method
+ @LargeTest
+ public void test6_0Command() throws Exception {
+ boolean result = false;
+ String msg = "test6_0Command()";
+ AudioEffect effect = null;
+ try {
+ effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull(msg + ": could not create AudioEffect", effect);
+ try {
+ byte[] cmd = new byte[0];
+ byte[] reply = new byte[4];
+ int status = effect.command(3, cmd, reply);
+ assertEquals(msg + ": command failed", AudioEffect.SUCCESS, status);
+ assertTrue(msg + ": effect not enabled", effect.getEnabled());
+ result = true;
+ } catch (IllegalStateException e) {
+ msg = msg.concat(": command in illegal state");
+ }
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ loge(msg, ": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ loge(msg, ": Effect library not loaded");
+ } catch (Exception e){
+ loge(msg, "Could not create media player:" + e);
+ } finally {
+ if (effect != null) {
+ effect.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // private methods
+ //----------------------------------
+
+ /*
+ * Initializes the message looper so that the MediaPlayer object can
+ * receive the callback messages.
+ */
+ private void createMediaPlayerLooper() {
+ new Thread() {
+ @Override
+ public void run() {
+ // Set up a looper to be used by mMediaPlayer.
+ Looper.prepare();
+
+ // Save the looper so that we can terminate this thread
+ // after we are done with it.
+ mLooper = Looper.myLooper();
+
+ mMediaPlayer = new MediaPlayer();
+ mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
+ public boolean onError(MediaPlayer player, int what, int extra) {
+ synchronized(lock) {
+ mError = what;
+ lock.notify();
+ }
+ return true;
+ }
+ });
+ mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+ public void onCompletion(MediaPlayer player) {
+ synchronized(lock) {
+ lock.notify();
+ }
+ }
+ });
+ synchronized(lock) {
+ mInitialized = true;
+ lock.notify();
+ }
+ Looper.loop(); // Blocks forever until Looper.quit() is called.
+ }
+ }.start();
+ }
+ /*
+ * Terminates the message looper thread.
+ */
+ private void terminateMediaPlayerLooper() {
+ if (mLooper != null) {
+ mLooper.quit();
+ mLooper = null;
+ }
+ if (mMediaPlayer != null) {
+ mMediaPlayer.release();
+ }
+ }
+
+ /*
+ * Initializes the message looper fro effect listener
+ */
+ class ListenerThread extends Thread {
+ boolean mControl;
+ boolean mEnable;
+ boolean mParameter;
+
+ public ListenerThread(boolean control, boolean enable, boolean parameter) {
+ super();
+ mControl = control;
+ mEnable = enable;
+ mParameter = parameter;
+ }
+ }
+ private void createListenerLooper(boolean control, boolean enable, boolean parameter) {
+
+ new ListenerThread(control, enable, parameter) {
+ @Override
+ public void run() {
+ // Set up a looper to be used by mEffect.
+ Looper.prepare();
+
+ // Save the looper so that we can terminate this thread
+ // after we are done with it.
+ mLooper = Looper.myLooper();
+
+ mEffect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
+ AudioEffect.EFFECT_TYPE_NULL,
+ 0,
+ 0);
+ assertNotNull("could not create AudioEffect", mEffect);
+
+ if (mControl) {
+ mEffect.setControlStatusListener(new AudioEffect.OnControlStatusChangeListener() {
+ public void onControlStatusChange(AudioEffect effect, boolean controlGranted) {
+ synchronized(lock) {
+ if (effect == mEffect) {
+ mHasControl = controlGranted;
+ lock.notify();
+ }
+ }
+ }
+ });
+ }
+ if (mEnable) {
+ mEffect.setEnableStatusListener(new AudioEffect.OnEnableStatusChangeListener() {
+ public void onEnableStatusChange(AudioEffect effect, boolean enabled) {
+ synchronized(lock) {
+ if (effect == mEffect) {
+ mIsEnabled = enabled;
+ lock.notify();
+ }
+ }
+ }
+ });
+ }
+ if (mParameter) {
+ mEffect.setParameterListener(new AudioEffect.OnParameterChangeListener() {
+ public void onParameterChange(AudioEffect effect, int status, byte[] param,
+ byte[] value) {
+ synchronized(lock) {
+ if (effect == mEffect) {
+ mParameterChanged = byteArrayToInt(param);
+ lock.notify();
+ }
+ }
+ }
+ });
+ }
+
+ synchronized(lock) {
+ mInitialized = true;
+ lock.notify();
+ }
+ Looper.loop(); // Blocks forever until Looper.quit() is called.
+ }
+ }.start();
+ }
+ /*
+ * Terminates the listener looper thread.
+ */
+ private void terminateListenerLooper() {
+ if (mEffect != null) {
+ mEffect.release();
+ mEffect = null;
+ }
+ if (mLooper != null) {
+ mLooper.quit();
+ mLooper = null;
+ }
+ }
+
+ protected int byteArrayToInt(byte[] valueBuf) {
+ return byteArrayToInt(valueBuf, 0);
+
+ }
+
+ protected int byteArrayToInt(byte[] valueBuf, int offset) {
+ ByteBuffer converter = ByteBuffer.wrap(valueBuf);
+ converter.order(ByteOrder.nativeOrder());
+ return converter.getInt(offset);
+
+ }
+
+ protected byte[] intToByteArray(int value) {
+ ByteBuffer converter = ByteBuffer.allocate(4);
+ converter.order(ByteOrder.nativeOrder());
+ converter.putInt(value);
+ return converter.array();
+ }
+
+ protected short byteArrayToShort(byte[] valueBuf) {
+ return byteArrayToShort(valueBuf, 0);
+ }
+
+ protected short byteArrayToShort(byte[] valueBuf, int offset) {
+ ByteBuffer converter = ByteBuffer.wrap(valueBuf);
+ converter.order(ByteOrder.nativeOrder());
+ return converter.getShort(offset);
+
+ }
+
+ protected byte[] shortToByteArray(short value) {
+ ByteBuffer converter = ByteBuffer.allocate(2);
+ converter.order(ByteOrder.nativeOrder());
+ short sValue = (short) value;
+ converter.putShort(sValue);
+ return converter.array();
+ }
+
+}
+
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java
new file mode 100644
index 000000000000..8a68c5eef68a
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.functional;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.media.AudioEffect;
+import android.media.AudioManager;
+import android.media.BassBoost;
+import android.media.Visualizer;
+import android.media.MediaPlayer;
+
+import android.os.Looper;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.util.UUID;
+
+/**
+ * Junit / Instrumentation test case for the media AudioTrack api
+
+ */
+public class MediaBassBoostTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+ private String TAG = "MediaBassBoostTest";
+ private final static int MIN_ENERGY_RATIO_2 = 4;
+ private final static short TEST_STRENGTH = 500;
+
+ private BassBoost mBassBoost = null;
+ private int mSession = -1;
+
+ public MediaBassBoostTest() {
+ super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ releaseBassBoost();
+ }
+
+ private static void assumeTrue(String message, boolean cond) {
+ assertTrue("(assume)"+message, cond);
+ }
+
+ private void log(String testName, String message) {
+ Log.v(TAG, "["+testName+"] "+message);
+ }
+
+ private void loge(String testName, String message) {
+ Log.e(TAG, "["+testName+"] "+message);
+ }
+
+ //-----------------------------------------------------------------
+ // BASS BOOST TESTS:
+ //----------------------------------
+
+
+ //-----------------------------------------------------------------
+ // 0 - constructor
+ //----------------------------------
+
+ //Test case 0.0: test constructor and release
+ @LargeTest
+ public void test0_0ConstructorAndRelease() throws Exception {
+ boolean result = false;
+ String msg = "test1_0ConstructorAndRelease()";
+ BassBoost bb = null;
+ try {
+ bb = new BassBoost(0, 0);
+ assertNotNull(msg + ": could not create BassBoost", bb);
+ try {
+ assertTrue(msg +": invalid effect ID", (bb.getId() != 0));
+ } catch (IllegalStateException e) {
+ msg = msg.concat(": BassBoost not initialized");
+ }
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": BassBoost not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ } finally {
+ if (bb != null) {
+ bb.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // 1 - get/set parameters
+ //----------------------------------
+
+ //Test case 1.0: test strength
+ @LargeTest
+ public void test1_0Strength() throws Exception {
+ boolean result = false;
+ String msg = "test1_0Strength()";
+ getBassBoost(0);
+ try {
+ if (mBassBoost.getStrengthSupported()) {
+ mBassBoost.setStrength((short)TEST_STRENGTH);
+ short strength = mBassBoost.getRoundedStrength();
+ // allow 10% difference between set strength and rounded strength
+ assertTrue(msg +": got incorrect strength",
+ ((float)strength > (float)TEST_STRENGTH * 0.9f) &&
+ ((float)strength < (float)TEST_STRENGTH * 1.1f));
+ } else {
+ short strength = mBassBoost.getRoundedStrength();
+ assertTrue(msg +": got incorrect strength", strength >= 0 && strength <= 1000);
+ }
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } finally {
+ releaseBassBoost();
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.1: test properties
+ @LargeTest
+ public void test1_1Properties() throws Exception {
+ boolean result = false;
+ String msg = "test1_1Properties()";
+ getBassBoost(0);
+ try {
+ BassBoost.Settings settings = mBassBoost.getProperties();
+ String str = settings.toString();
+ settings = new BassBoost.Settings(str);
+ mBassBoost.setProperties(settings);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } finally {
+ releaseBassBoost();
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // 2 - Effect action
+ //----------------------------------
+
+ //Test case 2.0: test actual bass boost influence on sound
+ @LargeTest
+ public void test2_0SoundModification() throws Exception {
+ boolean result = false;
+ String msg = "test2_0SoundModification()";
+ EnergyProbe probe = null;
+ AudioEffect vc = null;
+ MediaPlayer mp = null;
+ AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+ int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+ am.setStreamVolume(AudioManager.STREAM_MUSIC,
+ am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
+ 0);
+
+ try {
+ probe = new EnergyProbe(0);
+ // creating a volume controller on output mix ensures that ro.audio.silent mutes
+ // audio after the effects and not before
+ vc = new AudioEffect(
+ AudioEffect.EFFECT_TYPE_NULL,
+ UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"),
+ 0,
+ 0);
+ vc.setEnabled(true);
+
+ mp = new MediaPlayer();
+ mp.setDataSource(MediaNames.SINE_200_1000);
+ mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ getBassBoost(mp.getAudioSessionId());
+ mp.prepare();
+ mp.start();
+ Thread.sleep(200);
+ // measure reference energy around 1kHz
+ int refEnergy200 = probe.capture(200);
+ int refEnergy1000 = probe.capture(1000);
+ mBassBoost.setStrength((short)1000);
+ mBassBoost.setEnabled(true);
+ Thread.sleep(500);
+ // measure energy around 1kHz with band level at min
+ int energy200 = probe.capture(200);
+ int energy1000 = probe.capture(1000);
+ // verify that the energy ration between low and high frequencies is at least
+ // MIN_ENERGY_RATIO_2 times higher with bassboost on.
+ assertTrue(msg + ": bass boost has no effect",
+ ((float)energy200/(float)energy1000) >
+ (MIN_ENERGY_RATIO_2 * ((float)refEnergy200/(float)refEnergy1000)));
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } catch (InterruptedException e) {
+ loge(msg, "sleep() interrupted");
+ }
+ finally {
+ releaseBassBoost();
+ if (mp != null) {
+ mp.release();
+ }
+ if (vc != null) {
+ vc.release();
+ }
+ if (probe != null) {
+ probe.release();
+ }
+ am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+ }
+ assertTrue(msg, result);
+ }
+ //-----------------------------------------------------------------
+ // private methods
+ //----------------------------------
+
+ private class EnergyProbe {
+ Visualizer mVisualizer = null;
+ private byte[] mFft = new byte[1024];
+
+ public EnergyProbe(int session) {
+ mVisualizer = new Visualizer(session);
+ mVisualizer.setCaptureSize(1024);
+ }
+
+ public int capture(int freq) throws InterruptedException {
+ int energy = 0;
+ int count = 0;
+ if (mVisualizer != null) {
+ mVisualizer.setEnabled(true);
+ for (int i = 0; i < 10; i++) {
+ if (mVisualizer.getFft(mFft) == Visualizer.SUCCESS) {
+ // TODO: check speex FFT as it seems to return only the number of points
+ // correspondong to valid part of the spectrum (< Fs).
+ // e.g., if the number of points is 1024, it covers the frequency range
+ // 0 to 22050 instead of 0 to 44100 as expected from an FFT.
+ int bin = freq / (22050 / 1024);
+ int tmp = 0;
+ for (int j = bin-2; j < bin+3; j++) {
+ tmp += (int)mFft[j] * (int)mFft[j];
+ }
+ energy += tmp/5;
+ count++;
+ }
+ Thread.sleep(50);
+ }
+ mVisualizer.setEnabled(false);
+ }
+ if (count == 0) {
+ return 0;
+ }
+ return energy/count;
+ }
+
+ public void release() {
+ if (mVisualizer != null) {
+ mVisualizer.release();
+ mVisualizer = null;
+ }
+ }
+ }
+
+ private void getBassBoost(int session) {
+ if (mBassBoost == null || session != mSession) {
+ if (session != mSession && mBassBoost != null) {
+ mBassBoost.release();
+ mBassBoost = null;
+ }
+ try {
+ mBassBoost = new BassBoost(0, session);
+ mSession = session;
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "getBassBoost() BassBoost not found exception: "+e);
+ } catch (UnsupportedOperationException e) {
+ Log.e(TAG, "getBassBoost() Effect library not loaded exception: "+e);
+ }
+ }
+ assertNotNull("could not create mBassBoost", mBassBoost);
+ }
+
+ private void releaseBassBoost() {
+ if (mBassBoost != null) {
+ mBassBoost.release();
+ mBassBoost = null;
+ }
+ }
+
+}
+
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java
new file mode 100644
index 000000000000..e46887b3e4d2
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.functional;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.media.AudioEffect;
+import android.media.AudioManager;
+import android.media.Equalizer;
+import android.media.Visualizer;
+import android.media.MediaPlayer;
+
+import android.os.Looper;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.util.UUID;
+
+/**
+ * Junit / Instrumentation test case for the media AudioTrack api
+
+ */
+public class MediaEqualizerTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+ private String TAG = "MediaEqualizerTest";
+ private final static int MIN_NUMBER_OF_BANDS = 4;
+ private final static int MIN_BAND_LEVEL = -1500;
+ private final static int MAX_BAND_LEVEL = 1500;
+ private final static int TEST_FREQUENCY_MILLIHERTZ = 1000000;
+ private final static int MIN_NUMBER_OF_PRESETS = 4;
+ private Equalizer mEqualizer = null;
+ private int mSession = -1;
+
+ public MediaEqualizerTest() {
+ super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ releaseEqualizer();
+ }
+
+ private static void assumeTrue(String message, boolean cond) {
+ assertTrue("(assume)"+message, cond);
+ }
+
+ private void log(String testName, String message) {
+ Log.v(TAG, "["+testName+"] "+message);
+ }
+
+ private void loge(String testName, String message) {
+ Log.e(TAG, "["+testName+"] "+message);
+ }
+
+ //-----------------------------------------------------------------
+ // EQUALIZER TESTS:
+ //----------------------------------
+
+
+ //-----------------------------------------------------------------
+ // 0 - constructor
+ //----------------------------------
+
+ //Test case 0.0: test constructor and release
+ @LargeTest
+ public void test0_0ConstructorAndRelease() throws Exception {
+ boolean result = false;
+ String msg = "test1_0ConstructorAndRelease()";
+ Equalizer eq = null;
+ try {
+ eq = new Equalizer(0, 0);
+ assertNotNull(msg + ": could not create Equalizer", eq);
+ try {
+ assertTrue(msg +": invalid effect ID", (eq.getId() != 0));
+ } catch (IllegalStateException e) {
+ msg = msg.concat(": Equalizer not initialized");
+ }
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Equalizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ } finally {
+ if (eq != null) {
+ eq.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+
+ //-----------------------------------------------------------------
+ // 1 - get/set parameters
+ //----------------------------------
+
+ //Test case 1.0: test setBandLevel() and getBandLevel()
+ @LargeTest
+ public void test1_0BandLevel() throws Exception {
+ boolean result = false;
+ String msg = "test1_0BandLevel()";
+ getEqualizer(0);
+ try {
+ short numBands = mEqualizer.getNumberOfBands();
+ assertTrue(msg + ": not enough bands", numBands >= MIN_NUMBER_OF_BANDS);
+
+ short[] levelRange = mEqualizer.getBandLevelRange();
+ assertTrue(msg + ": min level too high", levelRange[0] <= MIN_BAND_LEVEL);
+ assertTrue(msg + ": max level too low", levelRange[1] >= MAX_BAND_LEVEL);
+
+ mEqualizer.setBandLevel((short)0, levelRange[1]);
+ short level = mEqualizer.getBandLevel((short)0);
+ // 10% margin on actual level compared to requested level
+ assertTrue(msg + ": setBandLevel failed",
+ ((float)level > (float)levelRange[1] * 0.9f) &&
+ ((float)level < (float)levelRange[1] * 1.1f));
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } finally {
+ releaseEqualizer();
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.1: test band frequency
+ @LargeTest
+ public void test1_1BandFrequency() throws Exception {
+ boolean result = false;
+ String msg = "test1_1BandFrequency()";
+ getEqualizer(0);
+ try {
+ short band = mEqualizer.getBand(TEST_FREQUENCY_MILLIHERTZ);
+ assertTrue(msg + ": getBand failed", band >= 0);
+ int[] freqRange = mEqualizer.getBandFreqRange(band);
+ assertTrue(msg + ": getBandFreqRange failed",
+ (freqRange[0] <= TEST_FREQUENCY_MILLIHERTZ) &&
+ (freqRange[1] >= TEST_FREQUENCY_MILLIHERTZ));
+ int freq = mEqualizer.getCenterFreq(band);
+ assertTrue(msg + ": getCenterFreq failed",
+ (freqRange[0] <= freq) && (freqRange[1] >= freq));
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } finally {
+ releaseEqualizer();
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.2: test presets
+ @LargeTest
+ public void test1_2Presets() throws Exception {
+ boolean result = false;
+ String msg = "test1_2Presets()";
+ getEqualizer(0);
+ try {
+ short numPresets = mEqualizer.getNumberOfPresets();
+ assertTrue(msg + ": getNumberOfPresets failed", numPresets >= MIN_NUMBER_OF_PRESETS);
+ mEqualizer.usePreset((short)(numPresets - 1));
+ short preset = mEqualizer.getCurrentPreset();
+ assertEquals(msg + ": usePreset failed", preset, (short)(numPresets - 1));
+ String name = mEqualizer.getPresetName(preset);
+ assertNotNull(msg + ": getPresetName failed", name);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } finally {
+ releaseEqualizer();
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.3: test properties
+ @LargeTest
+ public void test1_3Properties() throws Exception {
+ boolean result = false;
+ String msg = "test1_3Properties()";
+ getEqualizer(0);
+ try {
+ Equalizer.Settings settings = mEqualizer.getProperties();
+ String str = settings.toString();
+ settings = new Equalizer.Settings(str);
+ mEqualizer.setProperties(settings);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } finally {
+ releaseEqualizer();
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // 2 - Effect action
+ //----------------------------------
+
+ //Test case 2.0: test that the equalizer actually alters the sound
+ @LargeTest
+ public void test2_0SoundModification() throws Exception {
+ boolean result = false;
+ String msg = "test2_0SoundModification()";
+ EnergyProbe probe = null;
+ AudioEffect vc = null;
+ MediaPlayer mp = null;
+ AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+ int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+ am.setStreamVolume(AudioManager.STREAM_MUSIC,
+ am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
+ 0);
+ try {
+ probe = new EnergyProbe(0);
+ // creating a volume controller on output mix ensures that ro.audio.silent mutes
+ // audio after the effects and not before
+ vc = new AudioEffect(
+ AudioEffect.EFFECT_TYPE_NULL,
+ UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"),
+ 0,
+ 0);
+ vc.setEnabled(true);
+
+ mp = new MediaPlayer();
+ mp.setDataSource(MediaNames.SINE_200_1000);
+ mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ getEqualizer(mp.getAudioSessionId());
+ mp.prepare();
+ mp.start();
+ Thread.sleep(500);
+ // measure reference energy around 1kHz
+ int refEnergy = probe.capture(1000);
+ short band = mEqualizer.getBand(1000000);
+ short[] levelRange = mEqualizer.getBandLevelRange();
+ mEqualizer.setBandLevel(band, levelRange[0]);
+ mEqualizer.setEnabled(true);
+ Thread.sleep(500);
+ // measure energy around 1kHz with band level at min
+ int energy = probe.capture(1000);
+ assertTrue(msg + ": equalizer has no effect at 1kHz", energy < refEnergy/4);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } catch (InterruptedException e) {
+ loge(msg, "sleep() interrupted");
+ }
+ finally {
+ releaseEqualizer();
+ if (mp != null) {
+ mp.release();
+ }
+ if (vc != null) {
+ vc.release();
+ }
+ if (probe != null) {
+ probe.release();
+ }
+ am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // private methods
+ //----------------------------------
+
+ private class EnergyProbe {
+ Visualizer mVisualizer = null;
+ private byte[] mFft = new byte[1024];
+
+ public EnergyProbe(int session) {
+ mVisualizer = new Visualizer(session);
+ mVisualizer.setCaptureSize(1024);
+ }
+
+ public int capture(int freq) throws InterruptedException {
+ int energy = 0;
+ int count = 0;
+ if (mVisualizer != null) {
+ mVisualizer.setEnabled(true);
+ for (int i = 0; i < 10; i++) {
+ if (mVisualizer.getFft(mFft) == Visualizer.SUCCESS) {
+ // TODO: check speex FFT as it seems to return only the number of points
+ // correspondong to valid part of the spectrum (< Fs).
+ // e.g., if the number of points is 1024, it covers the frequency range
+ // 0 to 22050 instead of 0 to 44100 as expected from an FFT.
+ int bin = freq / (22050 / 1024);
+ int tmp = 0;
+ for (int j = bin-2; j < bin+3; j++) {
+ tmp += (int)mFft[j] * (int)mFft[j];
+ }
+ energy += tmp/5;
+ count++;
+ }
+ Thread.sleep(50);
+ }
+ mVisualizer.setEnabled(false);
+ }
+ if (count == 0) {
+ return 0;
+ }
+ return energy/count;
+ }
+
+ public void release() {
+ if (mVisualizer != null) {
+ mVisualizer.release();
+ mVisualizer = null;
+ }
+ }
+ }
+
+ private void getEqualizer(int session) {
+ if (mEqualizer == null || session != mSession) {
+ if (session != mSession && mEqualizer != null) {
+ mEqualizer.release();
+ mEqualizer = null;
+ }
+ try {
+ mEqualizer = new Equalizer(0, session);
+ mSession = session;
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "getEqualizer() Equalizer not found exception: "+e);
+ } catch (UnsupportedOperationException e) {
+ Log.e(TAG, "getEqualizer() Effect library not loaded exception: "+e);
+ }
+ }
+ assertNotNull("could not create mEqualizer", mEqualizer);
+ }
+
+ private void releaseEqualizer() {
+ if (mEqualizer != null) {
+ mEqualizer.release();
+ mEqualizer = null;
+ }
+ }
+
+}
+
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java
new file mode 100644
index 000000000000..6b8ae442a1dc
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.functional;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.media.AudioEffect;
+import android.media.AudioManager;
+import android.media.Virtualizer;
+import android.media.Visualizer;
+import android.media.MediaPlayer;
+
+import android.os.Looper;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.util.UUID;
+
+/**
+ * Junit / Instrumentation test case for the media AudioTrack api
+
+ */
+public class MediaVirtualizerTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+ private String TAG = "MediaVirtualizerTest";
+ private final static int MIN_ENERGY_RATIO_2 = 4;
+ private final static short TEST_STRENGTH = 500;
+
+ private Virtualizer mVirtualizer = null;
+ private int mSession = -1;
+
+ public MediaVirtualizerTest() {
+ super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ releaseVirtualizer();
+ }
+
+ private static void assumeTrue(String message, boolean cond) {
+ assertTrue("(assume)"+message, cond);
+ }
+
+ private void log(String testName, String message) {
+ Log.v(TAG, "["+testName+"] "+message);
+ }
+
+ private void loge(String testName, String message) {
+ Log.e(TAG, "["+testName+"] "+message);
+ }
+
+ //-----------------------------------------------------------------
+ // VIRTUALIZER TESTS:
+ //----------------------------------
+
+
+ //-----------------------------------------------------------------
+ // 0 - constructor
+ //----------------------------------
+
+ //Test case 0.0: test constructor and release
+ @LargeTest
+ public void test0_0ConstructorAndRelease() throws Exception {
+ boolean result = false;
+ String msg = "test1_0ConstructorAndRelease()";
+ Virtualizer virtualizer = null;
+ try {
+ virtualizer = new Virtualizer(0, 0);
+ assertNotNull(msg + ": could not create Virtualizer", virtualizer);
+ try {
+ assertTrue(msg +": invalid effect ID", (virtualizer.getId() != 0));
+ } catch (IllegalStateException e) {
+ msg = msg.concat(": Virtualizer not initialized");
+ }
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Virtualizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ } finally {
+ if (virtualizer != null) {
+ virtualizer.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+
+ //-----------------------------------------------------------------
+ // 1 - get/set parameters
+ //----------------------------------
+
+ //Test case 1.0: test strength
+ @LargeTest
+ public void test1_0Strength() throws Exception {
+ boolean result = false;
+ String msg = "test1_0Strength()";
+ getVirtualizer(0);
+ try {
+ if (mVirtualizer.getStrengthSupported()) {
+ mVirtualizer.setStrength((short)TEST_STRENGTH);
+ short strength = mVirtualizer.getRoundedStrength();
+ // allow 10% difference between set strength and rounded strength
+ assertTrue(msg +": got incorrect strength",
+ ((float)strength > (float)TEST_STRENGTH * 0.9f) &&
+ ((float)strength < (float)TEST_STRENGTH * 1.1f));
+ } else {
+ short strength = mVirtualizer.getRoundedStrength();
+ assertTrue(msg +": got incorrect strength", strength >= 0 && strength <= 1000);
+ }
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } finally {
+ releaseVirtualizer();
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.1: test properties
+ @LargeTest
+ public void test1_1Properties() throws Exception {
+ boolean result = false;
+ String msg = "test1_1Properties()";
+ getVirtualizer(0);
+ try {
+ Virtualizer.Settings settings = mVirtualizer.getProperties();
+ String str = settings.toString();
+ settings = new Virtualizer.Settings(str);
+ mVirtualizer.setProperties(settings);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } finally {
+ releaseVirtualizer();
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // 2 - Effect action
+ //----------------------------------
+
+ //Test case 2.0: test actual virtualizer influence on sound
+ @LargeTest
+ public void test2_0SoundModification() throws Exception {
+ boolean result = false;
+ String msg = "test2_0SoundModification()";
+ EnergyProbe probe = null;
+ AudioEffect vc = null;
+ MediaPlayer mp = null;
+ AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+ int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+ am.setStreamVolume(AudioManager.STREAM_MUSIC,
+ am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
+ 0);
+
+ try {
+ probe = new EnergyProbe(0);
+ // creating a volume controller on output mix ensures that ro.audio.silent mutes
+ // audio after the effects and not before
+ vc = new AudioEffect(
+ AudioEffect.EFFECT_TYPE_NULL,
+ UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"),
+ 0,
+ 0);
+ vc.setEnabled(true);
+
+ mp = new MediaPlayer();
+ mp.setDataSource(MediaNames.SINE_200_1000);
+ mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ getVirtualizer(mp.getAudioSessionId());
+ mp.prepare();
+ mp.start();
+ Thread.sleep(200);
+ // measure reference energy around 1kHz
+ int refEnergy200 = probe.capture(200);
+ int refEnergy1000 = probe.capture(1000);
+ mVirtualizer.setStrength((short)1000);
+ mVirtualizer.setEnabled(true);
+ Thread.sleep(500);
+ // measure energy around 1kHz with band level at min
+ int energy200 = probe.capture(200);
+ int energy1000 = probe.capture(1000);
+ // verify that the energy ration between low and high frequencies is at least
+ // four times higher with virtualizer on.
+ // NOTE: this is what is observed with current virtualizer implementation and the test
+ // audio file but is not the primary effect of the virtualizer. A better way would
+ // be to have a stereo PCM capture and check that a strongly paned input is centered
+ // when output. However, we cannot capture stereo with the visualizer.
+ assertTrue(msg + ": virtiualizer has no effect",
+ ((float)energy200/(float)energy1000) >
+ (MIN_ENERGY_RATIO_2 * ((float)refEnergy200/(float)refEnergy1000)));
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } catch (InterruptedException e) {
+ loge(msg, "sleep() interrupted");
+ }
+ finally {
+ releaseVirtualizer();
+ if (mp != null) {
+ mp.release();
+ }
+ if (vc != null) {
+ vc.release();
+ }
+ if (probe != null) {
+ probe.release();
+ }
+ am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+ }
+ assertTrue(msg, result);
+ }
+ //-----------------------------------------------------------------
+ // private methods
+ //----------------------------------
+
+ private class EnergyProbe {
+ Visualizer mVisualizer = null;
+ private byte[] mFft = new byte[1024];
+
+ public EnergyProbe(int session) {
+ mVisualizer = new Visualizer(session);
+ mVisualizer.setCaptureSize(1024);
+ }
+
+ public int capture(int freq) throws InterruptedException {
+ int energy = 0;
+ int count = 0;
+ if (mVisualizer != null) {
+ mVisualizer.setEnabled(true);
+ for (int i = 0; i < 10; i++) {
+ if (mVisualizer.getFft(mFft) == Visualizer.SUCCESS) {
+ // TODO: check speex FFT as it seems to return only the number of points
+ // correspondong to valid part of the spectrum (< Fs).
+ // e.g., if the number of points is 1024, it covers the frequency range
+ // 0 to 22050 instead of 0 to 44100 as expected from an FFT.
+ int bin = freq / (22050 / 1024);
+ int tmp = 0;
+ for (int j = bin-2; j < bin+3; j++) {
+ tmp += (int)mFft[j] * (int)mFft[j];
+ }
+ energy += tmp/5;
+ count++;
+ }
+ Thread.sleep(50);
+ }
+ mVisualizer.setEnabled(false);
+ }
+ if (count == 0) {
+ return 0;
+ }
+ return energy/count;
+ }
+
+ public void release() {
+ if (mVisualizer != null) {
+ mVisualizer.release();
+ mVisualizer = null;
+ }
+ }
+ }
+
+ private void getVirtualizer(int session) {
+ if (mVirtualizer == null || session != mSession) {
+ if (session != mSession && mVirtualizer != null) {
+ mVirtualizer.release();
+ mVirtualizer = null;
+ }
+ try {
+ mVirtualizer = new Virtualizer(0, session);
+ mSession = session;
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "getVirtualizer() Virtualizer not found exception: "+e);
+ } catch (UnsupportedOperationException e) {
+ Log.e(TAG, "getVirtualizer() Effect library not loaded exception: "+e);
+ }
+ }
+ assertNotNull("could not create mVirtualizer", mVirtualizer);
+ }
+
+ private void releaseVirtualizer() {
+ if (mVirtualizer != null) {
+ mVirtualizer.release();
+ mVirtualizer = null;
+ }
+ }
+
+}
+
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVisualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVisualizerTest.java
new file mode 100644
index 000000000000..26fdbfe4db95
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVisualizerTest.java
@@ -0,0 +1,505 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.functional;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.media.AudioEffect;
+import android.media.AudioManager;
+import android.media.Visualizer;
+import android.media.MediaPlayer;
+
+import android.os.Looper;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.util.UUID;
+
+/**
+ * Junit / Instrumentation test case for the media AudioTrack api
+
+ */
+public class MediaVisualizerTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+ private String TAG = "MediaVisualizerTest";
+ private final static int MIN_CAPTURE_RATE_MAX = 20000;
+ private final static int MIN_SAMPLING_RATE = 8000000;
+ private final static int MAX_SAMPLING_RATE = 48000000;
+ private final static int MIN_CAPTURE_SIZE_MAX = 1024;
+ private final static int MAX_CAPTURE_SIZE_MIN = 128;
+
+ private Visualizer mVisualizer = null;
+ private int mSession = -1;
+ private boolean mInitialized = false;
+ private Looper mLooper = null;
+ private final Object lock = new Object();
+ private byte[] mWaveform = null;
+ private byte[] mFft = null;
+ private boolean mCaptureWaveform = false;
+ private boolean mCaptureFft = false;
+
+ public MediaVisualizerTest() {
+ super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ releaseVisualizer();
+ }
+
+ private static void assumeTrue(String message, boolean cond) {
+ assertTrue("(assume)"+message, cond);
+ }
+
+ private void log(String testName, String message) {
+ Log.v(TAG, "["+testName+"] "+message);
+ }
+
+ private void loge(String testName, String message) {
+ Log.e(TAG, "["+testName+"] "+message);
+ }
+
+ //-----------------------------------------------------------------
+ // VISUALIZER TESTS:
+ //----------------------------------
+
+
+ //-----------------------------------------------------------------
+ // 0 - constructor
+ //----------------------------------
+
+ //Test case 0.0: test constructor and release
+ @LargeTest
+ public void test0_0ConstructorAndRelease() throws Exception {
+ boolean result = false;
+ String msg = "test1_0ConstructorAndRelease()";
+ Visualizer visualizer = null;
+ try {
+ visualizer = new Visualizer(0);
+ assertNotNull(msg + ": could not create Visualizer", visualizer);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Visualizer not found");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": Effect library not loaded");
+ } finally {
+ if (visualizer != null) {
+ visualizer.release();
+ }
+ }
+ assertTrue(msg, result);
+ }
+
+
+ //-----------------------------------------------------------------
+ // 1 - get/set parameters
+ //----------------------------------
+
+ //Test case 1.0: check capture rate and sampling rate
+ @LargeTest
+ public void test1_0CaptureRates() throws Exception {
+ boolean result = false;
+ String msg = "test1_0CaptureRates()";
+ getVisualizer(0);
+ try {
+ int captureRate = mVisualizer.getMaxCaptureRate();
+ assertTrue(msg +": insufficient max capture rate",
+ captureRate >= MIN_CAPTURE_RATE_MAX);
+ int samplingRate = mVisualizer.getSamplingRate();
+ assertTrue(msg +": invalid sampling rate",
+ samplingRate >= MIN_SAMPLING_RATE && samplingRate <= MAX_SAMPLING_RATE);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } finally {
+ releaseVisualizer();
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 1.1: check capture size
+ @LargeTest
+ public void test1_1CaptureSize() throws Exception {
+ boolean result = false;
+ String msg = "test1_1CaptureSize()";
+ getVisualizer(0);
+ try {
+ int[] range = mVisualizer.getCaptureSizeRange();
+ assertTrue(msg +": insufficient min capture size",
+ range[0] <= MAX_CAPTURE_SIZE_MIN);
+ assertTrue(msg +": insufficient min capture size",
+ range[1] >= MIN_CAPTURE_SIZE_MAX);
+ mVisualizer.setCaptureSize(range[0]);
+ assertEquals(msg +": insufficient min capture size",
+ range[0], mVisualizer.getCaptureSize());
+ mVisualizer.setCaptureSize(range[1]);
+ assertEquals(msg +": insufficient min capture size",
+ range[1], mVisualizer.getCaptureSize());
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } finally {
+ releaseVisualizer();
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // 2 - check capture
+ //----------------------------------
+
+ //Test case 2.0: test capture in polling mode
+ @LargeTest
+ public void test2_0PollingCapture() throws Exception {
+ boolean result = false;
+ String msg = "test2_0PollingCapture()";
+ AudioEffect vc = null;
+ MediaPlayer mp = null;
+ AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+ int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+ am.setStreamVolume(AudioManager.STREAM_MUSIC,
+ am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
+ 0);
+
+ try {
+ // creating a volume controller on output mix ensures that ro.audio.silent mutes
+ // audio after the effects and not before
+ vc = new AudioEffect(
+ AudioEffect.EFFECT_TYPE_NULL,
+ UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"),
+ 0,
+ 0);
+ vc.setEnabled(true);
+
+ mp = new MediaPlayer();
+ mp.setDataSource(MediaNames.SINE_200_1000);
+ mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ getVisualizer(mp.getAudioSessionId());
+ mVisualizer.setEnabled(true);
+ // check capture on silence
+ byte[] data = new byte[mVisualizer.getCaptureSize()];
+ mVisualizer.getWaveForm(data);
+ int energy = computeEnergy(data, true);
+ assertEquals(msg +": getWaveForm reports energy for silence",
+ 0, energy);
+ mVisualizer.getFft(data);
+ energy = computeEnergy(data, false);
+ assertEquals(msg +": getFft reports energy for silence",
+ 0, energy);
+ mp.prepare();
+ mp.start();
+ Thread.sleep(500);
+ // check capture on sound
+ mVisualizer.getWaveForm(data);
+ energy = computeEnergy(data, true);
+ assertTrue(msg +": getWaveForm reads insufficient level",
+ energy > 0);
+ mVisualizer.getFft(data);
+ energy = computeEnergy(data, false);
+ assertTrue(msg +": getFft reads insufficient level",
+ energy > 0);
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } catch (InterruptedException e) {
+ loge(msg, "sleep() interrupted");
+ }
+ finally {
+ releaseVisualizer();
+ if (mp != null) {
+ mp.release();
+ }
+ if (vc != null) {
+ vc.release();
+ }
+ am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+ }
+ assertTrue(msg, result);
+ }
+
+ //Test case 2.1: test capture with listener
+ @LargeTest
+ public void test2_1ListenerCapture() throws Exception {
+ boolean result = false;
+ String msg = "test2_1ListenerCapture()";
+ AudioEffect vc = null;
+ MediaPlayer mp = null;
+ AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+ int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+ am.setStreamVolume(AudioManager.STREAM_MUSIC,
+ am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
+ 0);
+
+ try {
+ // creating a volume controller on output mix ensures that ro.audio.silent mutes
+ // audio after the effects and not before
+ vc = new AudioEffect(
+ AudioEffect.EFFECT_TYPE_NULL,
+ UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"),
+ 0,
+ 0);
+ vc.setEnabled(true);
+
+ mp = new MediaPlayer();
+ mp.setDataSource(MediaNames.SINE_200_1000);
+ mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
+
+ getVisualizer(mp.getAudioSessionId());
+ createListenerLooper();
+ synchronized(lock) {
+ try {
+ lock.wait(1000);
+ } catch(Exception e) {
+ Log.e(TAG, "Looper creation: wait was interrupted.");
+ }
+ }
+ assertTrue(mInitialized);
+
+ mVisualizer.setEnabled(true);
+
+ // check capture on silence
+ synchronized(lock) {
+ try {
+ mCaptureWaveform = true;
+ lock.wait(1000);
+ mCaptureWaveform = false;
+ } catch(Exception e) {
+ Log.e(TAG, "Capture waveform: wait was interrupted.");
+ }
+ }
+ assertNotNull(msg +": waveform capture failed", mWaveform);
+ int energy = computeEnergy(mWaveform, true);
+ assertEquals(msg +": getWaveForm reports energy for silence",
+ 0, energy);
+
+ synchronized(lock) {
+ try {
+ mCaptureFft = true;
+ lock.wait(1000);
+ mCaptureFft = false;
+ } catch(Exception e) {
+ Log.e(TAG, "Capture FFT: wait was interrupted.");
+ }
+ }
+ assertNotNull(msg +": FFT capture failed", mFft);
+ energy = computeEnergy(mFft, false);
+ assertEquals(msg +": getFft reports energy for silence",
+ 0, energy);
+
+ mp.prepare();
+ mp.start();
+ Thread.sleep(500);
+
+ // check capture on sound
+ synchronized(lock) {
+ try {
+ mCaptureWaveform = true;
+ lock.wait(1000);
+ mCaptureWaveform = false;
+ } catch(Exception e) {
+ Log.e(TAG, "Capture waveform: wait was interrupted.");
+ }
+ }
+ assertNotNull(msg +": waveform capture failed", mWaveform);
+ energy = computeEnergy(mWaveform, true);
+ assertTrue(msg +": getWaveForm reads insufficient level",
+ energy > 0);
+
+ synchronized(lock) {
+ try {
+ mCaptureFft = true;
+ lock.wait(1000);
+ mCaptureFft = false;
+ } catch(Exception e) {
+ Log.e(TAG, "Capture FFT: wait was interrupted.");
+ }
+ }
+ assertNotNull(msg +": FFT capture failed", mFft);
+ energy = computeEnergy(mFft, false);
+ assertTrue(msg +": getFft reads insufficient level",
+ energy > 0);
+
+ result = true;
+ } catch (IllegalArgumentException e) {
+ msg = msg.concat(": Bad parameter value");
+ loge(msg, "Bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ msg = msg.concat(": get parameter() rejected");
+ loge(msg, "get parameter() rejected");
+ } catch (IllegalStateException e) {
+ msg = msg.concat("get parameter() called in wrong state");
+ loge(msg, "get parameter() called in wrong state");
+ } catch (InterruptedException e) {
+ loge(msg, "sleep() interrupted");
+ }
+ finally {
+ terminateListenerLooper();
+ releaseVisualizer();
+ if (mp != null) {
+ mp.release();
+ }
+ if (vc != null) {
+ vc.release();
+ }
+ am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+ }
+ assertTrue(msg, result);
+ }
+
+ //-----------------------------------------------------------------
+ // private methods
+ //----------------------------------
+
+ private int computeEnergy(byte[] data, boolean unsigned) {
+ int energy = 0;
+ if (data.length != 0) {
+ for (int i = 0; i < data.length; i++) {
+ int tmp;
+ // convert from unsigned 8 bit to signed 16 bit
+ if (unsigned) {
+ tmp = ((int)data[i] & 0xFF) - 128;
+ } else {
+ tmp = (int)data[i];
+ }
+ energy += tmp*tmp;
+ }
+ energy /= data.length;
+ }
+ return energy;
+ }
+
+ private void getVisualizer(int session) {
+ if (mVisualizer == null || session != mSession) {
+ if (session != mSession && mVisualizer != null) {
+ mVisualizer.release();
+ mVisualizer = null;
+ }
+ try {
+ mVisualizer = new Visualizer(session);
+ mSession = session;
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "getVisualizer() Visualizer not found exception: "+e);
+ } catch (UnsupportedOperationException e) {
+ Log.e(TAG, "getVisualizer() Effect library not loaded exception: "+e);
+ }
+ }
+ assertNotNull("could not create mVisualizer", mVisualizer);
+ }
+
+ private void releaseVisualizer() {
+ if (mVisualizer != null) {
+ mVisualizer.release();
+ mVisualizer = null;
+ }
+ }
+
+ private void createListenerLooper() {
+
+ new Thread() {
+ @Override
+ public void run() {
+ // Set up a looper to be used by mEffect.
+ Looper.prepare();
+
+ // Save the looper so that we can terminate this thread
+ // after we are done with it.
+ mLooper = Looper.myLooper();
+
+ if (mVisualizer != null) {
+ mVisualizer.setDataCaptureListener(new Visualizer.OnDataCaptureListener() {
+ public void onWaveFormDataCapture(
+ Visualizer visualizer, byte[] waveform, int samplingRate) {
+ synchronized(lock) {
+ if (visualizer == mVisualizer) {
+ if (mCaptureWaveform) {
+ mWaveform = waveform;
+ lock.notify();
+ }
+ }
+ }
+ }
+
+ public void onFftDataCapture(
+ Visualizer visualizer, byte[] fft, int samplingRate) {
+ synchronized(lock) {
+ if (visualizer == mVisualizer) {
+ if (mCaptureFft) {
+ mFft = fft;
+ lock.notify();
+ }
+ }
+ }
+ }
+ },
+ 10000,
+ true,
+ true);
+ }
+
+ synchronized(lock) {
+ mInitialized = true;
+ lock.notify();
+ }
+ Looper.loop(); // Blocks forever until Looper.quit() is called.
+ }
+ }.start();
+ }
+ /*
+ * Terminates the listener looper thread.
+ */
+ private void terminateListenerLooper() {
+ if (mLooper != null) {
+ mLooper.quit();
+ mLooper = null;
+ }
+ }
+
+}
+
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/power/MediaPlayerPowerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/power/MediaPlayerPowerTest.java
new file mode 100644
index 000000000000..9e91740ca5c2
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/power/MediaPlayerPowerTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.mediaframeworktest.power;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+import android.media.MediaPlayer;
+import android.os.Environment;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.io.File;
+
+/**
+ * Junit / Instrumentation test case for the power measurment the media player
+ */
+public class MediaPlayerPowerTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+ private String TAG = "MediaPlayerPowerTest";
+ private String MP3_POWERTEST =
+ Environment.getExternalStorageDirectory().toString() + "/power_sample_mp3.mp3";
+ private String MP3_STREAM = "http://75.17.48.204:10088/power_media/power_sample_mp3.mp3";
+ private String OGG_STREAM = "http://75.17.48.204:10088/power_media/power_sample_ogg.mp3";
+ private String AAC_STREAM = "http://75.17.48.204:10088/power_media/power_sample_aac.mp3";
+
+ public MediaPlayerPowerTest() {
+ super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+ }
+
+ protected void setUp() throws Exception {
+ getActivity();
+ super.setUp();
+
+ }
+
+ public void audioPlayback(String filePath) {
+ try {
+ MediaPlayer mp = new MediaPlayer();
+ mp.setDataSource(filePath);
+ mp.prepare();
+ mp.start();
+ Thread.sleep(200000);
+ mp.stop();
+ mp.release();
+ } catch (Exception e) {
+ Log.v(TAG, e.toString());
+ assertTrue("MP3 Playback", false);
+ }
+ }
+
+ // A very simple test case which start the audio player.
+ // Power measurment will be done in other application.
+ public void testPowerLocalMP3Playback() throws Exception {
+ audioPlayback(MP3_POWERTEST);
+ }
+
+ public void testPowerStreamMP3Playback() throws Exception {
+ audioPlayback(MP3_STREAM);
+ }
+
+ public void testPowerStreamOGGPlayback() throws Exception {
+ audioPlayback(OGG_STREAM);
+ }
+
+ public void testPowerStreamAACPlayback() throws Exception {
+ audioPlayback(AAC_STREAM);
+ }
+}
diff --git a/native/android/Android.mk b/native/android/Android.mk
index 950a1e9a7ad1..bd2b27af7f18 100644
--- a/native/android/Android.mk
+++ b/native/android/Android.mk
@@ -7,6 +7,7 @@ include $(CLEAR_VARS)
#
LOCAL_SRC_FILES:= \
asset_manager.cpp \
+ configuration.cpp \
input.cpp \
looper.cpp \
native_activity.cpp \
diff --git a/native/android/asset_manager.cpp b/native/android/asset_manager.cpp
index 36c381ef1b84..3f7c1b69af17 100644
--- a/native/android/asset_manager.cpp
+++ b/native/android/asset_manager.cpp
@@ -17,7 +17,7 @@
#define LOG_TAG "NAsset"
#include <utils/Log.h>
-#include <android/asset_manager.h>
+#include <android/asset_manager_jni.h>
#include <utils/AssetManager.h>
#include <utils/AssetDir.h>
#include <utils/Asset.h>
diff --git a/native/android/configuration.cpp b/native/android/configuration.cpp
new file mode 100644
index 000000000000..d76164ff418e
--- /dev/null
+++ b/native/android/configuration.cpp
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Configuration"
+#include <utils/Log.h>
+
+#include <utils/AssetManager.h>
+
+#include <android_runtime/android_content_res_Configuration.h>
+
+using namespace android;
+
+AConfiguration* AConfiguration_new() {
+ AConfiguration* config = new AConfiguration;
+ memset(config, 0, sizeof(AConfiguration));
+ return config;
+}
+
+void AConfiguration_delete(AConfiguration* config) {
+ delete config;
+}
+
+void AConfiguration_fromAssetManager(AConfiguration* out, AAssetManager* am) {
+ ((AssetManager*)am)->getConfiguration(out);
+}
+
+void AConfiguration_copy(AConfiguration* dest, AConfiguration* src) {
+ *dest = *src;
+}
+
+int32_t AConfiguration_getMcc(AConfiguration* config) {
+ return config->mcc;
+}
+
+int32_t AConfiguration_getMnc(AConfiguration* config) {
+ return config->mnc;
+}
+
+void AConfiguration_getLanguage(AConfiguration* config, char* outLanguage) {
+ outLanguage[0] = config->language[0];
+ outLanguage[1] = config->language[1];
+}
+
+void AConfiguration_getCountry(AConfiguration* config, char* outCountry) {
+ outCountry[0] = config->country[0];
+ outCountry[1] = config->country[1];
+}
+
+int32_t AConfiguration_getOrientation(AConfiguration* config) {
+ return config->orientation;
+}
+
+int32_t AConfiguration_getTouchscreen(AConfiguration* config) {
+ return config->touchscreen;
+}
+
+int32_t AConfiguration_getDensity(AConfiguration* config) {
+ return config->density;
+}
+
+int32_t AConfiguration_getKeyboard(AConfiguration* config) {
+ return config->keyboard;
+}
+
+int32_t AConfiguration_getNavigation(AConfiguration* config) {
+ return config->navigation;
+}
+
+int32_t AConfiguration_getKeysHidden(AConfiguration* config) {
+ return config->inputFlags&ResTable_config::MASK_KEYSHIDDEN;
+}
+
+int32_t AConfiguration_getNavHidden(AConfiguration* config) {
+ return (config->inputFlags&ResTable_config::MASK_NAVHIDDEN)
+ >> ResTable_config::SHIFT_NAVHIDDEN;
+}
+
+int32_t AConfiguration_getSdkVersion(AConfiguration* config) {
+ return config->sdkVersion;
+}
+
+int32_t AConfiguration_getScreenSize(AConfiguration* config) {
+ return config->screenLayout&ResTable_config::MASK_SCREENSIZE;
+}
+
+int32_t AConfiguration_getScreenLong(AConfiguration* config) {
+ return (config->screenLayout&ResTable_config::MASK_SCREENLONG)
+ >> ResTable_config::SHIFT_SCREENLONG;
+}
+
+int32_t AConfiguration_getUiModeType(AConfiguration* config) {
+ return config->uiMode&ResTable_config::MASK_UI_MODE_TYPE;
+}
+
+int32_t AConfiguration_getUiModeNight(AConfiguration* config) {
+ return (config->uiMode&ResTable_config::MASK_UI_MODE_NIGHT)
+ >> ResTable_config::SHIFT_UI_MODE_NIGHT;
+
+}
+
+// ----------------------------------------------------------------------
+
+void AConfiguration_setMcc(AConfiguration* config, int32_t mcc) {
+ config->mcc = mcc;
+}
+
+void AConfiguration_setMnc(AConfiguration* config, int32_t mnc) {
+ config->mnc = mnc;
+}
+
+void AConfiguration_setLanguage(AConfiguration* config, const char* language) {
+ config->language[0] = language[0];
+ config->language[1] = language[1];
+}
+
+void AConfiguration_setCountry(AConfiguration* config, const char* country) {
+ config->country[0] = country[0];
+ config->country[1] = country[1];
+}
+
+void AConfiguration_setOrientation(AConfiguration* config, int32_t orientation) {
+ config->orientation = orientation;
+}
+
+void AConfiguration_setTouchscreen(AConfiguration* config, int32_t touchscreen) {
+ config->touchscreen = touchscreen;
+}
+
+void AConfiguration_setDensity(AConfiguration* config, int32_t density) {
+ config->density = density;
+}
+
+void AConfiguration_setKeyboard(AConfiguration* config, int32_t keyboard) {
+ config->keyboard = keyboard;
+}
+
+void AConfiguration_setNavigation(AConfiguration* config, int32_t navigation) {
+ config->navigation = navigation;
+}
+
+void AConfiguration_setKeysHidden(AConfiguration* config, int32_t keysHidden) {
+ config->inputFlags = (config->inputFlags&~ResTable_config::MASK_KEYSHIDDEN)
+ | (keysHidden&ResTable_config::MASK_KEYSHIDDEN);
+}
+
+void AConfiguration_setNavHidden(AConfiguration* config, int32_t navHidden) {
+ config->inputFlags = (config->inputFlags&~ResTable_config::MASK_NAVHIDDEN)
+ | ((navHidden<<ResTable_config::SHIFT_NAVHIDDEN)&ResTable_config::MASK_NAVHIDDEN);
+}
+
+void AConfiguration_setSdkVersion(AConfiguration* config, int32_t sdkVersion) {
+ config->sdkVersion = sdkVersion;
+}
+
+void AConfiguration_setScreenSize(AConfiguration* config, int32_t screenSize) {
+ config->screenLayout = (config->screenLayout&~ResTable_config::MASK_SCREENSIZE)
+ | (screenSize&ResTable_config::MASK_SCREENSIZE);
+}
+
+void AConfiguration_setScreenLong(AConfiguration* config, int32_t screenLong) {
+ config->screenLayout = (config->screenLayout&~ResTable_config::MASK_SCREENLONG)
+ | ((screenLong<<ResTable_config::SHIFT_SCREENLONG)&ResTable_config::MASK_SCREENLONG);
+}
+
+void AConfiguration_setUiModeType(AConfiguration* config, int32_t uiModeType) {
+ config->uiMode = (config->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+ | (uiModeType&ResTable_config::MASK_UI_MODE_TYPE);
+}
+
+void AConfiguration_setUiModeNight(AConfiguration* config, int32_t uiModeNight) {
+ config->uiMode = (config->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
+ | ((uiModeNight<<ResTable_config::SHIFT_UI_MODE_NIGHT)&ResTable_config::MASK_UI_MODE_NIGHT);
+
+}
+
+// ----------------------------------------------------------------------
+
+int32_t AConfiguration_diff(AConfiguration* config1, AConfiguration* config2) {
+ return (config1->diff(*config2));
+}
+
+int32_t AConfiguration_match(AConfiguration* base, AConfiguration* requested) {
+ return base->match(*requested);
+}
+
+int32_t AConfiguration_isBetterThan(AConfiguration* base, AConfiguration* test,
+ AConfiguration* requested) {
+ return base->isBetterThan(*test, requested);
+}
diff --git a/native/copy-to-ndk.sh b/native/copy-to-ndk.sh
new file mode 100644
index 000000000000..4f5a16a4b595
--- /dev/null
+++ b/native/copy-to-ndk.sh
@@ -0,0 +1,55 @@
+# Take care of copying current header files over to the correct
+# location in the NDK.
+
+copyndkheaders() {
+ local CURR_PLATFORM=android-9
+ local ALL_PLATFORMS="$CURR_PLATFORM android-8 android-5 android-4 android-3"
+
+ local SRC_HEADERS=$ANDROID_BUILD_TOP/frameworks/base/native/include/android
+ local NDK_PLATFORMS=$ANDROID_BUILD_TOP/development/ndk/platforms
+ local DST_HEADERS=$NDK_PLATFORMS/$CURR_PLATFORM
+
+ local SRC_LIB_ANDROID=$ANDROID_PRODUCT_OUT/system/lib/libandroid.so
+ local DST_LIB_ANDROID=$NDK_PLATFORMS/$CURR_PLATFORM/arch-arm/usr/lib/libandroid.so
+
+ local didsomething=""
+
+ #echo "SRC_HEADERS: $SRC_HEADERS"
+
+ for i in $(cd $SRC_HEADERS; ls *.h); do
+ local src=$SRC_HEADERS/$i
+ local changed=""
+ for j in $ALL_PLATFORMS; do
+ local dst=$NDK_PLATFORMS/$j/arch-arm/usr/include/android/$i
+ if [ "$changed" == "" -a -e $dst ]; then
+ #echo "Exists: $dst"
+ if diff $src $dst >/dev/null; then
+ echo "$i: has not changed from $j" >/dev/null
+ changed="false"
+ else
+ changed="true"
+ echo "$i: has changed from $j" >/dev/null
+ fi
+ fi
+ done
+ if [ "$changed" == "true" -o "$changed" == "" ]; then
+ echo "Updating: $i"
+ cp $src $NDK_PLATFORMS/$CURR_PLATFORM/arch-arm/usr/include/android/$i
+ didsomething="true"
+ fi
+ done
+
+ if diff $SRC_LIB_ANDROID $DST_LIB_ANDROID >/dev/null; then
+ echo "libandroid.so: has not changed" >/dev/null
+ else
+ echo "Updating: $DST_LIB_ANDROID"
+ cp $SRC_LIB_ANDROID $DST_LIB_ANDROID
+ didsomething="true"
+ fi
+ if [ "$didsomething" != "" ]; then
+ echo "Headers changed... rebuilding platforms."
+ sh $ANDROID_BUILD_TOP/ndk/build/tools/build-platforms.sh
+ fi
+}
+
+copyndkheaders
diff --git a/native/include/android/asset_manager.h b/native/include/android/asset_manager.h
index 89989f8cc4f5..4fa0ef3a3a12 100644
--- a/native/include/android/asset_manager.h
+++ b/native/include/android/asset_manager.h
@@ -18,8 +18,6 @@
#ifndef ANDROID_ASSET_MANAGER_H
#define ANDROID_ASSET_MANAGER_H
-#include <jni.h>
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -43,14 +41,6 @@ enum {
/**
- * Given a Dalvik AssetManager object, obtain the corresponding native AAssetManager
- * object. Note that the caller is responsible for obtaining and holding a VM reference
- * to the jobject to prevent its being garbage collected while the native object is
- * in use.
- */
-AAssetManager* AAssetManager_fromJava(JNIEnv* env, jobject assetManager);
-
-/**
* Open the named directory within the asset hierarchy. The directory can then
* be inspected with the AAssetDir functions. To open the top-level directory,
* pass in "" as the dirName.
diff --git a/native/include/android/asset_manager_jni.h b/native/include/android/asset_manager_jni.h
new file mode 100644
index 000000000000..aec2d3c97c07
--- /dev/null
+++ b/native/include/android/asset_manager_jni.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_ASSET_MANAGER_JNI_H
+#define ANDROID_ASSET_MANAGER_JNI_H
+
+#include <android/asset_manager.h>
+#include <jni.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Given a Dalvik AssetManager object, obtain the corresponding native AAssetManager
+ * object. Note that the caller is responsible for obtaining and holding a VM reference
+ * to the jobject to prevent its being garbage collected while the native object is
+ * in use.
+ */
+AAssetManager* AAssetManager_fromJava(JNIEnv* env, jobject assetManager);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // ANDROID_ASSET_MANAGER_JNI_H
diff --git a/native/include/android/configuration.h b/native/include/android/configuration.h
new file mode 100644
index 000000000000..79b9b1e302ed
--- /dev/null
+++ b/native/include/android/configuration.h
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_CONFIGURATION_H
+#define ANDROID_CONFIGURATION_H
+
+#include <android/asset_manager.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct AConfiguration;
+typedef struct AConfiguration AConfiguration;
+
+enum {
+ ACONFIGURATION_ORIENTATION_ANY = 0x0000,
+ ACONFIGURATION_ORIENTATION_PORT = 0x0001,
+ ACONFIGURATION_ORIENTATION_LAND = 0x0002,
+ ACONFIGURATION_ORIENTATION_SQUARE = 0x0003,
+
+ ACONFIGURATION_TOUCHSCREEN_ANY = 0x0000,
+ ACONFIGURATION_TOUCHSCREEN_NOTOUCH = 0x0001,
+ ACONFIGURATION_TOUCHSCREEN_STYLUS = 0x0002,
+ ACONFIGURATION_TOUCHSCREEN_FINGER = 0x0003,
+
+ ACONFIGURATION_DENSITY_DEFAULT = 0,
+ ACONFIGURATION_DENSITY_LOW = 120,
+ ACONFIGURATION_DENSITY_MEDIUM = 160,
+ ACONFIGURATION_DENSITY_HIGH = 240,
+ ACONFIGURATION_DENSITY_NONE = 0xffff,
+
+ ACONFIGURATION_KEYBOARD_ANY = 0x0000,
+ ACONFIGURATION_KEYBOARD_NOKEYS = 0x0001,
+ ACONFIGURATION_KEYBOARD_QWERTY = 0x0002,
+ ACONFIGURATION_KEYBOARD_12KEY = 0x0003,
+
+ ACONFIGURATION_NAVIGATION_ANY = 0x0000,
+ ACONFIGURATION_NAVIGATION_NONAV = 0x0001,
+ ACONFIGURATION_NAVIGATION_DPAD = 0x0002,
+ ACONFIGURATION_NAVIGATION_TRACKBALL = 0x0003,
+ ACONFIGURATION_NAVIGATION_WHEEL = 0x0004,
+
+ ACONFIGURATION_KEYSHIDDEN_ANY = 0x0000,
+ ACONFIGURATION_KEYSHIDDEN_NO = 0x0001,
+ ACONFIGURATION_KEYSHIDDEN_YES = 0x0002,
+ ACONFIGURATION_KEYSHIDDEN_SOFT = 0x0003,
+
+ ACONFIGURATION_NAVHIDDEN_ANY = 0x0000,
+ ACONFIGURATION_NAVHIDDEN_NO = 0x0001,
+ ACONFIGURATION_NAVHIDDEN_YES = 0x0002,
+
+ ACONFIGURATION_SCREENSIZE_ANY = 0x00,
+ ACONFIGURATION_SCREENSIZE_SMALL = 0x01,
+ ACONFIGURATION_SCREENSIZE_NORMAL = 0x02,
+ ACONFIGURATION_SCREENSIZE_LARGE = 0x03,
+ ACONFIGURATION_SCREENSIZE_XLARGE = 0x04,
+
+ ACONFIGURATION_SCREENLONG_ANY = 0x00,
+ ACONFIGURATION_SCREENLONG_NO = 0x1,
+ ACONFIGURATION_SCREENLONG_YES = 0x2,
+
+ ACONFIGURATION_UI_MODE_TYPE_ANY = 0x00,
+ ACONFIGURATION_UI_MODE_TYPE_NORMAL = 0x01,
+ ACONFIGURATION_UI_MODE_TYPE_DESK = 0x02,
+ ACONFIGURATION_UI_MODE_TYPE_CAR = 0x03,
+
+ ACONFIGURATION_UI_MODE_NIGHT_ANY = 0x00,
+ ACONFIGURATION_UI_MODE_NIGHT_NO = 0x10,
+ ACONFIGURATION_UI_MODE_NIGHT_YES = 0x20,
+
+ ACONFIGURATION_MCC = 0x0001,
+ ACONFIGURATION_MNC = 0x0002,
+ ACONFIGURATION_LOCALE = 0x0004,
+ ACONFIGURATION_TOUCHSCREEN = 0x0008,
+ ACONFIGURATION_KEYBOARD = 0x0010,
+ ACONFIGURATION_KEYBOARD_HIDDEN = 0x0020,
+ ACONFIGURATION_NAVIGATION = 0x0040,
+ ACONFIGURATION_ORIENTATION = 0x0080,
+ ACONFIGURATION_DENSITY = 0x0100,
+ ACONFIGURATION_SCREEN_SIZE = 0x0200,
+ ACONFIGURATION_VERSION = 0x0400,
+ ACONFIGURATION_SCREEN_LAYOUT = 0x0800,
+ ACONFIGURATION_UI_MODE = 0x1000,
+};
+
+/**
+ * Create a new AConfiguration, initialized with no values set.
+ */
+AConfiguration* AConfiguration_new();
+
+/**
+ * Free an AConfiguration that was previously created with
+ * AConfiguration_new().
+ */
+void AConfiguration_delete(AConfiguration* config);
+
+/**
+ * Create and return a new AConfiguration based on the current configuration in
+ * use in the given AssetManager.
+ */
+void AConfiguration_fromAssetManager(AConfiguration* out, AAssetManager* am);
+
+/**
+ * Copy the contents of 'src' to 'dest'.
+ */
+void AConfiguration_copy(AConfiguration* dest, AConfiguration* src);
+
+/**
+ * Return the current MCC set in the configuration. 0 if not set.
+ */
+int32_t AConfiguration_getMcc(AConfiguration* config);
+
+/**
+ * Set the current MCC in the configuration. 0 to clear.
+ */
+void AConfiguration_setMcc(AConfiguration* config, int32_t mcc);
+
+/**
+ * Return the current MNC set in the configuration. 0 if not set.
+ */
+int32_t AConfiguration_getMnc(AConfiguration* config);
+
+/**
+ * Set the current MNC in the configuration. 0 to clear.
+ */
+void AConfiguration_setMnc(AConfiguration* config, int32_t mnc);
+
+/**
+ * Return the current language code set in the configuration. The output will
+ * be filled with an array of two characters. They are not 0-terminated. If
+ * a language is not set, they will be 0.
+ */
+void AConfiguration_getLanguage(AConfiguration* config, char* outLanguage);
+
+/**
+ * Set the current language code in the configuration, from the first two
+ * characters in the string.
+ */
+void AConfiguration_setLanguage(AConfiguration* config, const char* language);
+
+/**
+ * Return the current country code set in the configuration. The output will
+ * be filled with an array of two characters. They are not 0-terminated. If
+ * a country is not set, they will be 0.
+ */
+void AConfiguration_getCountry(AConfiguration* config, char* outCountry);
+
+/**
+ * Set the current country code in the configuration, from the first two
+ * characters in the string.
+ */
+void AConfiguration_setCountry(AConfiguration* config, const char* country);
+
+/**
+ * Return the current ACONFIGURATION_ORIENTATION_* set in the configuration.
+ */
+int32_t AConfiguration_getOrientation(AConfiguration* config);
+
+/**
+ * Set the current orientation in the configuration.
+ */
+void AConfiguration_setOrientation(AConfiguration* config, int32_t orientation);
+
+/**
+ * Return the current ACONFIGURATION_TOUCHSCREEN_* set in the configuration.
+ */
+int32_t AConfiguration_getTouchscreen(AConfiguration* config);
+
+/**
+ * Set the current touchscreen in the configuration.
+ */
+void AConfiguration_setTouchscreen(AConfiguration* config, int32_t touchscreen);
+
+/**
+ * Return the current ACONFIGURATION_DENSITY_* set in the configuration.
+ */
+int32_t AConfiguration_getDensity(AConfiguration* config);
+
+/**
+ * Set the current density in the configuration.
+ */
+void AConfiguration_setDensity(AConfiguration* config, int32_t density);
+
+/**
+ * Return the current ACONFIGURATION_KEYBOARD_* set in the configuration.
+ */
+int32_t AConfiguration_getKeyboard(AConfiguration* config);
+
+/**
+ * Set the current keyboard in the configuration.
+ */
+void AConfiguration_setKeyboard(AConfiguration* config, int32_t keyboard);
+
+/**
+ * Return the current ACONFIGURATION_NAVIGATION_* set in the configuration.
+ */
+int32_t AConfiguration_getNavigation(AConfiguration* config);
+
+/**
+ * Set the current navigation in the configuration.
+ */
+void AConfiguration_setNavigation(AConfiguration* config, int32_t navigation);
+
+/**
+ * Return the current ACONFIGURATION_KEYSHIDDEN_* set in the configuration.
+ */
+int32_t AConfiguration_getKeysHidden(AConfiguration* config);
+
+/**
+ * Set the current keys hidden in the configuration.
+ */
+void AConfiguration_setKeysHidden(AConfiguration* config, int32_t keysHidden);
+
+/**
+ * Return the current ACONFIGURATION_NAVHIDDEN_* set in the configuration.
+ */
+int32_t AConfiguration_getNavHidden(AConfiguration* config);
+
+/**
+ * Set the current nav hidden in the configuration.
+ */
+void AConfiguration_setNavHidden(AConfiguration* config, int32_t navHidden);
+
+/**
+ * Return the current SDK (API) version set in the configuration.
+ */
+int32_t AConfiguration_getSdkVersion(AConfiguration* config);
+
+/**
+ * Set the current SDK version in the configuration.
+ */
+void AConfiguration_setSdkVersion(AConfiguration* config, int32_t sdkVersion);
+
+/**
+ * Return the current ACONFIGURATION_SCREENSIZE_* set in the configuration.
+ */
+int32_t AConfiguration_getScreenSize(AConfiguration* config);
+
+/**
+ * Set the current screen size in the configuration.
+ */
+void AConfiguration_setScreenSize(AConfiguration* config, int32_t screenSize);
+
+/**
+ * Return the current ACONFIGURATION_SCREENLONG_* set in the configuration.
+ */
+int32_t AConfiguration_getScreenLong(AConfiguration* config);
+
+/**
+ * Set the current screen long in the configuration.
+ */
+void AConfiguration_setScreenLong(AConfiguration* config, int32_t screenLong);
+
+/**
+ * Return the current ACONFIGURATION_UI_MODE_TYPE_* set in the configuration.
+ */
+int32_t AConfiguration_getUiModeType(AConfiguration* config);
+
+/**
+ * Set the current UI mode type in the configuration.
+ */
+void AConfiguration_setUiModeType(AConfiguration* config, int32_t uiModeType);
+
+/**
+ * Return the current ACONFIGURATION_UI_MODE_NIGHT_* set in the configuration.
+ */
+int32_t AConfiguration_getUiModeNight(AConfiguration* config);
+
+/**
+ * Set the current UI mode night in the configuration.
+ */
+void AConfiguration_setUiModeNight(AConfiguration* config, int32_t uiModeNight);
+
+/**
+ * Perform a diff between two configurations. Returns a bit mask of
+ * ACONFIGURATION_* constants, each bit set meaning that configuration element
+ * is different between them.
+ */
+int32_t AConfiguration_diff(AConfiguration* config1, AConfiguration* config2);
+
+/**
+ * Determine whether 'base' is a valid configuration for use within the
+ * environment 'requested'. Returns 0 if there are any values in 'base'
+ * that conflict with 'requested'. Returns 1 if it does not conflict.
+ */
+int32_t AConfiguration_match(AConfiguration* base, AConfiguration* requested);
+
+/**
+ * Determine whether the configuration in 'test' is better than the existing
+ * configuration in 'base'. If 'requested' is non-NULL, this decision is based
+ * on the overall configuration given there. If it is NULL, this decision is
+ * simply based on which configuration is more specific. Returns non-0 if
+ * 'test' is better than 'base'.
+ *
+ * This assumes you have already filtered the configurations with
+ * AConfiguration_match().
+ */
+int32_t AConfiguration_isBetterThan(AConfiguration* base, AConfiguration* test,
+ AConfiguration* requested);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // ANDROID_CONFIGURATION_H
diff --git a/native/include/android/native_activity.h b/native/include/android/native_activity.h
index ee4204d884f2..d74e1ce5a3d3 100644
--- a/native/include/android/native_activity.h
+++ b/native/include/android/native_activity.h
@@ -197,6 +197,12 @@ typedef struct ANativeActivityCallbacks {
void (*onContentRectChanged)(ANativeActivity* activity, const ARect* rect);
/**
+ * The current device AConfiguration has changed. The new configuration can
+ * be retrieved from assetManager.
+ */
+ void (*onConfigurationChanged)(ANativeActivity* activity);
+
+ /**
* The system is running low on memory. Use this callback to release
* resources you do not need, to help the system avoid killing more
* important processes.
@@ -208,7 +214,9 @@ typedef struct ANativeActivityCallbacks {
* This is the function that must be in the native code to instantiate the
* application's native activity. It is called with the activity instance (see
* above); if the code is being instantiated from a previously saved instance,
- * the savedState will be non-NULL and point to the saved data.
+ * the savedState will be non-NULL and point to the saved data. You must make
+ * any copy of this data you need -- it will be released after you return from
+ * this function.
*/
typedef void ANativeActivity_createFunc(ANativeActivity* activity,
void* savedState, size_t savedStateSize);
diff --git a/native/include/android/native_window.h b/native/include/android/native_window.h
index 7599d7e550f6..ad03d0e930da 100644
--- a/native/include/android/native_window.h
+++ b/native/include/android/native_window.h
@@ -36,12 +36,23 @@ struct ANativeWindow;
typedef struct ANativeWindow ANativeWindow;
typedef struct ANativeWindow_Buffer {
+ // The number of pixels that are show horizontally.
int32_t width;
+
+ // The number of pixels that are shown vertically.
int32_t height;
+
+ // The number of *pixels* that a line in the buffer takes in
+ // memory. This may be >= width.
int32_t stride;
+
+ // The format of the buffer. One of WINDOW_FORMAT_*
int32_t format;
+
+ // The actual bits.
void* bits;
+ // Do not touch.
uint32_t reserved[6];
} ANativeWindow_Buffer;
diff --git a/opengl/libs/EGL/getProcAddress.cpp b/opengl/libs/EGL/getProcAddress.cpp
index 23837efa9f49..dcf8735c634d 100644
--- a/opengl/libs/EGL/getProcAddress.cpp
+++ b/opengl/libs/EGL/getProcAddress.cpp
@@ -27,9 +27,12 @@ namespace android {
// ----------------------------------------------------------------------------
#undef API_ENTRY
-#undef CALL_GL_API
+#undef CALL_GL_EXTENSION_API
#undef GL_EXTENSION
#undef GL_EXTENSION_NAME
+#undef GL_EXTENSION_ARRAY
+#undef GL_EXTENSION_LIST
+#undef GET_TLS
#if defined(__arm__)
@@ -60,7 +63,7 @@ namespace android {
: \
);
- #define GL_EXTENSION_NAME(_n) __glExtFwd##_n
+ #define GL_EXTENSION_NAME(_n) __glExtFwd##_n
#define GL_EXTENSION(_n) \
void API_ENTRY(GL_EXTENSION_NAME(_n))() { \
@@ -78,97 +81,40 @@ namespace android {
#endif
-GL_EXTENSION(0)
-GL_EXTENSION(1)
-GL_EXTENSION(2)
-GL_EXTENSION(3)
-GL_EXTENSION(4)
-GL_EXTENSION(5)
-GL_EXTENSION(6)
-GL_EXTENSION(7)
-GL_EXTENSION(8)
-GL_EXTENSION(9)
-GL_EXTENSION(10)
-GL_EXTENSION(11)
-GL_EXTENSION(12)
-GL_EXTENSION(13)
-GL_EXTENSION(14)
-GL_EXTENSION(15)
-
-GL_EXTENSION(16)
-GL_EXTENSION(17)
-GL_EXTENSION(18)
-GL_EXTENSION(19)
-GL_EXTENSION(20)
-GL_EXTENSION(21)
-GL_EXTENSION(22)
-GL_EXTENSION(23)
-GL_EXTENSION(24)
-GL_EXTENSION(25)
-GL_EXTENSION(26)
-GL_EXTENSION(27)
-GL_EXTENSION(28)
-GL_EXTENSION(29)
-GL_EXTENSION(30)
-GL_EXTENSION(31)
-
-GL_EXTENSION(32)
-GL_EXTENSION(33)
-GL_EXTENSION(34)
-GL_EXTENSION(35)
-GL_EXTENSION(36)
-GL_EXTENSION(37)
-GL_EXTENSION(38)
-GL_EXTENSION(39)
-GL_EXTENSION(40)
-GL_EXTENSION(41)
-GL_EXTENSION(42)
-GL_EXTENSION(43)
-GL_EXTENSION(44)
-GL_EXTENSION(45)
-GL_EXTENSION(46)
-GL_EXTENSION(47)
-
-GL_EXTENSION(48)
-GL_EXTENSION(49)
-GL_EXTENSION(50)
-GL_EXTENSION(51)
-GL_EXTENSION(52)
-GL_EXTENSION(53)
-GL_EXTENSION(54)
-GL_EXTENSION(55)
-GL_EXTENSION(56)
-GL_EXTENSION(57)
-GL_EXTENSION(58)
-GL_EXTENSION(59)
-GL_EXTENSION(60)
-GL_EXTENSION(61)
-GL_EXTENSION(62)
-GL_EXTENSION(63)
+#define GL_EXTENSION_LIST(name) \
+ name(0) name(1) name(2) name(3) \
+ name(4) name(5) name(6) name(7) \
+ name(8) name(9) name(10) name(11) \
+ name(12) name(13) name(14) name(15) \
+ name(16) name(17) name(18) name(19) \
+ name(20) name(21) name(22) name(23) \
+ name(24) name(25) name(26) name(27) \
+ name(28) name(29) name(30) name(31) \
+ name(32) name(33) name(34) name(35) \
+ name(36) name(37) name(38) name(39) \
+ name(40) name(41) name(42) name(43) \
+ name(44) name(45) name(46) name(47) \
+ name(48) name(49) name(50) name(51) \
+ name(52) name(53) name(54) name(55) \
+ name(56) name(57) name(58) name(59) \
+ name(60) name(61) name(62) name(63)
+
+
+GL_EXTENSION_LIST( GL_EXTENSION )
+
+#define GL_EXTENSION_ARRAY(_n) GL_EXTENSION_NAME(_n),
extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS] = {
- GL_EXTENSION_NAME(0), GL_EXTENSION_NAME(1), GL_EXTENSION_NAME(2), GL_EXTENSION_NAME(3),
- GL_EXTENSION_NAME(4), GL_EXTENSION_NAME(5), GL_EXTENSION_NAME(6), GL_EXTENSION_NAME(7),
- GL_EXTENSION_NAME(8), GL_EXTENSION_NAME(9), GL_EXTENSION_NAME(10), GL_EXTENSION_NAME(11),
- GL_EXTENSION_NAME(12), GL_EXTENSION_NAME(13), GL_EXTENSION_NAME(14), GL_EXTENSION_NAME(15),
- GL_EXTENSION_NAME(16), GL_EXTENSION_NAME(17), GL_EXTENSION_NAME(18), GL_EXTENSION_NAME(19),
- GL_EXTENSION_NAME(20), GL_EXTENSION_NAME(21), GL_EXTENSION_NAME(22), GL_EXTENSION_NAME(23),
- GL_EXTENSION_NAME(24), GL_EXTENSION_NAME(25), GL_EXTENSION_NAME(26), GL_EXTENSION_NAME(27),
- GL_EXTENSION_NAME(28), GL_EXTENSION_NAME(29), GL_EXTENSION_NAME(30), GL_EXTENSION_NAME(31),
- GL_EXTENSION_NAME(32), GL_EXTENSION_NAME(33), GL_EXTENSION_NAME(34), GL_EXTENSION_NAME(35),
- GL_EXTENSION_NAME(36), GL_EXTENSION_NAME(37), GL_EXTENSION_NAME(38), GL_EXTENSION_NAME(39),
- GL_EXTENSION_NAME(40), GL_EXTENSION_NAME(41), GL_EXTENSION_NAME(42), GL_EXTENSION_NAME(43),
- GL_EXTENSION_NAME(44), GL_EXTENSION_NAME(45), GL_EXTENSION_NAME(46), GL_EXTENSION_NAME(47),
- GL_EXTENSION_NAME(48), GL_EXTENSION_NAME(49), GL_EXTENSION_NAME(50), GL_EXTENSION_NAME(51),
- GL_EXTENSION_NAME(52), GL_EXTENSION_NAME(53), GL_EXTENSION_NAME(54), GL_EXTENSION_NAME(55),
- GL_EXTENSION_NAME(56), GL_EXTENSION_NAME(57), GL_EXTENSION_NAME(58), GL_EXTENSION_NAME(59),
- GL_EXTENSION_NAME(60), GL_EXTENSION_NAME(61), GL_EXTENSION_NAME(62), GL_EXTENSION_NAME(63)
+ GL_EXTENSION_LIST( GL_EXTENSION_ARRAY )
};
+#undef GET_TLS
+#undef GL_EXTENSION_LIST
+#undef GL_EXTENSION_ARRAY
#undef GL_EXTENSION_NAME
#undef GL_EXTENSION
#undef API_ENTRY
-#undef CALL_GL_API
+#undef CALL_GL_EXTENSION_API
// ----------------------------------------------------------------------------
}; // namespace android
diff --git a/opengl/libs/GLES2/gl2.cpp b/opengl/libs/GLES2/gl2.cpp
index 924737e697db..a12edf238e7d 100644
--- a/opengl/libs/GLES2/gl2.cpp
+++ b/opengl/libs/GLES2/gl2.cpp
@@ -39,6 +39,8 @@ using namespace android;
#undef CALL_GL_API
#undef CALL_GL_API_RETURN
+#define DEBUG_CALL_GL_API 0
+
#if USE_FAST_TLS_KEY
#ifdef HAVE_ARM_TLS_REGISTER
@@ -73,10 +75,24 @@ using namespace android;
#define API_ENTRY(_api) _api
+#if DEBUG_CALL_GL_API
+
+ #define CALL_GL_API(_api, ...) \
+ gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; \
+ _c->_api(__VA_ARGS__); \
+ GLenum status = GL_NO_ERROR; \
+ while ((status = glGetError()) != GL_NO_ERROR) { \
+ LOGD("[" #_api "] 0x%x", status); \
+ }
+
+#else
+
#define CALL_GL_API(_api, ...) \
gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; \
- _c->_api(__VA_ARGS__)
-
+ _c->_api(__VA_ARGS__);
+
+#endif
+
#define CALL_GL_API_RETURN(_api, ...) \
gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; \
return _c->_api(__VA_ARGS__)
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index f1c6532a90fb..c6e0a2423a1f 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -24,6 +24,8 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
+import android.content.res.ObbInfo;
+import android.content.res.ObbScanner;
import android.net.Uri;
import android.os.Environment;
import android.os.IBinder;
@@ -142,6 +144,10 @@ public class DefaultContainerService extends IntentService {
public boolean checkFreeStorage(boolean external, Uri fileUri) {
return checkFreeStorageInner(external, fileUri);
}
+
+ public ObbInfo getObbInfo(String filename) {
+ return ObbScanner.getObbInfo(filename);
+ }
};
public DefaultContainerService() {
diff --git a/packages/SystemUI/res/drawable-hdpi/button_frame_default.9.png b/packages/SystemUI/res/drawable-hdpi/button_frame_default.9.png
new file mode 100644
index 000000000000..d809b84004e9
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/button_frame_default.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/button_frame_pressed.9.png b/packages/SystemUI/res/drawable-hdpi/button_frame_pressed.9.png
new file mode 100644
index 000000000000..5cc007c0bf39
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/button_frame_pressed.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_expand_default.png b/packages/SystemUI/res/drawable-hdpi/status_bar_expand_default.png
new file mode 100644
index 000000000000..92fc7f8da470
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/status_bar_expand_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_expand_pressed.png b/packages/SystemUI/res/drawable-hdpi/status_bar_expand_pressed.png
new file mode 100644
index 000000000000..77f09acb0fdd
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/status_bar_expand_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/system_panel_airplane_default.png b/packages/SystemUI/res/drawable-hdpi/system_panel_airplane_default.png
new file mode 100644
index 000000000000..e375ee97a245
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/system_panel_airplane_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/system_panel_brightness_default.png b/packages/SystemUI/res/drawable-hdpi/system_panel_brightness_default.png
new file mode 100644
index 000000000000..f62502b12519
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/system_panel_brightness_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/system_panel_orientation_default.png b/packages/SystemUI/res/drawable-hdpi/system_panel_orientation_default.png
new file mode 100644
index 000000000000..e887fb8adbee
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/system_panel_orientation_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/system_panel_orientation_locked.png b/packages/SystemUI/res/drawable-hdpi/system_panel_orientation_locked.png
new file mode 100644
index 000000000000..58159d5a836d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/system_panel_orientation_locked.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/system_panel_sound_default.png b/packages/SystemUI/res/drawable-hdpi/system_panel_sound_default.png
new file mode 100644
index 000000000000..6e857b5ef232
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/system_panel_sound_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/button_frame_default.9.png b/packages/SystemUI/res/drawable-mdpi/button_frame_default.9.png
new file mode 100644
index 000000000000..7ab1f261cb27
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/button_frame_default.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/button_frame_pressed.9.png b/packages/SystemUI/res/drawable-mdpi/button_frame_pressed.9.png
new file mode 100644
index 000000000000..08f7a4d6bb45
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/button_frame_pressed.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_expand_default.png b/packages/SystemUI/res/drawable-mdpi/status_bar_expand_default.png
new file mode 100644
index 000000000000..4d197179368d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_expand_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_expand_pressed.png b/packages/SystemUI/res/drawable-mdpi/status_bar_expand_pressed.png
new file mode 100644
index 000000000000..830cbd521858
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/status_bar_expand_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/system_panel_airplane_default.png b/packages/SystemUI/res/drawable-mdpi/system_panel_airplane_default.png
new file mode 100644
index 000000000000..eb875323cd6a
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/system_panel_airplane_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/system_panel_brightness_default.png b/packages/SystemUI/res/drawable-mdpi/system_panel_brightness_default.png
new file mode 100644
index 000000000000..3bfc83e8fae3
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/system_panel_brightness_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/system_panel_orientation_default.png b/packages/SystemUI/res/drawable-mdpi/system_panel_orientation_default.png
new file mode 100644
index 000000000000..0d8479caf1c2
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/system_panel_orientation_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/system_panel_orientation_locked.png b/packages/SystemUI/res/drawable-mdpi/system_panel_orientation_locked.png
new file mode 100644
index 000000000000..8f1d26ceb8c3
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/system_panel_orientation_locked.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/system_panel_sound_default.png b/packages/SystemUI/res/drawable-mdpi/system_panel_sound_default.png
new file mode 100644
index 000000000000..22636d604248
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/system_panel_sound_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/button_frame.xml b/packages/SystemUI/res/drawable/button_frame.xml
new file mode 100644
index 000000000000..5db39a5da562
--- /dev/null
+++ b/packages/SystemUI/res/drawable/button_frame.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true" android:drawable="@drawable/button_frame_pressed" />
+ <item android:drawable="@drawable/button_frame_default" />
+</selector>
+
diff --git a/packages/SystemUI/res/drawable/status_bar_expand.xml b/packages/SystemUI/res/drawable/status_bar_expand.xml
new file mode 100644
index 000000000000..f966920344cd
--- /dev/null
+++ b/packages/SystemUI/res/drawable/status_bar_expand.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true" android:drawable="@drawable/status_bar_expand_pressed" />
+ <item android:drawable="@drawable/status_bar_expand_default" />
+</selector>
+
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar.xml b/packages/SystemUI/res/layout-xlarge/status_bar.xml
index 9ae2c250139a..ffb1571a44c1 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar.xml
@@ -39,13 +39,13 @@
android:onClick="notificationIconsClicked"
android:background="@drawable/status_bar_icon_tray"
>
- <view
+ <ImageView
class="com.android.systemui.statusbar.tablet.NotificationIconArea$MoreView"
- android:id="@+id/more"
+ android:id="@+id/expand"
android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:src="@drawable/stat_notify_more"
- android:layout_marginLeft="10dip"
+ android:layout_height="wrap_content"
+ android:src="@drawable/status_bar_expand"
+ android:onClick="notificationIconsClicked"
/>
<view
class="com.android.systemui.statusbar.tablet.NotificationIconArea$IconLayout"
@@ -64,39 +64,47 @@
</com.android.systemui.statusbar.tablet.NotificationIconArea>
- <RelativeLayout android:id="@+id/systemInfo"
- android:layout_width="200dip"
+ <LinearLayout android:id="@+id/ticker"
+ android:layout_width="300dip"
android:layout_height="match_parent"
- android:layout_centerHorizontal="true"
- android:clickable="true"
- android:onClick="systemInfoClicked"
+ android:paddingLeft="6dip"
+ android:animationCache="false"
+ android:layout_alignLeft="@id/notificationIcons"
+ android:layout_alignTop="@id/notificationIcons"
+ android:orientation="horizontal"
+ android:visibility="gone"
>
- <com.android.systemui.statusbar.Clock
- android:id="@+id/clock"
- android:textAppearance="@*android:style/TextAppearance.StatusBar.Icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerInParent="true"
- android:singleLine="true"
- android:textSize="16sp"
- android:textStyle="bold"
- android:padding="6dip"
- />
- <ImageView
+ <ImageView android:id="@+id/tickerIcon"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toLeftOf="@id/clock"
- android:layout_centerVertical="true"
- android:src="@drawable/dots_empty"
+ android:layout_height="match_parent"
+ android:layout_marginRight="8dip"
/>
- <ImageView
- android:layout_width="wrap_content"
+ <com.android.systemui.statusbar.TickerView android:id="@+id/tickerText"
+ android:layout_width="0dip"
+ android:layout_weight="1"
android:layout_height="wrap_content"
- android:layout_toRightOf="@id/clock"
- android:layout_centerVertical="true"
- android:src="@drawable/dots_full"
- />
- </RelativeLayout>
+ android:paddingTop="2dip"
+ android:paddingRight="10dip">
+ <TextView
+ android:textAppearance="@*android:style/TextAppearance.StatusBar.Ticker"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ />
+ <TextView
+ android:textAppearance="@*android:style/TextAppearance.StatusBar.Ticker"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ />
+ </com.android.systemui.statusbar.TickerView>
+ </LinearLayout>
+
+ <include layout="@layout/status_bar_center"
+ android:layout_width="100dip"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ />
<com.android.systemui.statusbar.KeyButtonView android:id="@+id/back"
android:layout_width="wrap_content"
@@ -125,60 +133,5 @@
android:src="@drawable/status_bar_home"
systemui:keyCode="3"
/>
-
-<!--
-
- <LinearLayout android:id="@+id/ticker"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingLeft="6dip"
- android:animationCache="false"
- android:orientation="horizontal" >
- <ImageSwitcher android:id="@+id/tickerIcon"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_marginRight="8dip"
- >
- <com.android.systemui.statusbar.AnimatedImageView
- android:layout_width="25dip"
- android:layout_height="25dip"
- />
- <com.android.systemui.statusbar.AnimatedImageView
- android:layout_width="25dip"
- android:layout_height="25dip"
- />
- </ImageSwitcher>
- <com.android.systemui.statusbar.TickerView android:id="@+id/tickerText"
- android:layout_width="0dip"
- android:layout_weight="1"
- android:layout_height="wrap_content"
- android:paddingTop="2dip"
- android:paddingRight="10dip">
- <TextView
- android:textAppearance="@*android:style/TextAppearance.StatusBar.Ticker"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- />
- <TextView
- android:textAppearance="@*android:style/TextAppearance.StatusBar.Ticker"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- />
- </com.android.systemui.statusbar.TickerView>
- </LinearLayout>
-
- <com.android.systemui.statusbar.DateView android:id="@+id/date"
- android:textAppearance="@*android:style/TextAppearance.StatusBar.Icon"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:singleLine="true"
- android:gravity="center_vertical|left"
- android:paddingLeft="6px"
- android:paddingRight="6px"
- android:background="@drawable/status_bar_background"
- />
--->
</RelativeLayout>
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_center.xml b/packages/SystemUI/res/layout-xlarge/status_bar_center.xml
new file mode 100644
index 000000000000..c32e997d947a
--- /dev/null
+++ b/packages/SystemUI/res/layout-xlarge/status_bar_center.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Center of status bar: System info display, system info panel trigger -->
+<RelativeLayout android:id="@+id/systemInfo"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
+ android:layout_width="100dip"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:clickable="true"
+ android:onClick="systemInfoClicked"
+ >
+ <com.android.systemui.statusbar.Clock
+ style="@*android:style/TextAppearance.StatusBar.Icon"
+ android:id="@+id/clock"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:gravity="center"
+ android:textSize="16sp"
+ android:textStyle="bold"
+ android:padding="2dip"
+ />
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_below="@id/clock"
+ android:src="@drawable/dots_empty"
+ />
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/clock"
+ android:src="@drawable/dots_full"
+ />
+</RelativeLayout>
+
+
diff --git a/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml b/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml
index 84a718a70448..3489eec47d37 100644
--- a/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml
+++ b/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml
@@ -46,17 +46,23 @@
android:background="@android:drawable/divider_horizontal_dark"
/>
- <LinearLayout
- android:id="@+id/content"
- android:layout_width="match_parent"
+ <ScrollView
+ android:id="@+id/notificationScroller"
android:layout_height="wrap_content"
- android:gravity="center_horizontal|bottom"
- android:animationCache="false"
- android:orientation="vertical"
- android:background="@drawable/status_bar_background"
- android:clickable="true"
- android:focusable="true"
- android:descendantFocusability="afterDescendants"
+ android:layout_width="match_parent"
>
- </LinearLayout>
+ <LinearLayout
+ android:id="@+id/content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal|bottom"
+ android:animationCache="false"
+ android:orientation="vertical"
+ android:background="@drawable/status_bar_background"
+ android:clickable="true"
+ android:focusable="true"
+ android:descendantFocusability="afterDescendants"
+ >
+ </LinearLayout>
+ </ScrollView>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout-xlarge/sysbar_panel_system.xml b/packages/SystemUI/res/layout-xlarge/sysbar_panel_system.xml
index 2222d082baa2..fc3790078301 100644
--- a/packages/SystemUI/res/layout-xlarge/sysbar_panel_system.xml
+++ b/packages/SystemUI/res/layout-xlarge/sysbar_panel_system.xml
@@ -2,7 +2,7 @@
<!--
/* apps/common/assets/default/default/skins/StatusBar.xml
**
-** Copyright 2006, The Android Open Source Project
+** Copyright 2010, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
@@ -18,35 +18,118 @@
*/
-->
-<!-- android:background="@drawable/status_bar_closed_default_background" -->
-<FrameLayout
+<com.android.systemui.statusbar.tablet.SystemPanel
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_height="300dip"
- android:layout_width="400dip"
- android:paddingLeft="8dip"
- android:paddingRight="8dip"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
android:background="#FF000000"
+ android:orientation="vertical"
>
-
- <RelativeLayout
- android:id="@+id/content"
+
+ <TextView android:id="@+id/settings_button"
+ style="?android:attr/textAppearance"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="left|center_vertical"
+ android:layout_marginTop="2dip"
+ android:layout_marginBottom="1dip"
+ android:layout_marginRight="10dip"
+ android:padding="8dip"
+ android:textSize="20sp"
+ android:text="@string/system_panel_settings_button"
+ />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1sp"
+ android:background="@android:drawable/divider_horizontal_dark"
+ />
+
+ <LinearLayout
+ android:padding="8dip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="center"
+ >
+ <ImageButton android:id="@+id/brightness"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/system_panel_brightness_default"
+ android:background="@drawable/button_frame"
+ />
+ <ImageButton android:id="@+id/sound"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dip"
+ android:src="@drawable/system_panel_sound_default"
+ android:background="@drawable/button_frame"
+ />
+ <ImageButton android:id="@+id/orientation"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dip"
+ android:src="@drawable/system_panel_orientation_default"
+ android:background="@drawable/button_frame"
+ />
+ <ImageButton android:id="@+id/airplane"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dip"
+ android:src="@drawable/system_panel_airplane_default"
+ android:background="@drawable/button_frame"
+ />
+ </LinearLayout>
+
+ <RelativeLayout
+ android:padding="8dip"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:animationCache="false"
- android:background="@drawable/status_bar_background"
- android:clickable="true"
- android:focusable="true"
- android:descendantFocusability="afterDescendants"
+ android:layout_height="wrap_content"
>
+ <ImageView android:id="@+id/battery_meter"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:src="@drawable/dots_empty"
+ />
+
+ <TextView android:id="@+id/battery_info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/battery_meter"
+ />
+
+ <com.android.systemui.statusbar.Clock
+ style="@*android:style/TextAppearance.StatusBar.Icon"
+ android:id="@+id/clock"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:textSize="20sp"
+ android:textStyle="bold"
+ android:padding="2dip"
+ android:layout_centerHorizontal="true"
+ />
+
+ <TextView android:id="@+id/date"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/clock"
+ />
- <TextView
- android:id="@+id/systemPanelDummy"
+ <ImageView android:id="@+id/signal_meter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_centerInParent="true"
- android:textColor="#FFCCCCCC"
- android:textSize="18sp"
+ android:layout_alignParentRight="true"
+ android:src="@drawable/dots_full"
+ />
+
+ <TextView android:id="@+id/signal_info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/signal_meter"
/>
</RelativeLayout>
-</FrameLayout>
+
+</com.android.systemui.statusbar.tablet.SystemPanel>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index ba3a3d1d3538..2df3b6d51858 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -51,4 +51,7 @@
power usage activity to find out what drained the battery. -->
<string name="battery_low_why">Battery use</string>
+ <!-- Name of the button that links to the Settings app. [MAXCHARS=NONE] -->
+ <string name="system_panel_settings_button">Settings</string>
+
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
index 5add6def08c2..91b583b98618 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java
@@ -1086,12 +1086,12 @@ public class PhoneStatusBarService extends StatusBarService {
}
private class MyTicker extends Ticker {
- MyTicker(Context context, StatusBarView sb) {
+ MyTicker(Context context, View sb) {
super(context, sb);
}
@Override
- void tickerStarting() {
+ public void tickerStarting() {
mTicking = true;
mIcons.setVisibility(View.GONE);
mTickerView.setVisibility(View.VISIBLE);
@@ -1103,7 +1103,7 @@ public class PhoneStatusBarService extends StatusBarService {
}
@Override
- void tickerDone() {
+ public void tickerDone() {
mIcons.setVisibility(View.VISIBLE);
mTickerView.setVisibility(View.GONE);
mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
@@ -1114,7 +1114,7 @@ public class PhoneStatusBarService extends StatusBarService {
}
}
- void tickerHalting() {
+ public void tickerHalting() {
mIcons.setVisibility(View.VISIBLE);
mTickerView.setVisibility(View.GONE);
mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java b/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java
index 07e865376680..0aaa3705cebf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/Ticker.java
@@ -141,7 +141,7 @@ public abstract class Ticker {
}
};
- Ticker(Context context, StatusBarView sb) {
+ public Ticker(Context context, View sb) {
mContext = context;
mTickerView = sb.findViewById(R.id.ticker);
@@ -163,7 +163,7 @@ public abstract class Ticker {
}
- void addEntry(StatusBarNotification n) {
+ public void addEntry(StatusBarNotification n) {
int initialCount = mSegments.size();
// If what's being displayed has the same text and icon, just drop it
@@ -212,7 +212,7 @@ public abstract class Ticker {
}
}
- void removeEntry(StatusBarNotification n) {
+ public void removeEntry(StatusBarNotification n) {
for (int i=mSegments.size()-1; i>=0; i--) {
Segment seg = mSegments.get(i);
if (n.id == seg.notification.id && n.pkg.equals(seg.notification.pkg)) {
@@ -221,13 +221,13 @@ public abstract class Ticker {
}
}
- void halt() {
+ public void halt() {
mHandler.removeCallbacks(mAdvanceTicker);
mSegments.clear();
tickerHalting();
}
- void reflowText() {
+ public void reflowText() {
if (mSegments.size() > 0) {
Segment seg = mSegments.get(0);
CharSequence text = seg.getText();
@@ -266,8 +266,8 @@ public abstract class Ticker {
mHandler.postDelayed(mAdvanceTicker, TICKER_SEGMENT_DELAY);
}
- abstract void tickerStarting();
- abstract void tickerDone();
- abstract void tickerHalting();
+ public abstract void tickerStarting();
+ public abstract void tickerDone();
+ public abstract void tickerHalting();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/TickerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/TickerView.java
index 9749ae442741..814081198d8b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/TickerView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/TickerView.java
@@ -34,5 +34,9 @@ public class TickerView extends TextSwitcher
super.onSizeChanged(w, h, oldw, oldh);
mTicker.reflowText();
}
+
+ public void setTicker(Ticker t) {
+ mTicker = t;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationIconArea.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationIconArea.java
index 3c7b13062bf9..7c7d74c3ce10 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationIconArea.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationIconArea.java
@@ -31,24 +31,16 @@ import com.android.systemui.R;
public class NotificationIconArea extends LinearLayout {
private static final String TAG = "NotificationIconArea";
- MoreView mMoreView;
IconLayout mIconLayout;
DraggerView mDraggerView;
public NotificationIconArea(Context context, AttributeSet attrs) {
super(context, attrs);
- mMoreView = (MoreView) findViewById(R.id.more);
mIconLayout = (IconLayout)findViewById(R.id.icons);
mDraggerView = (DraggerView) findViewById(R.id.handle);
}
- static class MoreView extends ImageView {
- public MoreView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
- }
-
static class IconLayout extends LinearLayout {
public IconLayout(Context context, AttributeSet attrs) {
super(context, attrs);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SystemPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SystemPanel.java
new file mode 100644
index 000000000000..236b5215507b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SystemPanel.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.tablet;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.media.AudioManager;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IPowerManager;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.util.AttributeSet;
+import android.util.Slog;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.WindowManagerImpl;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RemoteViews;
+import android.widget.ScrollView;
+import android.widget.TextSwitcher;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.systemui.statusbar.*;
+import com.android.systemui.R;
+
+public class SystemPanel extends LinearLayout {
+ private static final String TAG = "SystemPanel";
+
+ private static final int MINIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_DIM + 5;
+ private static final int MAXIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_ON;
+ private static final int DEFAULT_BACKLIGHT = (int) (android.os.Power.BRIGHTNESS_ON * 0.4f);
+
+
+ private TabletStatusBarService mBar;
+ private boolean mAirplaneMode;
+
+ private ImageButton mBrightnessButton;
+ private ImageButton mSoundButton;
+ private ImageButton mOrientationButton;
+ private ImageButton mAirplaneButton;
+
+ private final AudioManager mAudioManager;
+
+
+ private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
+ mSoundButton.setAlpha(getSilentMode() ? 0x7F : 0xFF);
+ }
+ }
+ };
+
+ public void setBar(TabletStatusBarService bar) {
+ mBar = bar;
+ }
+
+ public SystemPanel(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public SystemPanel(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ // get notified of phone state changes
+ TelephonyManager telephonyManager =
+ (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
+
+ mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ }
+
+ public void onAttachedToWindow() {
+ TextView settingsButton = (TextView)findViewById(R.id.settings_button);
+ settingsButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ getContext().startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ mBar.animateCollapse();
+ }});
+
+ mBrightnessButton = (ImageButton)findViewById(R.id.brightness);
+ mBrightnessButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ rotateBrightness();
+ }
+ });
+
+ mSoundButton = (ImageButton)findViewById(R.id.sound);
+ mSoundButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ setSilentMode(!getSilentMode());
+ mSoundButton.setAlpha(getSilentMode() ? 0x7F : 0xFF);
+ }
+ });
+ mOrientationButton = (ImageButton)findViewById(R.id.orientation);
+ mOrientationButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ Toast.makeText(getContext(), "Orientation control not implemented; please adjust neck angle.", Toast.LENGTH_SHORT).show();
+ }
+ });
+
+ mAirplaneButton = (ImageButton)findViewById(R.id.airplane);
+ mAirplaneButton.setAlpha(mAirplaneMode ? 0xFF : 0x7F);
+ mAirplaneButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ boolean newMode = !getAirplaneMode();
+ Toast.makeText(getContext(), "Attempting to turn "
+ + (newMode ? "on" : "off") + " airplane mode (flaky).",
+ Toast.LENGTH_SHORT).show();
+ setAirplaneMode(newMode);
+ }
+ });
+
+ IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
+ getContext().registerReceiver(mReceiver, filter);
+ }
+
+ public void onDetachedFromWindow() {
+ getContext().unregisterReceiver(mReceiver);
+ }
+
+ // ----------------------------------------------------------------------
+
+// private boolean isAutoBrightness() {
+// Context context = getContext();
+// try {
+// IPowerManager power = IPowerManager.Stub.asInterface(
+// ServiceManager.getService("power"));
+// if (power != null) {
+// int brightnessMode = Settings.System.getInt(context.getContentResolver(),
+// Settings.System.SCREEN_BRIGHTNESS_MODE);
+// return brightnessMode == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC;
+// }
+// } catch (RemoteException e) {
+// } catch (Settings.SettingNotFoundException e) {
+// }
+// return false;
+// }
+
+ private void rotateBrightness() {
+ int alpha = 0xFF;
+ Context context = getContext();
+ try {
+ IPowerManager power = IPowerManager.Stub.asInterface(
+ ServiceManager.getService("power"));
+ if (power != null) {
+ ContentResolver cr = context.getContentResolver();
+ int brightness = Settings.System.getInt(cr,
+ Settings.System.SCREEN_BRIGHTNESS);
+ int brightnessMode = Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL;
+ //Only get brightness setting if available
+ if (context.getResources().getBoolean(
+ com.android.internal.R.bool.config_automatic_brightness_available)) {
+ brightnessMode = Settings.System.getInt(cr,
+ Settings.System.SCREEN_BRIGHTNESS_MODE);
+ }
+
+ // Rotate AUTO -> MINIMUM -> DEFAULT -> MAXIMUM
+ // Technically, not a toggle...
+ if (brightnessMode == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC) {
+ brightness = MINIMUM_BACKLIGHT;
+ brightnessMode = Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL;
+ alpha = 0x40;
+ } else if (brightness < DEFAULT_BACKLIGHT) {
+ brightness = DEFAULT_BACKLIGHT;
+ alpha = 0xC0;
+ } else if (brightness < MAXIMUM_BACKLIGHT) {
+ brightness = MAXIMUM_BACKLIGHT;
+ alpha = 0xFF;
+ } else {
+ brightnessMode = Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC;
+ brightness = MINIMUM_BACKLIGHT;
+ alpha = 0x60;
+ }
+
+ if (context.getResources().getBoolean(
+ com.android.internal.R.bool.config_automatic_brightness_available)) {
+ // Set screen brightness mode (automatic or manual)
+ Settings.System.putInt(context.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ brightnessMode);
+ } else {
+ // Make sure we set the brightness if automatic mode isn't available
+ brightnessMode = Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL;
+ }
+ if (brightnessMode == Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL) {
+ power.setBacklightBrightness(brightness);
+ Settings.System.putInt(cr, Settings.System.SCREEN_BRIGHTNESS, brightness);
+ }
+ }
+ } catch (RemoteException e) {
+ } catch (Settings.SettingNotFoundException e) {
+ }
+
+ mBrightnessButton.setAlpha(alpha);
+ }
+
+ PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+ @Override
+ public void onServiceStateChanged(ServiceState serviceState) {
+ Slog.d(TAG, "phone service state changed: " + serviceState.getState());
+ mAirplaneMode = serviceState.getState() == ServiceState.STATE_POWER_OFF;
+ if (mAirplaneButton != null) {
+ mAirplaneButton.setAlpha(mAirplaneMode ? 0xFF : 0x7F);
+ }
+ }
+ };
+
+ private boolean getAirplaneMode() {
+ return mAirplaneMode;
+ }
+
+ private void setAirplaneMode(boolean on) {
+ Settings.System.putInt(
+ mContext.getContentResolver(),
+ Settings.System.AIRPLANE_MODE_ON,
+ on ? 1 : 0);
+ Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra("state", on);
+ getContext().sendBroadcast(intent);
+ }
+
+ boolean getSilentMode() {
+ return mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL;
+ }
+
+ void setSilentMode(boolean on) {
+ if (on) {
+ mAudioManager.setRingerMode((Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.VIBRATE_IN_SILENT, 1) == 1)
+ ? AudioManager.RINGER_MODE_VIBRATE
+ : AudioManager.RINGER_MODE_SILENT);
+ } else {
+ mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
+ }
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
index b1c4ee3d9c53..9c86f2d71164 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java
@@ -16,30 +16,34 @@
package com.android.systemui.statusbar.tablet;
+import android.animation.Animator;
+import android.app.ActivityManagerNative;
import android.app.Notification;
+import android.app.PendingIntent;
import android.app.Service;
+import android.app.StatusBarManager;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.PixelFormat;
+import android.graphics.Rect;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
+import android.os.RemoteException;
import android.util.Slog;
import android.view.Gravity;
import android.view.LayoutInflater;
-import android.widget.RemoteViews;
-import android.app.ActivityManagerNative;
-import android.app.PendingIntent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
-import android.graphics.Rect;
-import android.os.RemoteException;
import android.view.WindowManagerImpl;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
+import android.widget.RemoteViews;
+import android.widget.ScrollView;
+import android.widget.TextSwitcher;
import android.widget.TextView;
import com.android.internal.statusbar.StatusBarIcon;
@@ -53,26 +57,37 @@ public class TabletStatusBarService extends StatusBarService {
public static final boolean DEBUG = false;
public static final String TAG = "TabletStatusBar";
- View mStatusBarView;
- NotificationIconArea mNotificationIconArea;
+
int mIconSize;
H mHandler = new H();
+ // tracking all current notifications
+ private NotificationData mNotns = new NotificationData();
+
+ View mStatusBarView;
+ NotificationIconArea mNotificationIconArea;
+
View mNotificationPanel;
- View mSystemPanel;
+ SystemPanel mSystemPanel;
ViewGroup mPile;
TextView mClearButton;
NotificationIconArea.IconLayout mIconLayout;
- private NotificationData mNotns = new NotificationData();
-
+ KickerController mKicker;
+ View mKickerView;
+ boolean mTicking;
+ boolean mExpandedVisible;
+
+ // for disabling the status bar
+ int mDisabled = 0;
+
protected void addPanelWindows() {
mNotificationPanel = View.inflate(this, R.layout.sysbar_panel_notifications, null);
- mSystemPanel = View.inflate(this, R.layout.sysbar_panel_system, null);
+ mSystemPanel = (SystemPanel) View.inflate(this, R.layout.sysbar_panel_system, null);
mNotificationPanel.setVisibility(View.GONE);
mSystemPanel.setVisibility(View.GONE);
@@ -86,8 +101,8 @@ public class TabletStatusBarService extends StatusBarService {
ViewGroup.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
PixelFormat.TRANSLUCENT);
@@ -98,12 +113,12 @@ public class TabletStatusBarService extends StatusBarService {
WindowManagerImpl.getDefault().addView(mNotificationPanel, lp);
lp = new WindowManager.LayoutParams(
- 400, // ViewGroup.LayoutParams.WRAP_CONTENT,
- 200, // ViewGroup.LayoutParams.WRAP_CONTENT,
+ 500, // ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
PixelFormat.TRANSLUCENT);
@@ -112,23 +127,12 @@ public class TabletStatusBarService extends StatusBarService {
lp.windowAnimations = com.android.internal.R.style.Animation_SlidingCard;
WindowManagerImpl.getDefault().addView(mSystemPanel, lp);
-
- // Lorem ipsum, Dolores
- TextView tv = ((TextView) mSystemPanel.findViewById(R.id.systemPanelDummy));
- if (tv != null) tv.setText("System status: great");
-
- mPile = (ViewGroup)mNotificationPanel.findViewById(R.id.content);
- mPile.removeAllViews();
-
- mClearButton = (TextView)mNotificationPanel.findViewById(R.id.clear_all_button);
- mClearButton.setOnClickListener(mClearButtonListener);
+ mSystemPanel.setBar(this);
}
@Override
public void onCreate() {
super.onCreate(); // will add the main bar view
-
- addPanelWindows();
}
protected View makeStatusBarView() {
@@ -145,6 +149,20 @@ public class TabletStatusBarService extends StatusBarService {
// where the icons go
mIconLayout = (NotificationIconArea.IconLayout) sb.findViewById(R.id.icons);
+ mKicker = new KickerController((Context)this, mStatusBarView);
+
+ // Add the windows
+ addPanelWindows();
+
+ mPile = (ViewGroup)mNotificationPanel.findViewById(R.id.content);
+ mPile.removeAllViews();
+
+ ScrollView scroller = (ScrollView)mPile.getParent();
+ scroller.setFillViewport(true);
+
+ mClearButton = (TextView)mNotificationPanel.findViewById(R.id.clear_all_button);
+ mClearButton.setOnClickListener(mClearButtonListener);
+
return sb;
}
@@ -162,10 +180,12 @@ public class TabletStatusBarService extends StatusBarService {
case MSG_OPEN_NOTIFICATION_PANEL:
if (DEBUG) Slog.d(TAG, "opening notifications panel");
mNotificationPanel.setVisibility(View.VISIBLE);
+ mExpandedVisible = true;
break;
case MSG_CLOSE_NOTIFICATION_PANEL:
if (DEBUG) Slog.d(TAG, "closing notifications panel");
mNotificationPanel.setVisibility(View.GONE);
+ mExpandedVisible = false;
break;
case MSG_OPEN_SYSTEM_PANEL:
if (DEBUG) Slog.d(TAG, "opening system panel");
@@ -195,8 +215,7 @@ public class TabletStatusBarService extends StatusBarService {
public void addNotification(IBinder key, StatusBarNotification notification) {
if (DEBUG) Slog.d(TAG, "addNotification(" + key + " -> " + notification + ")");
addNotificationViews(key, notification);
-
- // TODO: kicker; immersive mode
+ // tick()
}
public void updateNotification(IBinder key, StatusBarNotification notification) {
@@ -274,7 +293,127 @@ public class TabletStatusBarService extends StatusBarService {
}
public void disable(int state) {
- // TODO
+ /*
+ final int old = mDisabled;
+ final int diff = state ^ old;
+ mDisabled = state;
+
+ if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
+ if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
+ Slog.d(TAG, "DISABLE_EXPAND: yes");
+ animateCollapse();
+ }
+ }
+ if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+ if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+ Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
+ if (mTicking) {
+ mKicker.halt();
+ } else {
+ mNotificationIconArea.setVisibility(View.INVISIBLE);
+ }
+ } else {
+ Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
+ if (!mExpandedVisible) {
+ mNotificationIconArea.setVisibility(View.VISIBLE);
+ }
+ }
+ } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+ if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+ Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: yes");
+ mKicker.halt();
+ }
+ }
+ */
+ }
+
+ void performDisableActions(int net) {
+ /*
+ int old = mDisabled;
+ int diff = net ^ old;
+ mDisabled = net;
+
+ // act accordingly
+ if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
+ if ((net & StatusBarManager.DISABLE_EXPAND) != 0) {
+ Slog.d(TAG, "DISABLE_EXPAND: yes");
+ animateCollapse();
+ }
+ }
+ if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+ if ((net & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+ Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
+ if (mTicking) {
+ mNotificationIconArea.setVisibility(View.INVISIBLE);
+ mKicker.halt();
+ } else {
+ mNotificationIconArea.setVisibility(View.INVISIBLE);
+ }
+ } else {
+ Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
+ if (!mExpandedVisible) {
+ mNotificationIconArea.setVisibility(View.VISIBLE);
+ }
+ }
+ } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+ if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+ mKicker.halt();
+ }
+ }
+ */
+ }
+
+ private void tick(StatusBarNotification n) {
+ // Show the ticker if one is requested. Also don't do this
+ // until status bar window is attached to the window manager,
+ // because... well, what's the point otherwise? And trying to
+ // run a ticker without being attached will crash!
+ if (n.notification.tickerText != null && mStatusBarView.getWindowToken() != null) {
+ if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
+ | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
+ mKicker.addEntry(n);
+ }
+ }
+ }
+
+ private class KickerController {
+ View mView;
+ ImageView mKickerIcon;
+ TextSwitcher mKickerText;
+
+ public KickerController(Context context, View sb) {
+ mView = sb.findViewById(R.id.ticker);
+ mKickerIcon = (ImageView) mView.findViewById(R.id.tickerIcon);
+ mKickerText = (TextSwitcher) mView.findViewById(R.id.tickerText);
+ }
+
+ public void halt() {
+ tickerHalting();
+ }
+
+ public void addEntry(StatusBarNotification n) {
+ mKickerIcon.setImageResource(n.notification.icon);
+ mKickerText.setCurrentText(n.notification.tickerText);
+ tickerStarting();
+ }
+
+ public void tickerStarting() {
+ mTicking = true;
+ mIconLayout.setVisibility(View.GONE);
+ mKickerView.setVisibility(View.VISIBLE);
+ }
+
+ public void tickerDone() {
+ mIconLayout.setVisibility(View.VISIBLE);
+ mKickerView.setVisibility(View.GONE);
+ mTicking = false;
+ }
+
+ public void tickerHalting() {
+ mIconLayout.setVisibility(View.VISIBLE);
+ mKickerView.setVisibility(View.GONE);
+ mTicking = false;
+ }
}
public void animateExpand() {
@@ -405,8 +544,9 @@ public class TabletStatusBarService extends StatusBarService {
}
StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) {
- NotificationData list = mNotns;
- ViewGroup parent = mPile;
+ if (DEBUG) {
+ Slog.d(TAG, "addNotificationViews(key=" + key + ", notification=" + notification);
+ }
// Construct the icon.
final StatusBarIconView iconView = new StatusBarIconView(this,
notification.pkg + "/0x" + Integer.toHexString(notification.id));
@@ -422,19 +562,13 @@ public class TabletStatusBarService extends StatusBarService {
}
// Construct the expanded view.
NotificationData.Entry entry = new NotificationData.Entry(key, notification, iconView);
- if (!inflateViews(entry, parent)) {
+ if (!inflateViews(entry, mPile)) {
handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
+ notification);
return null;
}
- // Add the expanded view.
- final int viewIndex = list.add(entry);
- if (parent != null) parent.addView(entry.row, viewIndex);
// Add the icon.
-// final int iconIndex = 0; // XXX: sort into ongoing and regular buckets
-// mIconLayout.addView(iconView, iconIndex,
-// new LinearLayout.LayoutParams(mIconSize, mIconSize));
-
+ mNotns.add(entry);
refreshIcons();
return iconView;
@@ -443,13 +577,25 @@ public class TabletStatusBarService extends StatusBarService {
private void refreshIcons() {
// XXX: need to implement a new limited linear layout class
// to avoid removing & readding everything
- mIconLayout.removeAllViews();
+
int N = mNotns.size();
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(mIconSize, mIconSize);
+
+ if (DEBUG) {
+ Slog.d(TAG, "refreshing icons (" + N + " notifications, mIconLayout="
+ + mIconLayout + ", mPile=" + mPile);
+ }
+
+ mIconLayout.removeAllViews();
for (int i=0; i<4; i++) {
if (i>=N) break;
mIconLayout.addView(mNotns.get(N-i-1).icon, i, params);
}
+
+ mPile.removeAllViews();
+ for (int i=0; i<N; i++) {
+ mPile.addView(mNotns.get(N-i-1).row);
+ }
}
private boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 73fa93c345a3..546c0f447c6c 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -406,7 +406,12 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
@Override
public final void openPanel(int featureId, KeyEvent event) {
- openPanel(getPanelState(featureId, true), event);
+ if (featureId == FEATURE_OPTIONS_PANEL && mActionBar != null &&
+ mActionBar.isOverflowReserved()) {
+ mActionBar.showOverflowMenu();
+ } else {
+ openPanel(getPanelState(featureId, true), event);
+ }
}
private void openPanel(PanelFeatureState st, KeyEvent event) {
@@ -497,7 +502,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
@Override
public final void closePanel(int featureId) {
- if (featureId == FEATURE_CONTEXT_MENU) {
+ if (featureId == FEATURE_OPTIONS_PANEL && mActionBar != null &&
+ mActionBar.isOverflowReserved()) {
+ mActionBar.hideOverflowMenu();
+ } else if (featureId == FEATURE_CONTEXT_MENU) {
closeContextMenu();
} else {
closePanel(getPanelState(featureId, true), true);
@@ -591,7 +599,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
// The panel key was pushed, so set the chording key
mPanelChordingKey = keyCode;
mPanelMayLongPress = false;
-
+
PanelFeatureState st = getPanelState(featureId, true);
if (!st.isOpen) {
if (getContext().getResources().getConfiguration().keyboard
@@ -600,7 +608,6 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
return preparePanel(st, event);
}
-
} else if (mPanelMayLongPress && mPanelChordingKey == keyCode
&& (event.getFlags()&KeyEvent.FLAG_LONG_PRESS) != 0) {
// We have had a long press while in a state where this
@@ -635,25 +642,40 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
boolean playSoundEffect = false;
- PanelFeatureState st = getPanelState(featureId, true);
- if (st.isOpen || st.isHandled) {
+ final PanelFeatureState st = getPanelState(featureId, true);
+ if (featureId == FEATURE_OPTIONS_PANEL && mActionBar != null) {
+ if (mActionBar.isOverflowReserved()) {
+ if (!mActionBar.isOverflowMenuShowing()) {
+ final Callback cb = getCallback();
+ if (cb != null) {
+ if (cb.onPreparePanel(featureId, st.createdPanelView, st.menu)) {
+ playSoundEffect = mActionBar.showOverflowMenu();
+ }
+ }
+ } else {
+ playSoundEffect = mActionBar.hideOverflowMenu();
+ }
+ }
+ } else {
+ if (st.isOpen || st.isHandled) {
- // Play the sound effect if the user closed an open menu (and not if
- // they just released a menu shortcut)
- playSoundEffect = st.isOpen;
+ // Play the sound effect if the user closed an open menu (and not if
+ // they just released a menu shortcut)
+ playSoundEffect = st.isOpen;
- // Close menu
- closePanel(st, true);
+ // Close menu
+ closePanel(st, true);
- } else if (st.isPrepared) {
+ } else if (st.isPrepared) {
- // Write 'menu opened' to event log
- EventLog.writeEvent(50001, 0);
+ // Write 'menu opened' to event log
+ EventLog.writeEvent(50001, 0);
- // Show menu
- openPanel(st, event);
+ // Show menu
+ openPanel(st, event);
- playSoundEffect = true;
+ playSoundEffect = true;
+ }
}
if (playSoundEffect) {
@@ -833,6 +855,21 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
private void reopenMenu(boolean toggleMenuMode) {
+ if (mActionBar != null) {
+ if (!mActionBar.isOverflowMenuShowing() || !toggleMenuMode) {
+ final Callback cb = getCallback();
+ if (cb != null) {
+ final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
+ if (cb.onPreparePanel(FEATURE_OPTIONS_PANEL, st.createdPanelView, st.menu)) {
+ mActionBar.showOverflowMenu();
+ }
+ }
+ } else {
+ mActionBar.hideOverflowMenu();
+ }
+ return;
+ }
+
PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
// Save the future expanded mode state since closePanel will reset it
@@ -1379,12 +1416,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
case KeyEvent.KEYCODE_MENU: {
- if (mActionBar != null && mActionBar.isOverflowReserved()) {
- mActionBar.showOverflowMenu();
- } else {
- onKeyUpPanel(featureId < 0 ? FEATURE_OPTIONS_PANEL : featureId,
- event);
- }
+ onKeyUpPanel(featureId < 0 ? FEATURE_OPTIONS_PANEL : featureId,
+ event);
return true;
}
@@ -2239,6 +2272,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
requestFeature(FEATURE_ACTION_BAR);
}
+ if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBarOverlay, false)) {
+ requestFeature(FEATURE_ACTION_BAR_OVERLAY);
+ }
+
if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionModeOverlay, false)) {
requestFeature(FEATURE_ACTION_MODE_OVERLAY);
}
@@ -2325,7 +2362,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
if (mIsFloating) {
layoutResource = com.android.internal.R.layout.dialog_title;
} else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
- layoutResource = com.android.internal.R.layout.screen_action_bar;
+ if ((features & (1 << FEATURE_ACTION_BAR_OVERLAY)) != 0) {
+ layoutResource = com.android.internal.R.layout.screen_action_bar_overlay;
+ } else {
+ layoutResource = com.android.internal.R.layout.screen_action_bar;
+ }
} else {
layoutResource = com.android.internal.R.layout.screen_title;
}
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index cb7fe06dcb2f..a07c38583107 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -37,6 +37,7 @@ import android.graphics.Rect;
import android.os.Handler;
import android.os.IBinder;
import android.os.LocalPowerManager;
+import android.os.Looper;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -48,15 +49,20 @@ import android.provider.Settings;
import com.android.internal.policy.PolicyManager;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.telephony.ITelephony;
+import com.android.internal.view.BaseInputHandler;
import com.android.internal.widget.PointerLocationView;
import android.util.Config;
import android.util.EventLog;
import android.util.Log;
+import android.util.Slog;
import android.view.Display;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.IWindowManager;
+import android.view.InputChannel;
+import android.view.InputQueue;
+import android.view.InputHandler;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.WindowOrientationListener;
@@ -221,6 +227,17 @@ public class PhoneWindowManager implements WindowManagerPolicy {
int mPointerLocationMode = 0;
PointerLocationView mPointerLocationView = null;
+ InputChannel mPointerLocationInputChannel;
+
+ private final InputHandler mPointerLocationInputHandler = new BaseInputHandler() {
+ @Override
+ public void handleMotion(MotionEvent event, Runnable finishedCallback) {
+ finishedCallback.run();
+ synchronized (mLock) {
+ mPointerLocationView.addTouchEvent(event);
+ }
+ }
+ };
// The current size of the screen.
int mW, mH;
@@ -617,8 +634,26 @@ public class PhoneWindowManager implements WindowManagerPolicy {
WindowManagerImpl wm = (WindowManagerImpl)
mContext.getSystemService(Context.WINDOW_SERVICE);
wm.addView(addView, lp);
+
+ if (mPointerLocationInputChannel == null) {
+ try {
+ mPointerLocationInputChannel =
+ mWindowManager.monitorInput("PointerLocationView");
+ InputQueue.registerInputChannel(mPointerLocationInputChannel,
+ mPointerLocationInputHandler, mHandler.getLooper().getQueue());
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Could not set up input monitoring channel for PointerLocation.",
+ ex);
+ }
+ }
}
if (removeView != null) {
+ if (mPointerLocationInputChannel != null) {
+ InputQueue.unregisterInputChannel(mPointerLocationInputChannel);
+ mPointerLocationInputChannel.dispose();
+ mPointerLocationInputChannel = null;
+ }
+
WindowManagerImpl wm = (WindowManagerImpl)
mContext.getSystemService(Context.WINDOW_SERVICE);
wm.removeView(removeView);
@@ -732,20 +767,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
: Configuration.KEYBOARDHIDDEN_YES;
}
- public void dispatchedPointerEventLw(MotionEvent ev, int targetX, int targetY) {
- if (mPointerLocationView == null) {
- return;
- }
- synchronized (mLock) {
- if (mPointerLocationView == null) {
- return;
- }
- ev.offsetLocation(targetX, targetY);
- mPointerLocationView.addTouchEvent(ev);
- ev.offsetLocation(-targetX, -targetY);
- }
- }
-
/** {@inheritDoc} */
public int windowTypeToLayerLw(int type) {
if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 252b42ab097f..1c7faa4ac9b8 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1696,7 +1696,10 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track
// Delegate master volume control to effect in output mix effect chain if needed
sp<EffectChain> chain = getEffectChain_l(AudioSystem::SESSION_OUTPUT_MIX);
if (chain != 0) {
- uint32_t v = (uint32_t)(masterVolume * (1 << 24));
+ uint32_t v = 0;
+ if (!masterMute) {
+ v = (uint32_t)(masterVolume * (1 << 24));
+ }
chain->setVolume_l(&v, &v);
masterVolume = (float)((v + (1 << 23)) >> 24);
chain.clear();
@@ -1750,7 +1753,7 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track
// compute volume for this track
int16_t left, right, aux;
- if (track->isMuted() || masterMute || track->isPausing() ||
+ if (track->isMuted() || track->isPausing() ||
mStreamTypes[track->type()].mute) {
left = right = aux = 0;
if (track->isPausing()) {
@@ -5351,7 +5354,7 @@ void AudioFlinger::EffectModule::process()
return;
}
- if (mState == ACTIVE || mState == STOPPING || mState == STOPPED) {
+ if (mState == ACTIVE || mState == STOPPING || mState == STOPPED || mState == RESTART) {
// do 32 bit to 16 bit conversion for auxiliary effect input buffer
if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
AudioMixer::ditherAndClamp(mConfig.inputCfg.buffer.s32,
@@ -6032,8 +6035,8 @@ void AudioFlinger::EffectHandle::dump(char* buffer, size_t size)
AudioFlinger::EffectChain::EffectChain(const wp<ThreadBase>& wThread,
int sessionId)
: mThread(wThread), mSessionId(sessionId), mActiveTrackCnt(0), mOwnInBuffer(false),
- mVolumeCtrlIdx(-1), mLeftVolume(0), mRightVolume(0),
- mNewLeftVolume(0), mNewRightVolume(0)
+ mVolumeCtrlIdx(-1), mLeftVolume(UINT_MAX), mRightVolume(UINT_MAX),
+ mNewLeftVolume(UINT_MAX), mNewRightVolume(UINT_MAX)
{
mStrategy = AudioSystem::getStrategyForStream(AudioSystem::MUSIC);
}
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index cc1566b7096f..ae4e1682044d 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -314,14 +314,14 @@ public class ConnectivityService extends IConnectivityManager.Stub {
case ConnectivityManager.TYPE_WIFI:
if (DBG) Slog.v(TAG, "Starting Wifi Service.");
WifiStateTracker wst = new WifiStateTracker(context, mHandler);
- WifiService wifiService = new WifiService(context, wst);
+ WifiService wifiService = new WifiService(context);
ServiceManager.addService(Context.WIFI_SERVICE, wifiService);
- wifiService.startWifi();
+ wifiService.checkAndStartWifi();
mNetTrackers[ConnectivityManager.TYPE_WIFI] = wst;
wst.startMonitoring();
//TODO: as part of WWS refactor, create only when needed
- mWifiWatchdogService = new WifiWatchdogService(context, wst);
+ mWifiWatchdogService = new WifiWatchdogService(context);
break;
case ConnectivityManager.TYPE_MOBILE:
@@ -1205,16 +1205,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
sendConnectedBroadcast(info);
}
- private void handleScanResultsAvailable(NetworkInfo info) {
- int networkType = info.getType();
- if (networkType != ConnectivityManager.TYPE_WIFI) {
- if (DBG) Slog.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) {
NotificationManager notificationManager = (NotificationManager) mContext
@@ -1619,11 +1609,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
break;
- case NetworkStateTracker.EVENT_SCAN_RESULTS_AVAILABLE:
- info = (NetworkInfo) msg.obj;
- handleScanResultsAvailable(info);
- break;
-
case NetworkStateTracker.EVENT_NOTIFICATION_CHANGED:
handleNotificationChange(msg.arg1 == 1, msg.arg2,
(Notification) msg.obj);
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index 91951233fca1..c2c799b4e0a0 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -75,7 +75,8 @@ public class InputManager {
int sw);
private static native boolean nativeHasKeys(int deviceId, int sourceMask,
int[] keyCodes, boolean[] keyExists);
- private static native void nativeRegisterInputChannel(InputChannel inputChannel);
+ private static native void nativeRegisterInputChannel(InputChannel inputChannel,
+ boolean monitor);
private static native void nativeUnregisterInputChannel(InputChannel inputChannel);
private static native int nativeInjectInputEvent(InputEvent event,
int injectorPid, int injectorUid, int syncMode, int timeoutMillis);
@@ -225,14 +226,38 @@ public class InputManager {
return nativeHasKeys(deviceId, sourceMask, keyCodes, keyExists);
}
+ /**
+ * Creates an input channel that will receive all input from the input dispatcher.
+ * @param inputChannelName The input channel name.
+ * @return The input channel.
+ */
+ public InputChannel monitorInput(String inputChannelName) {
+ if (inputChannelName == null) {
+ throw new IllegalArgumentException("inputChannelName must not be null.");
+ }
+
+ InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
+ nativeRegisterInputChannel(inputChannels[0], true);
+ inputChannels[0].dispose(); // don't need to retain the Java object reference
+ return inputChannels[1];
+ }
+
+ /**
+ * Registers an input channel so that it can be used as an input event target.
+ * @param inputChannel The input channel to register.
+ */
public void registerInputChannel(InputChannel inputChannel) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null.");
}
- nativeRegisterInputChannel(inputChannel);
+ nativeRegisterInputChannel(inputChannel, false);
}
+ /**
+ * Unregisters an input channel.
+ * @param inputChannel The input channel to unregister.
+ */
public void unregisterInputChannel(InputChannel inputChannel) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null.");
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 36b3a5ecee88..164142e909d6 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -77,9 +77,12 @@ import android.view.inputmethod.EditorInfo;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
+import java.text.Collator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
/**
* This class provides a system service that manages input methods.
@@ -463,6 +466,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
screenOnOffFilt.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
mContext.registerReceiver(new ScreenOnOffReceiver(), screenOnOffFilt);
+ mStatusBar = statusBar;
+ statusBar.setIconVisibility("ime", false);
+
buildInputMethodListLocked(mMethodList, mMethodMap);
final String enabledStr = Settings.Secure.getString(
@@ -506,9 +512,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
- mStatusBar = statusBar;
- statusBar.setIconVisibility("ime", false);
-
mSettingsObserver = new SettingsObserver(mHandler);
updateFromSettingsLocked();
}
@@ -1217,6 +1220,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (mCurClient == null || client == null
|| mCurClient.client.asBinder() != client.asBinder()) {
Slog.w(TAG, "Ignoring showInputMethodDialogFromClient of: " + client);
+ return;
}
mHandler.sendEmptyMessage(MSG_SHOW_IM_PICKER);
@@ -1508,21 +1512,22 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
hideInputMethodMenuLocked();
int N = immis.size();
-
- mItems = new CharSequence[N];
- mIms = new InputMethodInfo[N];
-
- int j = 0;
+
+ final Map<CharSequence, InputMethodInfo> imMap =
+ new TreeMap<CharSequence, InputMethodInfo>(Collator.getInstance());
+
for (int i = 0; i < N; ++i) {
InputMethodInfo property = immis.get(i);
if (property == null) {
continue;
}
- mItems[j] = property.loadLabel(pm);
- mIms[j] = property;
- j++;
+ imMap.put(property.loadLabel(pm), property);
}
-
+
+ N = imMap.size();
+ mItems = imMap.keySet().toArray(new CharSequence[N]);
+ mIms = imMap.values().toArray(new InputMethodInfo[N]);
+
int checkedItem = 0;
for (int i = 0; i < N; ++i) {
if (mIms[i].getId().equals(lastInputMethodId)) {
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index ef5e9cc1f614..d604886ed1d3 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -16,34 +16,42 @@
package com.android.server;
+import com.android.internal.app.IMediaContainerService;
import com.android.server.am.ActivityManagerService;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.res.ObbInfo;
-import android.content.res.ObbScanner;
import android.net.Uri;
-import android.os.storage.IMountService;
-import android.os.storage.IMountServiceListener;
-import android.os.storage.IMountShutdownObserver;
-import android.os.storage.StorageResultCode;
import android.os.Binder;
+import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
-import android.os.IBinder;
-import android.os.Environment;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.storage.IMountService;
+import android.os.storage.IMountServiceListener;
+import android.os.storage.IMountShutdownObserver;
+import android.os.storage.IObbActionListener;
+import android.os.storage.StorageResultCode;
import android.util.Slog;
+
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
/**
* MountService implements back-end services for platform storage
@@ -137,9 +145,77 @@ class MountService extends IMountService.Stub
final private HashSet<String> mAsecMountSet = new HashSet<String>();
/**
- * Private hash of currently mounted filesystem images.
+ * Mounted OBB tracking information. Used to track the current state of all
+ * OBBs.
+ */
+ final private Map<IObbActionListener, ObbState> mObbMounts = new HashMap<IObbActionListener, ObbState>();
+ final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
+
+ class ObbState implements IBinder.DeathRecipient {
+ public ObbState(String filename, IObbActionListener token, int callerUid) {
+ this.filename = filename;
+ this.token = token;
+ this.callerUid = callerUid;
+ mounted = false;
+ }
+
+ // OBB source filename
+ String filename;
+
+ // Token of remote Binder caller
+ IObbActionListener token;
+
+ // Binder.callingUid()
+ public int callerUid;
+
+ // Whether this is mounted currently.
+ boolean mounted;
+
+ @Override
+ public void binderDied() {
+ ObbAction action = new UnmountObbAction(this, true);
+ mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
+
+ removeObbState(this);
+
+ token.asBinder().unlinkToDeath(this, 0);
+ }
+ }
+
+ // OBB Action Handler
+ final private ObbActionHandler mObbActionHandler;
+
+ // OBB action handler messages
+ private static final int OBB_RUN_ACTION = 1;
+ private static final int OBB_MCS_BOUND = 2;
+ private static final int OBB_MCS_UNBIND = 3;
+ private static final int OBB_MCS_RECONNECT = 4;
+ private static final int OBB_MCS_GIVE_UP = 5;
+
+ /*
+ * Default Container Service information
*/
- final private HashSet<String> mObbMountSet = new HashSet<String>();
+ static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
+ "com.android.defcontainer", "com.android.defcontainer.DefaultContainerService");
+
+ final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();
+
+ class DefaultContainerConnection implements ServiceConnection {
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "onServiceConnected");
+ IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service);
+ mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_MCS_BOUND, imcs));
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "onServiceDisconnected");
+ }
+ };
+
+ // Used in the ObbActionHandler
+ private IMediaContainerService mContainerService = null;
// Handler messages
private static final int H_UNMOUNT_PM_UPDATE = 1;
@@ -363,7 +439,7 @@ class MountService extends IMountService.Stub
public void binderDied() {
if (LOCAL_LOGD) Slog.d(TAG, "An IMountServiceListener has died!");
- synchronized(mListeners) {
+ synchronized (mListeners) {
mListeners.remove(this);
mListener.asBinder().unlinkToDeath(this, 0);
}
@@ -917,6 +993,9 @@ class MountService extends IMountService.Stub
mHandlerThread.start();
mHandler = new MountServiceHandler(mHandlerThread.getLooper());
+ // Add OBB Action Handler to MountService thread.
+ mObbActionHandler = new ObbActionHandler(mHandlerThread.getLooper());
+
/*
* Vold does not run in the simulator, so pretend the connector thread
* ran and did its thing.
@@ -1013,7 +1092,7 @@ class MountService extends IMountService.Stub
private void setUmsEnabling(boolean enable) {
synchronized (mListeners) {
- mUmsEnabling = true;
+ mUmsEnabling = enable;
}
}
@@ -1351,12 +1430,16 @@ class MountService extends IMountService.Stub
mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
}
- private boolean isCallerOwnerOfPackage(String packageName) {
+ private boolean isCallerOwnerOfPackageOrSystem(String packageName) {
final int callerUid = Binder.getCallingUid();
- return isUidOwnerOfPackage(packageName, callerUid);
+ return isUidOwnerOfPackageOrSystem(packageName, callerUid);
}
- private boolean isUidOwnerOfPackage(String packageName, int callerUid) {
+ private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) {
+ if (callerUid == android.os.Process.SYSTEM_UID) {
+ return true;
+ }
+
if (packageName == null) {
return false;
}
@@ -1375,12 +1458,6 @@ class MountService extends IMountService.Stub
waitForReady();
warnOnNotMounted();
- // XXX replace with call to IMediaContainerService
- ObbInfo obbInfo = ObbScanner.getObbInfo(filename);
- if (!isCallerOwnerOfPackage(obbInfo.packageName)) {
- throw new IllegalArgumentException("Caller package does not match OBB file");
- }
-
try {
ArrayList<String> rsp = mConnector.doCommand(String.format("obb path %s", filename));
String []tok = rsp.get(0).split(" ");
@@ -1392,7 +1469,7 @@ class MountService extends IMountService.Stub
} catch (NativeDaemonConnectorException e) {
int code = e.getCode();
if (code == VoldResponseCode.OpFailedStorageNotFound) {
- throw new IllegalArgumentException(String.format("OBB '%s' not found", filename));
+ return null;
} else {
throw new IllegalStateException(String.format("Unexpected response code %d", code));
}
@@ -1400,95 +1477,390 @@ class MountService extends IMountService.Stub
}
public boolean isObbMounted(String filename) {
+ synchronized (mObbMounts) {
+ return mObbPathToStateMap.containsKey(filename);
+ }
+ }
+
+ public void mountObb(String filename, String key, IObbActionListener token) {
waitForReady();
warnOnNotMounted();
- // XXX replace with call to IMediaContainerService
- ObbInfo obbInfo = ObbScanner.getObbInfo(filename);
- if (!isCallerOwnerOfPackage(obbInfo.packageName)) {
- throw new IllegalArgumentException("Caller package does not match OBB file");
+ final ObbState obbState;
+
+ synchronized (mObbMounts) {
+ if (isObbMounted(filename)) {
+ throw new IllegalArgumentException("OBB file is already mounted");
+ }
+
+ if (mObbMounts.containsKey(token)) {
+ throw new IllegalArgumentException("You may only have one OBB mounted at a time");
+ }
+
+ final int callerUid = Binder.getCallingUid();
+ obbState = new ObbState(filename, token, callerUid);
+ addObbState(obbState);
}
- synchronized (mObbMountSet) {
- return mObbMountSet.contains(filename);
+ try {
+ token.asBinder().linkToDeath(obbState, 0);
+ } catch (RemoteException rex) {
+ Slog.e(TAG, "Failed to link to listener death");
}
+
+ MountObbAction action = new MountObbAction(obbState, key);
+ mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
+
+ if (DEBUG_OBB)
+ Slog.i(TAG, "Send to OBB handler: " + action.toString());
}
- public int mountObb(String filename, String key) {
- waitForReady();
- warnOnNotMounted();
+ public void unmountObb(String filename, boolean force, IObbActionListener token) {
+ final ObbState obbState;
- synchronized (mObbMountSet) {
- if (mObbMountSet.contains(filename)) {
- return StorageResultCode.OperationFailedStorageMounted;
+ synchronized (mObbMounts) {
+ if (!isObbMounted(filename)) {
+ throw new IllegalArgumentException("OBB is not mounted");
}
+ obbState = mObbPathToStateMap.get(filename);
}
- final int callerUid = Binder.getCallingUid();
+ UnmountObbAction action = new UnmountObbAction(obbState, force);
+ mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
+
+ if (DEBUG_OBB)
+ Slog.i(TAG, "Send to OBB handler: " + action.toString());
+ }
- // XXX replace with call to IMediaContainerService
- ObbInfo obbInfo = ObbScanner.getObbInfo(filename);
- if (!isUidOwnerOfPackage(obbInfo.packageName, callerUid)) {
- throw new IllegalArgumentException("Caller package does not match OBB file");
+ private void addObbState(ObbState obbState) {
+ synchronized (mObbMounts) {
+ mObbMounts.put(obbState.token, obbState);
+ mObbPathToStateMap.put(obbState.filename, obbState);
}
+ }
- if (key == null) {
- key = "none";
+ private void removeObbState(ObbState obbState) {
+ synchronized (mObbMounts) {
+ mObbMounts.remove(obbState.token);
+ mObbPathToStateMap.remove(obbState.filename);
}
+ }
- int rc = StorageResultCode.OperationSucceeded;
- String cmd = String.format("obb mount %s %s %d", filename, key, callerUid);
- try {
- mConnector.doCommand(cmd);
- } catch (NativeDaemonConnectorException e) {
- int code = e.getCode();
- if (code != VoldResponseCode.OpFailedStorageBusy) {
- rc = StorageResultCode.OperationFailedInternalError;
+ private class ObbActionHandler extends Handler {
+ private boolean mBound = false;
+ private List<ObbAction> mActions = new LinkedList<ObbAction>();
+
+ ObbActionHandler(Looper l) {
+ super(l);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case OBB_RUN_ACTION: {
+ ObbAction action = (ObbAction) msg.obj;
+
+ if (DEBUG_OBB)
+ Slog.i(TAG, "OBB_RUN_ACTION: " + action.toString());
+
+ // If a bind was already initiated we don't really
+ // need to do anything. The pending install
+ // will be processed later on.
+ if (!mBound) {
+ // If this is the only one pending we might
+ // have to bind to the service again.
+ if (!connectToService()) {
+ Slog.e(TAG, "Failed to bind to media container service");
+ action.handleError();
+ return;
+ } else {
+ // Once we bind to the service, the first
+ // pending request will be processed.
+ mActions.add(action);
+ }
+ } else {
+ // Already bound to the service. Just make
+ // sure we trigger off processing the first request.
+ if (mActions.size() == 0) {
+ mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
+ }
+
+ mActions.add(action);
+ }
+ break;
+ }
+ case OBB_MCS_BOUND: {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "OBB_MCS_BOUND");
+ if (msg.obj != null) {
+ mContainerService = (IMediaContainerService) msg.obj;
+ }
+ if (mContainerService == null) {
+ // Something seriously wrong. Bail out
+ Slog.e(TAG, "Cannot bind to media container service");
+ for (ObbAction action : mActions) {
+ // Indicate service bind error
+ action.handleError();
+ }
+ mActions.clear();
+ } else if (mActions.size() > 0) {
+ ObbAction action = mActions.get(0);
+ if (action != null) {
+ action.execute(this);
+ }
+ } else {
+ // Should never happen ideally.
+ Slog.w(TAG, "Empty queue");
+ }
+ break;
+ }
+ case OBB_MCS_RECONNECT: {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "OBB_MCS_RECONNECT");
+ if (mActions.size() > 0) {
+ if (mBound) {
+ disconnectService();
+ }
+ if (!connectToService()) {
+ Slog.e(TAG, "Failed to bind to media container service");
+ for (ObbAction action : mActions) {
+ // Indicate service bind error
+ action.handleError();
+ }
+ mActions.clear();
+ }
+ }
+ break;
+ }
+ case OBB_MCS_UNBIND: {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "OBB_MCS_UNBIND");
+
+ // Delete pending install
+ if (mActions.size() > 0) {
+ mActions.remove(0);
+ }
+ if (mActions.size() == 0) {
+ if (mBound) {
+ disconnectService();
+ }
+ } else {
+ // There are more pending requests in queue.
+ // Just post MCS_BOUND message to trigger processing
+ // of next pending install.
+ mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
+ }
+ break;
+ }
+ case OBB_MCS_GIVE_UP: {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "OBB_MCS_GIVE_UP");
+ mActions.remove(0);
+ break;
+ }
}
}
- if (rc == StorageResultCode.OperationSucceeded) {
- synchronized (mObbMountSet) {
- mObbMountSet.add(filename);
+ private boolean connectToService() {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "Trying to bind to DefaultContainerService");
+
+ Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
+ if (mContext.bindService(service, mDefContainerConn, Context.BIND_AUTO_CREATE)) {
+ mBound = true;
+ return true;
}
+ return false;
+ }
+
+ private void disconnectService() {
+ mContainerService = null;
+ mBound = false;
+ mContext.unbindService(mDefContainerConn);
}
- return rc;
}
- public int unmountObb(String filename, boolean force) {
- waitForReady();
- warnOnNotMounted();
+ abstract class ObbAction {
+ private static final int MAX_RETRIES = 3;
+ private int mRetries;
+
+ ObbState mObbState;
- ObbInfo obbInfo = ObbScanner.getObbInfo(filename);
- if (!isCallerOwnerOfPackage(obbInfo.packageName)) {
- throw new IllegalArgumentException("Caller package does not match OBB file");
+ ObbAction(ObbState obbState) {
+ mObbState = obbState;
}
- synchronized (mObbMountSet) {
- if (!mObbMountSet.contains(filename)) {
- return StorageResultCode.OperationFailedStorageNotMounted;
+ public void execute(ObbActionHandler handler) {
+ try {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "Starting to execute action: " + this.toString());
+ mRetries++;
+ if (mRetries > MAX_RETRIES) {
+ Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
+ mObbActionHandler.sendEmptyMessage(OBB_MCS_GIVE_UP);
+ handleError();
+ return;
+ } else {
+ handleExecute();
+ if (DEBUG_OBB)
+ Slog.i(TAG, "Posting install MCS_UNBIND");
+ mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
+ }
+ } catch (RemoteException e) {
+ if (DEBUG_OBB)
+ Slog.i(TAG, "Posting install MCS_RECONNECT");
+ mObbActionHandler.sendEmptyMessage(OBB_MCS_RECONNECT);
+ } catch (Exception e) {
+ if (DEBUG_OBB)
+ Slog.d(TAG, "Error handling OBB action", e);
+ handleError();
}
- }
+ }
- int rc = StorageResultCode.OperationSucceeded;
- String cmd = String.format("obb unmount %s%s", filename, (force ? " force" : ""));
- try {
- mConnector.doCommand(cmd);
- } catch (NativeDaemonConnectorException e) {
- int code = e.getCode();
- if (code == VoldResponseCode.OpFailedStorageBusy) {
- rc = StorageResultCode.OperationFailedStorageBusy;
+ abstract void handleExecute() throws RemoteException;
+ abstract void handleError();
+ }
+
+ class MountObbAction extends ObbAction {
+ private String mKey;
+
+ MountObbAction(ObbState obbState, String key) {
+ super(obbState);
+ mKey = key;
+ }
+
+ public void handleExecute() throws RemoteException {
+ ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename);
+ if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mObbState.callerUid)) {
+ throw new IllegalArgumentException("Caller package does not match OBB file");
+ }
+
+ if (mKey == null) {
+ mKey = "none";
+ }
+
+ int rc = StorageResultCode.OperationSucceeded;
+ String cmd = String.format("obb mount %s %s %d", mObbState.filename, mKey,
+ mObbState.callerUid);
+ try {
+ mConnector.doCommand(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ int code = e.getCode();
+ if (code != VoldResponseCode.OpFailedStorageBusy) {
+ rc = StorageResultCode.OperationFailedInternalError;
+ }
+ }
+
+ if (rc == StorageResultCode.OperationSucceeded) {
+ try {
+ mObbState.token.onObbResult(mObbState.filename, "mounted");
+ } catch (RemoteException e) {
+ Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
+ }
} else {
- rc = StorageResultCode.OperationFailedInternalError;
+ Slog.e(TAG, "Couldn't mount OBB file");
+
+ // We didn't succeed, so remove this from the mount-set.
+ removeObbState(mObbState);
}
}
- if (rc == StorageResultCode.OperationSucceeded) {
- synchronized (mObbMountSet) {
- mObbMountSet.remove(filename);
+ public void handleError() {
+ removeObbState(mObbState);
+
+ try {
+ mObbState.token.onObbResult(mObbState.filename, "error");
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Couldn't send back OBB mount error for " + mObbState.filename);
}
}
- return rc;
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("MountObbAction{");
+ sb.append("filename=");
+ sb.append(mObbState.filename);
+ sb.append(",callerUid=");
+ sb.append(mObbState.callerUid);
+ sb.append(",token=");
+ sb.append(mObbState.token != null ? mObbState.token.toString() : "NULL");
+ sb.append('}');
+ return sb.toString();
+ }
+ }
+
+ class UnmountObbAction extends ObbAction {
+ private boolean mForceUnmount;
+
+ UnmountObbAction(ObbState obbState, boolean force) {
+ super(obbState);
+ mForceUnmount = force;
+ }
+
+ public void handleExecute() throws RemoteException {
+ ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename);
+
+ if (!isCallerOwnerOfPackageOrSystem(obbInfo.packageName)) {
+ throw new IllegalArgumentException("Caller package does not match OBB file");
+ }
+
+ int rc = StorageResultCode.OperationSucceeded;
+ String cmd = String.format("obb unmount %s%s", mObbState.filename,
+ (mForceUnmount ? " force" : ""));
+ try {
+ mConnector.doCommand(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ int code = e.getCode();
+ if (code == VoldResponseCode.OpFailedStorageBusy) {
+ rc = StorageResultCode.OperationFailedStorageBusy;
+ } else {
+ rc = StorageResultCode.OperationFailedInternalError;
+ }
+ }
+
+ if (rc == StorageResultCode.OperationSucceeded) {
+ removeObbState(mObbState);
+
+ try {
+ mObbState.token.onObbResult(mObbState.filename, "unmounted");
+ } catch (RemoteException e) {
+ Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
+ }
+ } else {
+ try {
+ mObbState.token.onObbResult(mObbState.filename, "error");
+ } catch (RemoteException e) {
+ Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
+ }
+ }
+ }
+
+ public void handleError() {
+ removeObbState(mObbState);
+
+ try {
+ mObbState.token.onObbResult(mObbState.filename, "error");
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Couldn't send back OBB unmount error for " + mObbState.filename);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("UnmountObbAction{");
+ sb.append("filename=");
+ sb.append(mObbState.filename != null ? mObbState.filename : "null");
+ sb.append(",force=");
+ sb.append(mForceUnmount);
+ sb.append(",callerUid=");
+ sb.append(mObbState.callerUid);
+ sb.append(",token=");
+ sb.append(mObbState.token != null ? mObbState.token.toString() : "null");
+ sb.append('}');
+ return sb.toString();
+ }
}
}
diff --git a/services/java/com/android/server/ViewServer.java b/services/java/com/android/server/ViewServer.java
index b369f716c7c5..7b5d18ac8b2c 100644
--- a/services/java/com/android/server/ViewServer.java
+++ b/services/java/com/android/server/ViewServer.java
@@ -60,6 +60,8 @@ class ViewServer implements Runnable {
private static final String COMMAND_WINDOW_MANAGER_LIST = "LIST";
// Keeps a connection open and notifies when the list of windows changes
private static final String COMMAND_WINDOW_MANAGER_AUTOLIST = "AUTOLIST";
+ // Returns the focused window
+ private static final String COMMAND_WINDOW_MANAGER_GET_FOCUS = "GET_FOCUS";
private ServerSocket mServer;
private Thread mThread;
@@ -250,6 +252,8 @@ class ViewServer implements Runnable {
result = writeValue(mClient, VALUE_SERVER_VERSION);
} else if (COMMAND_WINDOW_MANAGER_LIST.equalsIgnoreCase(command)) {
result = mWindowManager.viewServerListWindows(mClient);
+ } else if (COMMAND_WINDOW_MANAGER_GET_FOCUS.equalsIgnoreCase(command)) {
+ result = mWindowManager.viewServerGetFocusedWindow(mClient);
} else if(COMMAND_WINDOW_MANAGER_AUTOLIST.equalsIgnoreCase(command)) {
result = windowManagerAutolistLoop();
} else {
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 15080b2f25b3..b43b33e4f9ce 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -17,6 +17,8 @@
package com.android.server;
import android.app.AlarmManager;
+import android.app.Notification;
+import android.app.NotificationManager;
import android.app.PendingIntent;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothDevice;
@@ -26,25 +28,30 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.database.ContentObserver;
import android.net.wifi.IWifiManager;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.net.wifi.WifiStateTracker;
+import android.net.wifi.WifiStateMachine;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.SupplicantState;
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.ConnectivityManager;
import android.net.InterfaceConfiguration;
-import android.net.NetworkStateTracker;
import android.net.DhcpInfo;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.State;
import android.os.Binder;
+import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.INetworkManagementService;
+import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
+import android.text.TextUtils;
import android.util.Slog;
import java.util.ArrayList;
@@ -72,7 +79,7 @@ public class WifiService extends IWifiManager.Stub {
private static final String TAG = "WifiService";
private static final boolean DBG = true;
- private final WifiStateTracker mWifiStateTracker;
+ private final WifiStateMachine mWifiStateMachine;
private Context mContext;
@@ -123,10 +130,63 @@ public class WifiService extends IWifiManager.Stub {
private boolean mIsReceiverRegistered = false;
- WifiService(Context context, WifiStateTracker tracker) {
+
+ NetworkInfo mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, "WIFI", "");
+
+ // Variables relating to the 'available networks' notification
+ /**
+ * The icon to show in the 'available networks' notification. This will also
+ * be the ID of the Notification given to the NotificationManager.
+ */
+ private static final int ICON_NETWORKS_AVAILABLE =
+ com.android.internal.R.drawable.stat_notify_wifi_in_range;
+ /**
+ * When a notification is shown, we wait this amount before possibly showing it again.
+ */
+ private final long NOTIFICATION_REPEAT_DELAY_MS;
+ /**
+ * Whether the user has set the setting to show the 'available networks' notification.
+ */
+ private boolean mNotificationEnabled;
+ /**
+ * Observes the user setting to keep {@link #mNotificationEnabled} in sync.
+ */
+ private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver;
+ /**
+ * The {@link System#currentTimeMillis()} must be at least this value for us
+ * to show the notification again.
+ */
+ private long mNotificationRepeatTime;
+ /**
+ * The Notification object given to the NotificationManager.
+ */
+ private Notification mNotification;
+ /**
+ * Whether the notification is being shown, as set by us. That is, if the
+ * user cancels the notification, we will not receive the callback so this
+ * will still be true. We only guarantee if this is false, then the
+ * notification is not showing.
+ */
+ private boolean mNotificationShown;
+ /**
+ * The number of continuous scans that must occur before consider the
+ * supplicant in a scanning state. This allows supplicant to associate with
+ * remembered networks that are in the scan results.
+ */
+ private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3;
+ /**
+ * The number of scans since the last network state change. When this
+ * exceeds {@link #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the
+ * supplicant to actually be scanning. When the network state changes to
+ * something other than scanning, we reset this to 0.
+ */
+ private int mNumScansSinceNetworkStateChange;
+
+
+ WifiService(Context context) {
mContext = context;
- mWifiStateTracker = tracker;
- mWifiStateTracker.enableRssiPolling(true);
+ mWifiStateMachine = new WifiStateMachine(mContext);
+ mWifiStateMachine.enableRssiPolling(true);
mBatteryStats = BatteryStatsService.getService();
mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
@@ -164,6 +224,42 @@ public class WifiService extends IWifiManager.Stub {
}
},new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+
+ mContext.registerReceiver(
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
+ // reset & clear notification on any wifi state change
+ resetNotification();
+ } else if (intent.getAction().equals(
+ WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+ mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
+ WifiManager.EXTRA_NETWORK_INFO);
+ // reset & clear notification on a network connect & disconnect
+ switch(mNetworkInfo.getDetailedState()) {
+ case CONNECTED:
+ case DISCONNECTED:
+ resetNotification();
+ break;
+ }
+ } else if (intent.getAction().equals(
+ WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
+ checkAndSetNotification();
+ }
+ }
+ }, filter);
+
+ // Setting is in seconds
+ NOTIFICATION_REPEAT_DELAY_MS = Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l;
+ mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(new Handler());
+ mNotificationEnabledSettingObserver.register();
}
/**
@@ -172,7 +268,7 @@ public class WifiService extends IWifiManager.Stub {
*
* This function is used only at boot time
*/
- public void startWifi() {
+ public void checkAndStartWifi() {
/* Start if Wi-Fi is enabled or the saved state indicates Wi-Fi was on */
boolean wifiEnabled = !isAirplaneModeOn()
&& (getPersistedWifiEnabled() || testAndClearWifiSavedState());
@@ -255,17 +351,13 @@ public class WifiService extends IWifiManager.Stub {
Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, enabled ? 1 : 0);
}
- NetworkStateTracker getNetworkStateTracker() {
- return mWifiStateTracker;
- }
-
/**
* see {@link android.net.wifi.WifiManager#pingSupplicant()}
* @return {@code true} if the operation succeeds, {@code false} otherwise
*/
public boolean pingSupplicant() {
enforceAccessPermission();
- return mWifiStateTracker.pingSupplicant();
+ return mWifiStateMachine.pingSupplicant();
}
/**
@@ -274,7 +366,7 @@ public class WifiService extends IWifiManager.Stub {
*/
public boolean startScan(boolean forceActive) {
enforceChangePermission();
- return mWifiStateTracker.startScan(forceActive);
+ return mWifiStateMachine.startScan(forceActive);
}
private void enforceAccessPermission() {
@@ -304,7 +396,7 @@ public class WifiService extends IWifiManager.Stub {
enforceChangePermission();
if (DBG) {
- Slog.e(TAG, "Invoking mWifiStateTracker.setWifiEnabled\n");
+ Slog.e(TAG, "Invoking mWifiStateMachine.setWifiEnabled\n");
}
// set a flag if the user is enabling Wifi while in airplane mode
@@ -312,7 +404,7 @@ public class WifiService extends IWifiManager.Stub {
mAirplaneModeOverwridden.set(true);
}
- mWifiStateTracker.setWifiEnabled(enable);
+ mWifiStateMachine.setWifiEnabled(enable);
persistWifiEnabled(enable);
if (enable) {
@@ -338,7 +430,7 @@ public class WifiService extends IWifiManager.Stub {
*/
public int getWifiEnabledState() {
enforceAccessPermission();
- return mWifiStateTracker.getWifiState();
+ return mWifiStateMachine.getWifiState();
}
/**
@@ -362,7 +454,7 @@ public class WifiService extends IWifiManager.Stub {
setWifiApConfiguration(wifiConfig);
}
- mWifiStateTracker.setWifiApEnabled(wifiConfig, enabled);
+ mWifiStateMachine.setWifiApEnabled(wifiConfig, enabled);
return true;
}
@@ -377,7 +469,7 @@ public class WifiService extends IWifiManager.Stub {
*/
public int getWifiApEnabledState() {
enforceAccessPermission();
- return mWifiStateTracker.getWifiApState();
+ return mWifiStateMachine.getWifiApState();
}
/**
@@ -427,7 +519,7 @@ public class WifiService extends IWifiManager.Stub {
*/
public boolean disconnect() {
enforceChangePermission();
- return mWifiStateTracker.disconnectCommand();
+ return mWifiStateMachine.disconnectCommand();
}
/**
@@ -436,7 +528,7 @@ public class WifiService extends IWifiManager.Stub {
*/
public boolean reconnect() {
enforceChangePermission();
- return mWifiStateTracker.reconnectCommand();
+ return mWifiStateMachine.reconnectCommand();
}
/**
@@ -445,7 +537,7 @@ public class WifiService extends IWifiManager.Stub {
*/
public boolean reassociate() {
enforceChangePermission();
- return mWifiStateTracker.reassociateCommand();
+ return mWifiStateMachine.reassociateCommand();
}
/**
@@ -454,7 +546,7 @@ public class WifiService extends IWifiManager.Stub {
*/
public List<WifiConfiguration> getConfiguredNetworks() {
enforceAccessPermission();
- return mWifiStateTracker.getConfiguredNetworks();
+ return mWifiStateMachine.getConfiguredNetworks();
}
/**
@@ -464,7 +556,7 @@ public class WifiService extends IWifiManager.Stub {
*/
public int addOrUpdateNetwork(WifiConfiguration config) {
enforceChangePermission();
- return mWifiStateTracker.addOrUpdateNetwork(config);
+ return mWifiStateMachine.addOrUpdateNetwork(config);
}
/**
@@ -475,7 +567,7 @@ public class WifiService extends IWifiManager.Stub {
*/
public boolean removeNetwork(int netId) {
enforceChangePermission();
- return mWifiStateTracker.removeNetwork(netId);
+ return mWifiStateMachine.removeNetwork(netId);
}
/**
@@ -487,7 +579,7 @@ public class WifiService extends IWifiManager.Stub {
*/
public boolean enableNetwork(int netId, boolean disableOthers) {
enforceChangePermission();
- return mWifiStateTracker.enableNetwork(netId, disableOthers);
+ return mWifiStateMachine.enableNetwork(netId, disableOthers);
}
/**
@@ -498,7 +590,7 @@ public class WifiService extends IWifiManager.Stub {
*/
public boolean disableNetwork(int netId) {
enforceChangePermission();
- return mWifiStateTracker.disableNetwork(netId);
+ return mWifiStateMachine.disableNetwork(netId);
}
/**
@@ -511,7 +603,7 @@ public class WifiService extends IWifiManager.Stub {
* Make sure we have the latest information, by sending
* a status request to the supplicant.
*/
- return mWifiStateTracker.requestConnectionInfo();
+ return mWifiStateMachine.requestConnectionInfo();
}
/**
@@ -521,7 +613,7 @@ public class WifiService extends IWifiManager.Stub {
*/
public List<ScanResult> getScanResults() {
enforceAccessPermission();
- return mWifiStateTracker.getScanResultsList();
+ return mWifiStateMachine.getScanResultsList();
}
/**
@@ -533,7 +625,7 @@ public class WifiService extends IWifiManager.Stub {
public boolean saveConfiguration() {
boolean result = true;
enforceChangePermission();
- return mWifiStateTracker.saveConfig();
+ return mWifiStateMachine.saveConfig();
}
/**
@@ -576,7 +668,7 @@ public class WifiService extends IWifiManager.Stub {
numChannels);
}
- mWifiStateTracker.setNumAllowedChannels(numChannels);
+ mWifiStateMachine.setNumAllowedChannels(numChannels);
return true;
}
@@ -596,7 +688,7 @@ public class WifiService extends IWifiManager.Stub {
* Wi-Fi is not currently enabled), get the value from
* Settings.
*/
- numChannels = mWifiStateTracker.getNumAllowedChannels();
+ numChannels = mWifiStateMachine.getNumAllowedChannels();
if (numChannels < 0) {
numChannels = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS,
@@ -622,9 +714,59 @@ public class WifiService extends IWifiManager.Stub {
*/
public DhcpInfo getDhcpInfo() {
enforceAccessPermission();
- return mWifiStateTracker.getDhcpInfo();
+ return mWifiStateMachine.getDhcpInfo();
+ }
+
+ /**
+ * see {@link android.net.wifi.WifiManager#startWifi}
+ *
+ */
+ public void startWifi() {
+ enforceChangePermission();
+ /* TODO: may be add permissions for access only to connectivity service
+ * TODO: if a start issued, keep wifi alive until a stop issued irrespective
+ * of WifiLock & device idle status unless wifi enabled status is toggled
+ */
+
+ mWifiStateMachine.setDriverStart(true);
+ mWifiStateMachine.reconnectCommand();
+ }
+
+ /**
+ * see {@link android.net.wifi.WifiManager#stopWifi}
+ *
+ */
+ public void stopWifi() {
+ enforceChangePermission();
+ /* TODO: may be add permissions for access only to connectivity service
+ * TODO: if a stop is issued, wifi is brought up only by startWifi
+ * unless wifi enabled status is toggled
+ */
+ mWifiStateMachine.setDriverStart(false);
+ }
+
+
+ /**
+ * see {@link android.net.wifi.WifiManager#addToBlacklist}
+ *
+ */
+ public void addToBlacklist(String bssid) {
+ enforceChangePermission();
+
+ mWifiStateMachine.addToBlacklist(bssid);
+ }
+
+ /**
+ * see {@link android.net.wifi.WifiManager#clearBlacklist}
+ *
+ */
+ public void clearBlacklist() {
+ enforceChangePermission();
+
+ mWifiStateMachine.clearBlacklist();
}
+
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -641,11 +783,11 @@ public class WifiService extends IWifiManager.Stub {
mAlarmManager.cancel(mIdleIntent);
mDeviceIdle = false;
mScreenOff = false;
- mWifiStateTracker.enableRssiPolling(true);
+ mWifiStateMachine.enableRssiPolling(true);
} else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
Slog.d(TAG, "ACTION_SCREEN_OFF");
mScreenOff = true;
- mWifiStateTracker.enableRssiPolling(false);
+ mWifiStateMachine.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
@@ -653,7 +795,7 @@ public class WifiService extends IWifiManager.Stub {
* or plugged in to AC).
*/
if (!shouldWifiStayAwake(stayAwakeConditions, mPluggedType)) {
- WifiInfo info = mWifiStateTracker.requestConnectionInfo();
+ WifiInfo info = mWifiStateMachine.requestConnectionInfo();
if (info.getSupplicantState() != SupplicantState.COMPLETED) {
// we used to go to sleep immediately, but this caused some race conditions
// we don't have time to track down for this release. Delay instead,
@@ -704,7 +846,7 @@ public class WifiService extends IWifiManager.Stub {
isBluetoothPlaying = true;
}
}
- mWifiStateTracker.setBluetoothScanMode(isBluetoothPlaying);
+ mWifiStateMachine.setBluetoothScanMode(isBluetoothPlaying);
} else {
return;
@@ -771,21 +913,21 @@ public class WifiService extends IWifiManager.Stub {
/* Disable tethering when airplane mode is enabled */
if (airplaneMode) {
- mWifiStateTracker.setWifiApEnabled(null, false);
+ mWifiStateMachine.setWifiApEnabled(null, false);
}
if (wifiShouldBeEnabled) {
if (wifiShouldBeStarted) {
- mWifiStateTracker.setWifiEnabled(true);
- mWifiStateTracker.setScanOnlyMode(
+ mWifiStateMachine.setWifiEnabled(true);
+ mWifiStateMachine.setScanOnlyMode(
strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY);
- mWifiStateTracker.startWifi(true);
+ mWifiStateMachine.setDriverStart(true);
} else {
- mWifiStateTracker.requestCmWakeLock();
- mWifiStateTracker.disconnectAndStop();
+ mWifiStateMachine.requestCmWakeLock();
+ mWifiStateMachine.setDriverStart(false);
}
} else {
- mWifiStateTracker.setWifiEnabled(false);
+ mWifiStateMachine.setWifiEnabled(false);
}
}
@@ -832,17 +974,17 @@ public class WifiService extends IWifiManager.Stub {
+ ", uid=" + Binder.getCallingUid());
return;
}
- pw.println("Wi-Fi is " + mWifiStateTracker.getWifiStateByName());
+ pw.println("Wi-Fi is " + mWifiStateMachine.getWifiStateByName());
pw.println("Stay-awake conditions: " +
Settings.System.getInt(mContext.getContentResolver(),
Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0));
pw.println();
pw.println("Internal state:");
- pw.println(mWifiStateTracker);
+ pw.println(mWifiStateMachine);
pw.println();
pw.println("Latest scan results:");
- List<ScanResult> scanResults = mWifiStateTracker.getScanResultsList();
+ List<ScanResult> scanResults = mWifiStateMachine.getScanResultsList();
if (scanResults != null && scanResults.size() != 0) {
pw.println(" BSSID Frequency RSSI Flags SSID");
for (ScanResult r : scanResults) {
@@ -1069,7 +1211,7 @@ public class WifiService extends IWifiManager.Stub {
if (mMulticasters.size() != 0) {
return;
} else {
- mWifiStateTracker.startPacketFiltering();
+ mWifiStateMachine.startPacketFiltering();
}
}
}
@@ -1084,7 +1226,7 @@ public class WifiService extends IWifiManager.Stub {
// our new size == 1 (first call), but this function won't
// be called often and by making the stopPacket call each
// time we're less fragile and self-healing.
- mWifiStateTracker.stopPacketFiltering();
+ mWifiStateMachine.stopPacketFiltering();
}
int uid = Binder.getCallingUid();
@@ -1121,7 +1263,7 @@ public class WifiService extends IWifiManager.Stub {
removed.unlinkDeathRecipient();
}
if (mMulticasters.size() == 0) {
- mWifiStateTracker.startPacketFiltering();
+ mWifiStateMachine.startPacketFiltering();
}
Long ident = Binder.clearCallingIdentity();
@@ -1140,4 +1282,166 @@ public class WifiService extends IWifiManager.Stub {
return (mMulticasters.size() > 0);
}
}
+
+ private void checkAndSetNotification() {
+ // If we shouldn't place a notification on available networks, then
+ // don't bother doing any of the following
+ if (!mNotificationEnabled) return;
+
+ State state = mNetworkInfo.getState();
+ if ((state == NetworkInfo.State.DISCONNECTED)
+ || (state == NetworkInfo.State.UNKNOWN)) {
+ // Look for an open network
+ List<ScanResult> scanResults = mWifiStateMachine.getScanResultsList();
+ if (scanResults != null) {
+ int numOpenNetworks = 0;
+ for (int i = scanResults.size() - 1; i >= 0; i--) {
+ ScanResult scanResult = scanResults.get(i);
+
+ if (TextUtils.isEmpty(scanResult.capabilities)) {
+ numOpenNetworks++;
+ }
+ }
+
+ if (numOpenNetworks > 0) {
+ if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) {
+ /*
+ * We've scanned continuously at least
+ * NUM_SCANS_BEFORE_NOTIFICATION times. The user
+ * probably does not have a remembered network in range,
+ * since otherwise supplicant would have tried to
+ * associate and thus resetting this counter.
+ */
+ setNotificationVisible(true, numOpenNetworks, false, 0);
+ }
+ return;
+ }
+ }
+ }
+
+ // No open networks in range, remove the notification
+ setNotificationVisible(false, 0, false, 0);
+ }
+
+ /**
+ * Clears variables related to tracking whether a notification has been
+ * shown recently and clears the current notification.
+ */
+ private void resetNotification() {
+ mNotificationRepeatTime = 0;
+ mNumScansSinceNetworkStateChange = 0;
+ setNotificationVisible(false, 0, false, 0);
+ }
+
+ /**
+ * Display or don't display a notification that there are open Wi-Fi networks.
+ * @param visible {@code true} if notification should be visible, {@code false} otherwise
+ * @param numNetworks the number networks seen
+ * @param force {@code true} to force notification to be shown/not-shown,
+ * even if it is already shown/not-shown.
+ * @param delay time in milliseconds after which the notification should be made
+ * visible or invisible.
+ */
+ private void setNotificationVisible(boolean visible, int numNetworks, boolean force,
+ int delay) {
+
+ // Since we use auto cancel on the notification, when the
+ // mNetworksAvailableNotificationShown is true, the notification may
+ // have actually been canceled. However, when it is false we know
+ // for sure that it is not being shown (it will not be shown any other
+ // place than here)
+
+ // If it should be hidden and it is already hidden, then noop
+ if (!visible && !mNotificationShown && !force) {
+ return;
+ }
+
+ NotificationManager notificationManager = (NotificationManager) mContext
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+
+ Message message;
+ if (visible) {
+
+ // Not enough time has passed to show the notification again
+ if (System.currentTimeMillis() < mNotificationRepeatTime) {
+ return;
+ }
+
+ if (mNotification == null) {
+ // Cache the Notification mainly so we can remove the
+ // EVENT_NOTIFICATION_CHANGED message with this Notification from
+ // the queue later
+ mNotification = new Notification();
+ mNotification.when = 0;
+ mNotification.icon = ICON_NETWORKS_AVAILABLE;
+ mNotification.flags = Notification.FLAG_AUTO_CANCEL;
+ mNotification.contentIntent = PendingIntent.getActivity(mContext, 0,
+ new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK), 0);
+ }
+
+ CharSequence title = mContext.getResources().getQuantityText(
+ com.android.internal.R.plurals.wifi_available, numNetworks);
+ CharSequence details = mContext.getResources().getQuantityText(
+ com.android.internal.R.plurals.wifi_available_detailed, numNetworks);
+ mNotification.tickerText = title;
+ mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent);
+
+ mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS;
+
+ notificationManager.notify(ICON_NETWORKS_AVAILABLE, mNotification);
+ /*
+ * TODO: Clean up connectivity service & remove this
+ */
+ /* message = mCsHandler.obtainMessage(EVENT_NOTIFICATION_CHANGED, 1,
+ ICON_NETWORKS_AVAILABLE, mNotification); */
+
+
+ } else {
+
+ notificationManager.cancel(ICON_NETWORKS_AVAILABLE);
+ /*
+ * TODO: Clean up connectivity service & remove this
+ */
+ /*
+ // Remove any pending messages to show the notification
+ mCsHandler.removeMessages(EVENT_NOTIFICATION_CHANGED, mNotification);
+
+ message = mCsHandler.obtainMessage(EVENT_NOTIFICATION_CHANGED, 0,
+ ICON_NETWORKS_AVAILABLE);
+ */
+ }
+
+ //mCsHandler.sendMessageDelayed(message, delay);
+
+ mNotificationShown = visible;
+ }
+
+ private class NotificationEnabledSettingObserver extends ContentObserver {
+
+ public NotificationEnabledSettingObserver(Handler handler) {
+ super(handler);
+ }
+
+ public void register() {
+ ContentResolver cr = mContext.getContentResolver();
+ cr.registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this);
+ mNotificationEnabled = getValue();
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+
+ mNotificationEnabled = getValue();
+ resetNotification();
+ }
+
+ private boolean getValue() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1;
+ }
+ }
+
+
}
diff --git a/services/java/com/android/server/WifiWatchdogService.java b/services/java/com/android/server/WifiWatchdogService.java
index be14cd3d70f1..46d6befc0c0e 100644
--- a/services/java/com/android/server/WifiWatchdogService.java
+++ b/services/java/com/android/server/WifiWatchdogService.java
@@ -27,7 +27,6 @@ import android.net.DhcpInfo;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.net.wifi.WifiStateTracker;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -77,7 +76,6 @@ public class WifiWatchdogService {
private Context mContext;
private ContentResolver mContentResolver;
- private WifiStateTracker mWifiStateTracker;
private WifiManager mWifiManager;
/**
@@ -108,10 +106,9 @@ public class WifiWatchdogService {
/** Whether the current AP check should be canceled. */
private boolean mShouldCancel;
- WifiWatchdogService(Context context, WifiStateTracker wifiStateTracker) {
+ WifiWatchdogService(Context context) {
mContext = context;
mContentResolver = context.getContentResolver();
- mWifiStateTracker = wifiStateTracker;
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
createThread();
@@ -752,7 +749,7 @@ public class WifiWatchdogService {
// Black list this "bad" AP, this will cause an attempt to connect to another
blacklistAp(ap.bssid);
// Initiate an association to an alternate AP
- mWifiStateTracker.reassociateCommand();
+ mWifiManager.reassociate();
}
private void blacklistAp(String bssid) {
@@ -763,7 +760,7 @@ public class WifiWatchdogService {
// Before taking action, make sure we should not cancel our processing
if (shouldCancel()) return;
- mWifiStateTracker.addToBlacklist(bssid);
+ mWifiManager.addToBlacklist(bssid);
if (D) {
myLogD("Blacklisting " + bssid);
@@ -858,7 +855,7 @@ public class WifiWatchdogService {
* (and blacklisted them). Clear the blacklist so the AP with best
* signal is chosen.
*/
- mWifiStateTracker.clearBlacklist();
+ mWifiManager.clearBlacklist();
if (V) {
myLogV("handleSleep: Set state to SLEEP and cleared blacklist");
@@ -929,7 +926,7 @@ public class WifiWatchdogService {
* should revert anything done by the watchdog monitoring.
*/
private void handleReset() {
- mWifiStateTracker.clearBlacklist();
+ mWifiManager.clearBlacklist();
setIdleState(true);
}
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index c4de958a0873..3f5888bf84e2 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -62,10 +62,14 @@ import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
+import android.graphics.Canvas;
import android.graphics.Matrix;
+import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Region;
+import android.graphics.Typeface;
+import android.graphics.Paint.FontMetricsInt;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Bundle;
@@ -92,6 +96,7 @@ import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
import android.util.SparseIntArray;
+import android.util.TypedValue;
import android.view.Display;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
@@ -115,6 +120,7 @@ import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.WindowManagerImpl;
import android.view.WindowManagerPolicy;
+import android.view.Surface.OutOfResourcesException;
import android.view.WindowManager.LayoutParams;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
@@ -369,6 +375,7 @@ public class WindowManagerService extends IWindowManager.Stub
private DimAnimator mDimAnimator = null;
Surface mBlurSurface;
boolean mBlurShown;
+ Watermark mWatermark;
int mTransactionSequence = 0;
@@ -4373,11 +4380,19 @@ public class WindowManagerService extends IWindowManager.Stub
}
return mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_DPAD, sw);
}
-
+
public boolean hasKeys(int[] keycodes, boolean[] keyExists) {
return mInputManager.hasKeys(-1, InputDevice.SOURCE_ANY, keycodes, keyExists);
}
+ public InputChannel monitorInput(String inputChannelName) {
+ if (!checkCallingPermission(android.Manifest.permission.READ_INPUT_STATE,
+ "monitorInput()")) {
+ throw new SecurityException("Requires READ_INPUT_STATE permission");
+ }
+ return mInputManager.monitorInput(inputChannelName);
+ }
+
public void enableScreenAfterBoot() {
synchronized(mWindowMap) {
if (mSystemBooted) {
@@ -4720,6 +4735,51 @@ public class WindowManagerService extends IWindowManager.Stub
}
/**
+ * Returns the focused window in the following format:
+ * windowHashCodeInHexadecimal windowName
+ *
+ * @param client The remote client to send the listing to.
+ * @return False if an error occurred, true otherwise.
+ */
+ boolean viewServerGetFocusedWindow(Socket client) {
+ if (isSystemSecure()) {
+ return false;
+ }
+
+ boolean result = true;
+
+ WindowState focusedWindow = getFocusedWindow();
+
+ BufferedWriter out = null;
+
+ // Any uncaught exception will crash the system process
+ try {
+ OutputStream clientStream = client.getOutputStream();
+ out = new BufferedWriter(new OutputStreamWriter(clientStream), 8 * 1024);
+
+ if(focusedWindow != null) {
+ out.write(Integer.toHexString(System.identityHashCode(focusedWindow)));
+ out.write(' ');
+ out.append(focusedWindow.mAttrs.getTitle());
+ }
+ out.write('\n');
+ out.flush();
+ } catch (Exception e) {
+ result = false;
+ } finally {
+ if (out != null) {
+ try {
+ out.close();
+ } catch (IOException e) {
+ result = false;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
* Sends a command to a target window. The result of the command, if any, will be
* written in the output stream of the specified socket.
*
@@ -7817,6 +7877,7 @@ public class WindowManagerService extends IWindowManager.Stub
} catch (RemoteException e) {
// Ignore if process has died.
}
+ notifyFocusChanged();
}
if (lastFocus != null) {
@@ -7826,7 +7887,6 @@ public class WindowManagerService extends IWindowManager.Stub
} catch (RemoteException e) {
// Ignore if process has died.
}
- notifyFocusChanged();
}
}
} break;
@@ -8471,12 +8531,6 @@ public class WindowManagerService extends IWindowManager.Stub
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);
}
- if (mFxSession == null) {
- mFxSession = new SurfaceSession();
- }
-
- if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION");
-
// Initialize state of exiting tokens.
for (i=mExitingTokens.size()-1; i>=0; i--) {
mExitingTokens.get(i).hasVisible = false;
@@ -8493,8 +8547,24 @@ public class WindowManagerService extends IWindowManager.Stub
float buttonBrightness = -1;
boolean focusDisplayed = false;
boolean animating = false;
+ boolean createWatermark = false;
+
+ if (mFxSession == null) {
+ mFxSession = new SurfaceSession();
+ createWatermark = true;
+ }
+
+ if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION");
Surface.openTransaction();
+
+ if (createWatermark) {
+ createWatermark();
+ }
+ if (mWatermark != null) {
+ mWatermark.positionSurface(dw, dh);
+ }
+
try {
boolean wallpaperForceHidingChanged = false;
int repeats = 0;
@@ -9997,6 +10067,93 @@ public class WindowManagerService extends IWindowManager.Stub
mScreenFrozenLock.release();
}
+ static int getPropertyInt(String name, int defUnits, int defDps, DisplayMetrics dm) {
+ String str = SystemProperties.get(name);
+ try {
+ int val = Integer.parseInt(str);
+ return val;
+ } catch (Exception e) {
+ }
+ if (defUnits == TypedValue.COMPLEX_UNIT_PX) {
+ return defDps;
+ }
+ int val = (int)TypedValue.applyDimension(defUnits, defDps, dm);
+ return val;
+ }
+
+ class Watermark {
+ Surface mSurface;
+ int mWidth;
+ int mHeight;
+ int mXPercent;
+ int mYPercent;
+
+ Watermark(SurfaceSession session, String text) {
+ final DisplayMetrics dm = new DisplayMetrics();
+ mDisplay.getMetrics(dm);
+
+ int fontSize = getPropertyInt("ro.watermark.height",
+ TypedValue.COMPLEX_UNIT_DIP, 48, dm);
+ mXPercent = getPropertyInt("ro.watermark.x",
+ TypedValue.COMPLEX_UNIT_PX, 50, dm);
+ mYPercent = getPropertyInt("ro.watermark.y",
+ TypedValue.COMPLEX_UNIT_PX, 99, dm);
+ int color = getPropertyInt("ro.watermark.color",
+ TypedValue.COMPLEX_UNIT_PX, 0x80ffffff, dm);
+ int shadowRadius = getPropertyInt("ro.watermark.shadow.radius",
+ TypedValue.COMPLEX_UNIT_PX, 5, dm);
+ int shadowDx = getPropertyInt("ro.watermark.shadow.dx",
+ TypedValue.COMPLEX_UNIT_PX, 0, dm);
+ int shadowDy = getPropertyInt("ro.watermark.shadow.dy",
+ TypedValue.COMPLEX_UNIT_PX, 0, dm);
+ int shadowColor = getPropertyInt("ro.watermark.shadow.color",
+ TypedValue.COMPLEX_UNIT_PX, 0xff000000, dm);
+
+ Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ paint.setTextSize(fontSize);
+ paint.setColor(color);
+ paint.setShadowLayer(shadowRadius, shadowDx, shadowDy, shadowColor);
+ paint.setTypeface(Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD));
+
+ FontMetricsInt fm = paint.getFontMetricsInt();
+ mHeight = fm.descent - fm.ascent + 20;
+ mWidth = (int)paint.measureText(text) + 20;
+
+ try {
+ mSurface = new Surface(session, 0,
+ "WatermarkSurface",
+ -1, mWidth, mHeight, PixelFormat.TRANSLUCENT, 0);
+ mSurface.setLayer(TYPE_LAYER_MULTIPLIER*100);
+ Rect dirty = new Rect(0, 0, mWidth, mHeight);
+ Canvas c = mSurface.lockCanvas(dirty);
+ c.drawText(text, 10, -fm.ascent+10, paint);
+ mSurface.unlockCanvasAndPost(c);
+ mSurface.show();
+ } catch (OutOfResourcesException e) {
+ }
+ }
+
+ void positionSurface(int dw, int dh) {
+ int availW = dw - mWidth;
+ int availH = dh - mHeight;
+ mSurface.setPosition((availW*mXPercent)/100,
+ (availH*mYPercent)/100);
+ }
+ }
+
+ void createWatermark() {
+ if (mWatermark != null) {
+ return;
+ }
+
+ String text = SystemProperties.get("ro.watermark.text");
+ if (text == null || text.length() <= 0) {
+ return;
+ }
+
+ mWatermark = new Watermark(mFxSession, text);
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission("android.permission.DUMP")
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index a32cd4c2b374..df930ad445e6 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -3578,6 +3578,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
// Tell anyone interested that we are done booting!
+ SystemProperties.set("sys.boot_completed", "1");
broadcastIntentLocked(null, null,
new Intent(Intent.ACTION_BOOT_COMPLETED, null),
null, null, 0, null, null,
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 0982b32719a4..ebe71ab777b6 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -213,7 +213,7 @@ public:
void setDisplayOrientation(int32_t displayId, int32_t orientation);
status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel,
- jweak inputChannelObjWeak);
+ jweak inputChannelObjWeak, bool monitor);
status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
void setInputWindows(JNIEnv* env, jobjectArray windowObjArray);
@@ -334,6 +334,7 @@ private:
bool mWindowsReady;
Vector<InputWindow> mWindows;
Vector<InputWindow*> mWallpaperWindows;
+ Vector<sp<InputChannel> > mMonitoringChannels;
// Focus tracking for keys, trackball, etc.
InputWindow* mFocusedWindow;
@@ -382,6 +383,10 @@ private:
static void addTarget(const InputWindow* window, int32_t targetFlags,
nsecs_t timeSpentWaitingForApplication, Vector<InputTarget>& outTargets);
+ void registerMonitoringChannel(const sp<InputChannel>& inputChannel);
+ void unregisterMonitoringChannel(const sp<InputChannel>& inputChannel);
+ void addMonitoringTargetsLd(Vector<InputTarget>& outTargets);
+
static inline JNIEnv* jniEnv() {
return AndroidRuntime::getJNIEnv();
}
@@ -492,7 +497,7 @@ void NativeInputManager::setDisplayOrientation(int32_t displayId, int32_t orient
}
status_t NativeInputManager::registerInputChannel(JNIEnv* env,
- const sp<InputChannel>& inputChannel, jobject inputChannelObj) {
+ const sp<InputChannel>& inputChannel, jobject inputChannelObj, bool monitor) {
jweak inputChannelObjWeak = env->NewWeakGlobalRef(inputChannelObj);
if (! inputChannelObjWeak) {
LOGE("Could not create weak reference for input channel.");
@@ -519,9 +524,14 @@ status_t NativeInputManager::registerInputChannel(JNIEnv* env,
status = mInputManager->registerInputChannel(inputChannel);
if (! status) {
+ // Success.
+ if (monitor) {
+ registerMonitoringChannel(inputChannel);
+ }
return OK;
}
+ // Failed!
{
AutoMutex _l(mInputChannelRegistryLock);
mInputChannelObjWeakByReceiveFd.removeItem(inputChannel->getReceivePipeFd());
@@ -552,6 +562,8 @@ status_t NativeInputManager::unregisterInputChannel(JNIEnv* env,
env->DeleteWeakGlobalRef(inputChannelObjWeak);
+ unregisterMonitoringChannel(inputChannel);
+
return mInputManager->unregisterInputChannel(inputChannel);
}
@@ -829,6 +841,8 @@ void NativeInputManager::notifyInputChannelBroken(const sp<InputChannel>& inputC
env->DeleteLocalRef(inputChannelObjLocal);
}
+
+ unregisterMonitoringChannel(inputChannel);
}
bool NativeInputManager::notifyInputChannelANR(const sp<InputChannel>& inputChannel,
@@ -1429,7 +1443,9 @@ int32_t NativeInputManager::waitForTouchedWindowLd(MotionEvent* motionEvent, uin
// If there is no currently touched window then fail.
if (! mTouchedWindow) {
- LOGW("Dropping event because there is no touched window to receive it.");
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("Dropping event because there is no touched window to receive it.");
+#endif
injectionResult = INPUT_EVENT_INJECTION_FAILED;
injectionPermission = INJECTION_PERMISSION_GRANTED;
break; // failed, exit wait loop
@@ -1587,6 +1603,8 @@ int32_t NativeInputManager::waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t
outTargets.clear();
return INPUT_EVENT_INJECTION_SUCCEEDED;
}
+
+ addMonitoringTargetsLd(outTargets);
}
pokeUserActivityIfNeeded(windowType, POWER_MANAGER_BUTTON_EVENT);
@@ -1631,6 +1649,8 @@ int32_t NativeInputManager::waitForNonTouchEventTargets(MotionEvent* motionEvent
}
windowType = focusedWindow->layoutParamsType;
+
+ addMonitoringTargetsLd(outTargets);
} // release lock
pokeUserActivityIfNeeded(windowType, POWER_MANAGER_BUTTON_EVENT);
@@ -1657,6 +1677,8 @@ int32_t NativeInputManager::waitForTouchEventTargets(MotionEvent* motionEvent,
}
windowType = touchedWindow->layoutParamsType;
+
+ addMonitoringTargetsLd(outTargets);
} // release lock
int32_t eventType;
@@ -1714,6 +1736,39 @@ void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType)
android_server_PowerManagerService_userActivity(eventTime, eventType);
}
+void NativeInputManager::registerMonitoringChannel(const sp<InputChannel>& inputChannel) {
+ { // acquire lock
+ AutoMutex _l(mDispatchLock);
+ mMonitoringChannels.push(inputChannel);
+ } // release lock
+}
+
+void NativeInputManager::unregisterMonitoringChannel(const sp<InputChannel>& inputChannel) {
+ { // acquire lock
+ AutoMutex _l(mDispatchLock);
+
+ for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+ if (mMonitoringChannels[i] == inputChannel) {
+ mMonitoringChannels.removeAt(i);
+ break;
+ }
+ }
+ } // release lock
+}
+
+void NativeInputManager::addMonitoringTargetsLd(Vector<InputTarget>& outTargets) {
+ for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+ outTargets.push();
+
+ InputTarget& target = outTargets.editTop();
+ target.inputChannel = mMonitoringChannels[i];
+ target.flags = 0;
+ target.timeout = -1;
+ target.xOffset = 0;
+ target.yOffset = 0;
+ }
+}
+
static void dumpMotionRange(String8& dump,
const char* name, const InputDeviceInfo::MotionRange* range) {
if (range) {
@@ -1805,6 +1860,11 @@ void NativeInputManager::dumpDispatchStateLd(String8& dump) {
mWindows[i].ownerPid, mWindows[i].ownerUid,
mWindows[i].dispatchingTimeout / 1000000.0);
}
+
+ for (size_t i = 0; i < mMonitoringChannels.size(); i++) {
+ dump.appendFormat(" monitoringChannel[%d]: '%s'\n",
+ i, mMonitoringChannels[i]->getName().string());
+ }
}
// ----------------------------------------------------------------------------
@@ -2012,7 +2072,7 @@ static void android_server_InputManager_handleInputChannelDisposed(JNIEnv* env,
}
static void android_server_InputManager_nativeRegisterInputChannel(JNIEnv* env, jclass clazz,
- jobject inputChannelObj) {
+ jobject inputChannelObj, jboolean monitor) {
if (checkInputManagerUnitialized(env)) {
return;
}
@@ -2026,15 +2086,17 @@ static void android_server_InputManager_nativeRegisterInputChannel(JNIEnv* env,
status_t status = gNativeInputManager->registerInputChannel(
- env, inputChannel, inputChannelObj);
+ env, inputChannel, inputChannelObj, monitor);
if (status) {
jniThrowRuntimeException(env, "Failed to register input channel. "
"Check logs for details.");
return;
}
- android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
- android_server_InputManager_handleInputChannelDisposed, NULL);
+ if (! monitor) {
+ android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
+ android_server_InputManager_handleInputChannelDisposed, NULL);
+ }
}
static void android_server_InputManager_nativeUnregisterInputChannel(JNIEnv* env, jclass clazz,
@@ -2149,7 +2211,7 @@ static JNINativeMethod gInputManagerMethods[] = {
(void*) android_server_InputManager_nativeGetSwitchState },
{ "nativeHasKeys", "(II[I[Z)Z",
(void*) android_server_InputManager_nativeHasKeys },
- { "nativeRegisterInputChannel", "(Landroid/view/InputChannel;)V",
+ { "nativeRegisterInputChannel", "(Landroid/view/InputChannel;Z)V",
(void*) android_server_InputManager_nativeRegisterInputChannel },
{ "nativeUnregisterInputChannel", "(Landroid/view/InputChannel;)V",
(void*) android_server_InputManager_nativeUnregisterInputChannel },
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index a14bfb569b8b..79772edec355 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -5,6 +5,7 @@ LOCAL_SRC_FILES:= \
clz.cpp.arm \
DisplayHardware/DisplayHardware.cpp \
DisplayHardware/DisplayHardwareBase.cpp \
+ DisplayHardware/HWComposer.cpp \
BlurFilter.cpp.arm \
GLExtensions.cpp \
Layer.cpp \
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
index 2eac0a80a08b..166c52804e10 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
@@ -36,11 +36,11 @@
#include "DisplayHardware/DisplayHardware.h"
-#include <hardware/copybit.h>
#include <hardware/overlay.h>
#include <hardware/gralloc.h>
#include "GLExtensions.h"
+#include "HWComposer.h"
using namespace android;
@@ -76,7 +76,7 @@ DisplayHardware::DisplayHardware(
const sp<SurfaceFlinger>& flinger,
uint32_t dpy)
: DisplayHardwareBase(flinger, dpy),
- mFlags(0)
+ mFlags(0), mHwc(0)
{
init(dpy);
}
@@ -262,6 +262,17 @@ void DisplayHardware::init(uint32_t dpy)
// Unbind the context from this thread
eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+
+
+ // initialize the H/W composer
+ mHwc = new HWComposer();
+ if (mHwc->initCheck() == NO_ERROR) {
+ mHwc->setFrameBuffer(mDisplay, mSurface);
+ }
+}
+
+HWComposer& DisplayHardware::getHwComposer() const {
+ return *mHwc;
}
/*
@@ -317,7 +328,12 @@ void DisplayHardware::flip(const Region& dirty) const
}
mPageFlipCount++;
- eglSwapBuffers(dpy, surface);
+
+ if (mHwc->initCheck() == NO_ERROR) {
+ mHwc->commit();
+ } else {
+ eglSwapBuffers(dpy, surface);
+ }
checkEGLErrors("eglSwapBuffers");
// for debugging
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.h b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
index 66bf5215d5d6..f2cfd2db579b 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
@@ -34,12 +34,11 @@
#include "DisplayHardware/DisplayHardwareBase.h"
struct overlay_control_device_t;
-struct framebuffer_device_t;
-struct copybit_image_t;
namespace android {
class FramebufferNativeWindow;
+class HWComposer;
class DisplayHardware : public DisplayHardwareBase
{
@@ -80,6 +79,9 @@ public:
uint32_t getPageFlipCount() const;
EGLDisplay getEGLDisplay() const { return mDisplay; }
overlay_control_device_t* getOverlayEngine() const { return mOverlayEngine; }
+
+ // Hardware Composer
+ HWComposer& getHwComposer() const;
status_t compositionComplete() const;
@@ -107,6 +109,8 @@ private:
GLint mMaxViewportDims;
GLint mMaxTextureSize;
+ HWComposer* mHwc;
+
sp<FramebufferNativeWindow> mNativeWindow;
overlay_control_device_t* mOverlayEngine;
};
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
new file mode 100644
index 000000000000..8ca880b09502
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+
+#include <hardware/hardware.h>
+
+#include <cutils/log.h>
+
+#include <EGL/egl.h>
+
+#include "HWComposer.h"
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+HWComposer::HWComposer()
+ : mModule(0), mHwc(0), mList(0),
+ mDpy(EGL_NO_DISPLAY), mSur(EGL_NO_SURFACE)
+{
+ int err = hw_get_module(HWC_HARDWARE_MODULE_ID, &mModule);
+ LOGW_IF(err, "%s module not found", HWC_HARDWARE_MODULE_ID);
+ if (err == 0) {
+ err = hwc_open(mModule, &mHwc);
+ LOGE_IF(err, "%s device failed to initialize (%s)",
+ HWC_HARDWARE_COMPOSER, strerror(-err));
+ }
+}
+
+HWComposer::~HWComposer() {
+ free(mList);
+ if (mHwc) {
+ hwc_close(mHwc);
+ }
+}
+
+status_t HWComposer::initCheck() const {
+ return mHwc ? NO_ERROR : NO_INIT;
+}
+
+void HWComposer::setFrameBuffer(EGLDisplay dpy, EGLSurface sur) {
+ mDpy = (hwc_display_t)dpy;
+ mSur = (hwc_surface_t)sur;
+}
+
+status_t HWComposer::createWorkList(size_t numLayers) {
+ if (mHwc && (!mList || mList->numHwLayers < numLayers)) {
+ free(mList);
+ size_t size = sizeof(hwc_layer_list) + numLayers*sizeof(hwc_layer_t);
+ mList = (hwc_layer_list_t*)malloc(size);
+ mList->flags = HWC_GEOMETRY_CHANGED;
+ mList->numHwLayers = numLayers;
+ }
+ return NO_ERROR;
+}
+
+status_t HWComposer::prepare() const {
+ int err = mHwc->prepare(mHwc, mList);
+ return (status_t)err;
+}
+
+status_t HWComposer::commit() const {
+ int err = mHwc->set(mHwc, mDpy, mSur, mList);
+ mList->flags &= ~HWC_GEOMETRY_CHANGED;
+ return (status_t)err;
+}
+
+HWComposer::iterator HWComposer::begin() {
+ return mList ? &(mList->hwLayers[0]) : NULL;
+}
+
+HWComposer::iterator HWComposer::end() {
+ return mList ? &(mList->hwLayers[mList->numHwLayers]) : NULL;
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
new file mode 100644
index 000000000000..729f23bbe0f5
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SF_HWCOMPOSER_H
+#define ANDROID_SF_HWCOMPOSER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <EGL/egl.h>
+
+#include <hardware/hwcomposer.h>
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+class HWComposer
+{
+public:
+
+ HWComposer();
+ ~HWComposer();
+
+ status_t initCheck() const;
+
+ // tells the HAL what the framebuffer is
+ void setFrameBuffer(EGLDisplay dpy, EGLSurface sur);
+
+ // create a work list for numLayers layer
+ status_t createWorkList(size_t numLayers);
+
+ // Asks the HAL what it can do
+ status_t prepare() const;
+
+ // commits the list
+ status_t commit() const;
+
+
+ typedef hwc_layer_t const * const_iterator;
+ typedef hwc_layer_t* iterator;
+
+ iterator begin();
+ iterator end();
+
+private:
+ hw_module_t const* mModule;
+ hwc_composer_device_t* mHwc;
+ hwc_layer_list_t* mList;
+ hwc_display_t mDpy;
+ hwc_surface_t mSur;
+};
+
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_SF_HWCOMPOSER_H
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 629d993ca007..3720e166992b 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -35,6 +35,7 @@
#include "Layer.h"
#include "SurfaceFlinger.h"
#include "DisplayHardware/DisplayHardware.h"
+#include "DisplayHardware/HWComposer.h"
#define DEBUG_RESIZE 0
@@ -177,6 +178,62 @@ status_t Layer::setBuffers( uint32_t w, uint32_t h,
return NO_ERROR;
}
+void Layer::setGeometry(hwc_layer_t* hwcl)
+{
+ hwcl->compositionType = HWC_FRAMEBUFFER;
+ hwcl->hints = 0;
+ hwcl->flags = 0;
+ hwcl->transform = 0;
+ hwcl->blending = HWC_BLENDING_NONE;
+
+ // we can't do alpha-fade with the hwc HAL
+ const State& s(drawingState());
+ if (s.alpha < 0xFF) {
+ hwcl->flags = HWC_SKIP_LAYER;
+ return;
+ }
+
+ // we can only handle simple transformation
+ if (mOrientation & Transform::ROT_INVALID) {
+ hwcl->flags = HWC_SKIP_LAYER;
+ return;
+ }
+
+ hwcl->transform = mOrientation;
+
+ if (needsBlending()) {
+ hwcl->blending = mPremultipliedAlpha ?
+ HWC_BLENDING_PREMULT : HWC_BLENDING_COVERAGE;
+ }
+
+ hwcl->displayFrame.left = mTransformedBounds.left;
+ hwcl->displayFrame.top = mTransformedBounds.top;
+ hwcl->displayFrame.right = mTransformedBounds.right;
+ hwcl->displayFrame.bottom = mTransformedBounds.bottom;
+
+ hwcl->visibleRegionScreen.rects =
+ reinterpret_cast<hwc_rect_t const *>(
+ visibleRegionScreen.getArray(
+ &hwcl->visibleRegionScreen.numRects));
+}
+
+void Layer::setPerFrameData(hwc_layer_t* hwcl) {
+ sp<GraphicBuffer> buffer(mBufferManager.getActiveBuffer());
+ if (buffer == NULL) {
+ // this situation can happen if we ran out of memory for instance.
+ // not much we can do. continue to use whatever texture was bound
+ // to this context.
+ hwcl->handle = NULL;
+ return;
+ }
+ hwcl->handle = const_cast<native_handle_t*>(buffer->handle);
+ // TODO: set the crop value properly
+ hwcl->sourceCrop.left = 0;
+ hwcl->sourceCrop.top = 0;
+ hwcl->sourceCrop.right = buffer->width;
+ hwcl->sourceCrop.bottom = buffer->height;
+}
+
void Layer::reloadTexture(const Region& dirty)
{
sp<GraphicBuffer> buffer(mBufferManager.getActiveBuffer());
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index e1d283beddb7..188da6a784df 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -68,6 +68,8 @@ public:
bool isFixedSize() const;
// LayerBase interface
+ virtual void setGeometry(hwc_layer_t* hwcl);
+ virtual void setPerFrameData(hwc_layer_t* hwcl);
virtual void onDraw(const Region& clip) const;
virtual uint32_t doTransaction(uint32_t transactionFlags);
virtual void lockPageFlip(bool& recomputeVisibleRegions);
diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
index d5aa53f9f178..043d54d96210 100644
--- a/services/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -39,8 +39,11 @@ namespace android {
// ---------------------------------------------------------------------------
+int32_t LayerBase::sSequence = 1;
+
LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display)
: dpy(display), contentDirty(false),
+ sequence(uint32_t(android_atomic_inc(&sSequence))),
mFlinger(flinger),
mNeedsFiltering(false),
mOrientation(0),
@@ -304,22 +307,17 @@ void LayerBase::drawRegion(const Region& reg) const
}
}
-void LayerBase::draw(const Region& inClip) const
-{
- // invalidate the region we'll update
- Region clip(inClip); // copy-on-write, so no-op most of the time
-
- // Remove the transparent area from the clipping region
- const State& s = drawingState();
- if (LIKELY(!s.transparentRegion.isEmpty())) {
- clip.subtract(transparentRegionScreen);
- if (clip.isEmpty()) {
- // usually this won't happen because this should be taken care of
- // by SurfaceFlinger::computeVisibleRegions()
- return;
- }
- }
+void LayerBase::setGeometry(hwc_layer_t* hwcl) {
+ hwcl->flags |= HWC_SKIP_LAYER;
+}
+void LayerBase::setPerFrameData(hwc_layer_t* hwcl) {
+ hwcl->compositionType = HWC_FRAMEBUFFER;
+ hwcl->handle = NULL;
+}
+
+void LayerBase::draw(const Region& clip) const
+{
// reset GL state
glEnable(GL_SCISSOR_TEST);
diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h
index 4288cf79469b..dd1cd05f1b93 100644
--- a/services/surfaceflinger/LayerBase.h
+++ b/services/surfaceflinger/LayerBase.h
@@ -35,6 +35,8 @@
#include <pixelflinger/pixelflinger.h>
+#include <hardware/hwcomposer.h>
+
#include "Transform.h"
namespace android {
@@ -53,6 +55,8 @@ class Texture;
class LayerBase : public RefBase
{
+ static int32_t sSequence;
+
public:
LayerBase(SurfaceFlinger* flinger, DisplayID display);
@@ -61,6 +65,7 @@ public:
Region visibleRegionScreen;
Region transparentRegionScreen;
Region coveredRegionScreen;
+ int32_t sequence;
struct State {
uint32_t w;
@@ -105,6 +110,10 @@ public:
virtual const char* getTypeId() const { return "LayerBase"; }
+ virtual void setGeometry(hwc_layer_t* hwcl);
+
+ virtual void setPerFrameData(hwc_layer_t* hwcl);
+
/**
* draw - performs some global clipping optimizations
* and calls onDraw().
@@ -210,12 +219,6 @@ public:
inline const State& currentState() const { return mCurrentState; }
inline State& currentState() { return mCurrentState; }
- static int compareCurrentStateZ(
- sp<LayerBase> const * layerA,
- sp<LayerBase> const * layerB) {
- return layerA[0]->currentState().z - layerB[0]->currentState().z;
- }
-
int32_t getOrientation() const { return mOrientation; }
int tx() const { return mLeft; }
int ty() const { return mTop; }
diff --git a/services/surfaceflinger/LayerBuffer.cpp b/services/surfaceflinger/LayerBuffer.cpp
index 5f836366c431..e8b2ebf16745 100644
--- a/services/surfaceflinger/LayerBuffer.cpp
+++ b/services/surfaceflinger/LayerBuffer.cpp
@@ -495,7 +495,7 @@ status_t LayerBuffer::BufferSource::initTempBuffer() const
const ISurface::BufferHeap& buffers(mBufferHeap);
uint32_t w = mLayer.mTransformedBounds.width();
uint32_t h = mLayer.mTransformedBounds.height();
- if (buffers.w * h != buffers.h * w) {
+ if (mLayer.getOrientation() & (Transform::ROT_90 | Transform::ROT_270)) {
int t = w; w = h; h = t;
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 3167c4cb7ed1..47bb4c1b1111 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -52,6 +52,7 @@
#include "SurfaceFlinger.h"
#include "DisplayHardware/DisplayHardware.h"
+#include "DisplayHardware/HWComposer.h"
/* ideally AID_GRAPHICS would be in a semi-public header
* or there would be a way to map a user/group name to its id
@@ -65,95 +66,6 @@
namespace android {
// ---------------------------------------------------------------------------
-SurfaceFlinger::LayerVector::LayerVector(const SurfaceFlinger::LayerVector& rhs)
- : lookup(rhs.lookup), layers(rhs.layers)
-{
-}
-
-ssize_t SurfaceFlinger::LayerVector::indexOf(
- const sp<LayerBase>& key, size_t guess) const
-{
- if (guess<size() && lookup.keyAt(guess) == key)
- return guess;
- const ssize_t i = lookup.indexOfKey(key);
- if (i>=0) {
- const size_t idx = lookup.valueAt(i);
- LOGE_IF(layers[idx]!=key,
- "LayerVector[%p]: layers[%d]=%p, key=%p",
- this, int(idx), layers[idx].get(), key.get());
- return idx;
- }
- return i;
-}
-
-ssize_t SurfaceFlinger::LayerVector::add(
- const sp<LayerBase>& layer,
- Vector< sp<LayerBase> >::compar_t cmp)
-{
- size_t count = layers.size();
- ssize_t l = 0;
- ssize_t h = count-1;
- ssize_t mid;
- sp<LayerBase> const* a = layers.array();
- while (l <= h) {
- mid = l + (h - l)/2;
- const int c = cmp(a+mid, &layer);
- if (c == 0) { l = mid; break; }
- else if (c<0) { l = mid+1; }
- else { h = mid-1; }
- }
- size_t order = l;
- while (order<count && !cmp(&layer, a+order)) {
- order++;
- }
- count = lookup.size();
- for (size_t i=0 ; i<count ; i++) {
- if (lookup.valueAt(i) >= order) {
- lookup.editValueAt(i)++;
- }
- }
- layers.insertAt(layer, order);
- lookup.add(layer, order);
- return order;
-}
-
-ssize_t SurfaceFlinger::LayerVector::remove(const sp<LayerBase>& layer)
-{
- const ssize_t keyIndex = lookup.indexOfKey(layer);
- if (keyIndex >= 0) {
- const size_t index = lookup.valueAt(keyIndex);
- LOGE_IF(layers[index]!=layer,
- "LayerVector[%p]: layers[%u]=%p, layer=%p",
- this, int(index), layers[index].get(), layer.get());
- layers.removeItemsAt(index);
- lookup.removeItemsAt(keyIndex);
- const size_t count = lookup.size();
- for (size_t i=0 ; i<count ; i++) {
- if (lookup.valueAt(i) >= size_t(index)) {
- lookup.editValueAt(i)--;
- }
- }
- return index;
- }
- return NAME_NOT_FOUND;
-}
-
-ssize_t SurfaceFlinger::LayerVector::reorder(
- const sp<LayerBase>& layer,
- Vector< sp<LayerBase> >::compar_t cmp)
-{
- // XXX: it's a little lame. but oh well...
- ssize_t err = remove(layer);
- if (err >=0)
- err = add(layer, cmp);
- return err;
-}
-
-// ---------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#endif
-
SurfaceFlinger::SurfaceFlinger()
: BnSurfaceComposer(), Thread(false),
mTransactionFlags(0),
@@ -165,6 +77,7 @@ SurfaceFlinger::SurfaceFlinger()
mAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER"),
mDump("android.permission.DUMP"),
mVisibleRegionsDirty(false),
+ mHwWorkListDirty(false),
mDeferReleaseConsole(false),
mFreezeDisplay(false),
mFreezeCount(0),
@@ -457,6 +370,11 @@ bool SurfaceFlinger::threadLoop()
// post surfaces (if needed)
handlePageFlip();
+ if (UNLIKELY(mHwWorkListDirty)) {
+ // build the h/w work list
+ handleWorkList();
+ }
+
const DisplayHardware& hw(graphicPlane(0).displayHardware());
if (LIKELY(hw.canDraw() && !isFrozen())) {
// repaint the framebuffer (if needed)
@@ -521,6 +439,10 @@ void SurfaceFlinger::handleTransaction(uint32_t transactionFlags)
{
Vector< sp<LayerBase> > ditchedLayers;
+ /*
+ * Perform and commit the transaction
+ */
+
{ // scope for the lock
Mutex::Autolock _l(mStateLock);
const nsecs_t now = systemTime();
@@ -528,9 +450,15 @@ void SurfaceFlinger::handleTransaction(uint32_t transactionFlags)
handleTransactionLocked(transactionFlags, ditchedLayers);
mLastTransactionTime = systemTime() - now;
mDebugInTransaction = 0;
+ mHwWorkListDirty = true;
+ // here the transaction has been committed
}
- // do this without lock held
+ /*
+ * Clean-up all layers that went away
+ * (do this without the lock held)
+ */
+
const size_t count = ditchedLayers.size();
for (size_t i=0 ; i<count ; i++) {
if (ditchedLayers[i] != 0) {
@@ -764,8 +692,8 @@ void SurfaceFlinger::commitTransaction()
void SurfaceFlinger::handlePageFlip()
{
bool visibleRegions = mVisibleRegionsDirty;
- LayerVector& currentLayers = const_cast<LayerVector&>(
- mDrawingState.layersSortedByZ);
+ LayerVector& currentLayers(
+ const_cast<LayerVector&>(mDrawingState.layersSortedByZ));
visibleRegions |= lockPageFlip(currentLayers);
const DisplayHardware& hw = graphicPlane(0).displayHardware();
@@ -773,8 +701,22 @@ void SurfaceFlinger::handlePageFlip()
if (visibleRegions) {
Region opaqueRegion;
computeVisibleRegions(currentLayers, mDirtyRegion, opaqueRegion);
+
+ /*
+ * rebuild the visible layer list
+ */
+ mVisibleLayersSortedByZ.clear();
+ const LayerVector& currentLayers(mDrawingState.layersSortedByZ);
+ size_t count = currentLayers.size();
+ mVisibleLayersSortedByZ.setCapacity(count);
+ for (size_t i=0 ; i<count ; i++) {
+ if (!currentLayers[i]->visibleRegionScreen.isEmpty())
+ mVisibleLayersSortedByZ.add(currentLayers[i]);
+ }
+
mWormholeRegion = screenRegion.subtract(opaqueRegion);
mVisibleRegionsDirty = false;
+ mHwWorkListDirty = true;
}
unlockPageFlip(currentLayers);
@@ -805,6 +747,21 @@ void SurfaceFlinger::unlockPageFlip(const LayerVector& currentLayers)
}
}
+void SurfaceFlinger::handleWorkList()
+{
+ mHwWorkListDirty = false;
+ HWComposer& hwc(graphicPlane(0).displayHardware().getHwComposer());
+ if (hwc.initCheck() == NO_ERROR) {
+ const Vector< sp<LayerBase> >& currentLayers(mVisibleLayersSortedByZ);
+ const size_t count = currentLayers.size();
+ hwc.createWorkList(count);
+ HWComposer::iterator cur(hwc.begin());
+ HWComposer::iterator last(hwc.end());
+ for (size_t i=0 ; (i<count) && (cur!=last) ; ++i, ++cur) {
+ currentLayers[i]->setGeometry(cur);
+ }
+ }
+}
void SurfaceFlinger::handleRepaint()
{
@@ -869,18 +826,61 @@ void SurfaceFlinger::composeSurfaces(const Region& dirty)
// draw something...
drawWormhole();
}
- const SurfaceFlinger& flinger(*this);
- const LayerVector& drawingLayers(mDrawingState.layersSortedByZ);
- const size_t count = drawingLayers.size();
- sp<LayerBase> const* const layers = drawingLayers.array();
+
+ status_t err = NO_ERROR;
+ const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
+ const size_t count = layers.size();
+
+ const DisplayHardware& hw(graphicPlane(0).displayHardware());
+ HWComposer& hwc(hw.getHwComposer());
+ HWComposer::iterator cur(hwc.begin());
+ HWComposer::iterator last(hwc.end());
+
+ // update the per-frame h/w composer data for each layer
+ if (cur != last) {
+ for (size_t i=0 ; i<count && cur!=last ; ++i, ++cur) {
+ layers[i]->setPerFrameData(cur);
+ }
+ err = hwc.prepare();
+ LOGE_IF(err, "HWComposer::prepare failed (%s)", strerror(-err));
+ }
+
+ // and then, render the layers targeted at the framebuffer
+ Region transparent(hw.bounds());
for (size_t i=0 ; i<count ; ++i) {
- const sp<LayerBase>& layer = layers[i];
- const Region& visibleRegion(layer->visibleRegionScreen);
- if (!visibleRegion.isEmpty()) {
- const Region clip(dirty.intersect(visibleRegion));
- if (!clip.isEmpty()) {
- layer->draw(clip);
+
+ // see if we need to skip this layer
+ if (!err && cur != last) {
+ if (!((cur->compositionType == HWC_FRAMEBUFFER) ||
+ (cur->flags & HWC_SKIP_LAYER))) {
+ ++cur;
+ continue;
}
+ ++cur;
+ }
+
+ // draw the layer into the framebuffer
+ const sp<LayerBase>& layer(layers[i]);
+ transparent.subtractSelf(layer->visibleRegionScreen);
+ const Region clip(dirty.intersect(layer->visibleRegionScreen));
+ if (!clip.isEmpty()) {
+ layer->draw(clip);
+ }
+ }
+
+ // finally clear everything we didn't draw as a result of calling
+ // prepare (this leaves the FB transparent).
+ transparent.andSelf(dirty);
+ if (!transparent.isEmpty()) {
+ glClearColor(0,0,0,0);
+ Region::const_iterator it = transparent.begin();
+ Region::const_iterator const end = transparent.end();
+ const int32_t height = hw.getHeight();
+ while (it != end) {
+ const Rect& r(*it++);
+ const GLint sy = height - (r.top + r.height());
+ glScissor(r.left, sy, r.width(), r.height());
+ glClear(GL_COLOR_BUFFER_BIT);
}
}
}
@@ -1029,8 +1029,7 @@ status_t SurfaceFlinger::addLayer(const sp<LayerBase>& layer)
status_t SurfaceFlinger::addLayer_l(const sp<LayerBase>& layer)
{
- ssize_t i = mCurrentState.layersSortedByZ.add(
- layer, &LayerBase::compareCurrentStateZ);
+ ssize_t i = mCurrentState.layersSortedByZ.add(layer);
return (i < 0) ? status_t(i) : status_t(NO_ERROR);
}
@@ -1372,9 +1371,10 @@ status_t SurfaceFlinger::setClientState(
flags |= eTraversalNeeded;
}
if (what & eLayerChanged) {
+ ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
if (layer->setLayer(s.z)) {
- mCurrentState.layersSortedByZ.reorder(
- layer, &Layer::compareCurrentStateZ);
+ mCurrentState.layersSortedByZ.removeAt(idx);
+ mCurrentState.layersSortedByZ.add(layer);
// we need traversal (state changed)
// AND transaction (list changed)
flags |= eTransactionNeeded|eTraversalNeeded;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 8821e5c071e2..8e286e505d33 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -40,9 +40,6 @@
#include "MessageQueue.h"
-struct copybit_device_t;
-struct overlay_device_t;
-
namespace android {
// ---------------------------------------------------------------------------
@@ -246,21 +243,19 @@ private:
status_t setClientState(const sp<Client>& client,
int32_t count, const layer_state_t* states);
-
- class LayerVector {
+ class LayerVector : public SortedVector< sp<LayerBase> > {
public:
- inline LayerVector() { }
- LayerVector(const LayerVector&);
- inline size_t size() const { return layers.size(); }
- inline sp<LayerBase> const* array() const { return layers.array(); }
- ssize_t add(const sp<LayerBase>&, Vector< sp<LayerBase> >::compar_t);
- ssize_t remove(const sp<LayerBase>&);
- ssize_t reorder(const sp<LayerBase>&, Vector< sp<LayerBase> >::compar_t);
- ssize_t indexOf(const sp<LayerBase>& key, size_t guess=0) const;
- inline sp<LayerBase> operator [] (size_t i) const { return layers[i]; }
- private:
- KeyedVector< sp<LayerBase> , size_t> lookup;
- Vector< sp<LayerBase> > layers;
+ LayerVector() { }
+ LayerVector(const LayerVector& rhs) : SortedVector< sp<LayerBase> >(rhs) { }
+ virtual int do_compare(const void* lhs, const void* rhs) const {
+ const sp<LayerBase>& l(*reinterpret_cast<const sp<LayerBase>*>(lhs));
+ const sp<LayerBase>& r(*reinterpret_cast<const sp<LayerBase>*>(rhs));
+ // sort layers by Z order
+ uint32_t lz = l->currentState().z;
+ uint32_t rz = r->currentState().z;
+ // then by sequence, so we get a stable ordering
+ return (lz != rz) ? (lz - rz) : (l->sequence - r->sequence);
+ }
};
struct State {
@@ -301,6 +296,7 @@ private:
void handlePageFlip();
bool lockPageFlip(const LayerVector& currentLayers);
void unlockPageFlip(const LayerVector& currentLayers);
+ void handleWorkList();
void handleRepaint();
void postFramebuffer();
void composeSurfaces(const Region& dirty);
@@ -375,10 +371,13 @@ private:
Region mInvalidRegion;
Region mWormholeRegion;
bool mVisibleRegionsDirty;
+ bool mHwWorkListDirty;
bool mDeferReleaseConsole;
bool mFreezeDisplay;
int32_t mFreezeCount;
nsecs_t mFreezeDisplayTime;
+ Vector< sp<LayerBase> > mVisibleLayersSortedByZ;
+
// don't use a lock for these, we don't care
int mDebugRegion;
diff --git a/telephony/java/com/android/internal/telephony/CallManager.java b/telephony/java/com/android/internal/telephony/CallManager.java
index 1381a2da18b0..2934e6af8258 100644
--- a/telephony/java/com/android/internal/telephony/CallManager.java
+++ b/telephony/java/com/android/internal/telephony/CallManager.java
@@ -24,6 +24,7 @@ import android.os.RegistrantList;
import android.telephony.PhoneStateListener;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -163,6 +164,14 @@ public final class CallManager {
}
/**
+ * Returns all the registered phone objects.
+ * @return all the registered phone objects.
+ */
+ public List<Phone> getAllPhones() {
+ return Collections.unmodifiableList(mPhones);
+ }
+
+ /**
* Get current coarse-grained voice call state.
* If the Call Manager has an active call and call waiting occurs,
* then the phone state is RINGING not OFFHOOK
@@ -206,7 +215,7 @@ public final class CallManager {
* @param phone
*/
public void unregisterPhone(Phone phone) {
- if (phone != null && !mPhones.contains(phone)) {
+ if (phone != null && mPhones.contains(phone)) {
mPhones.remove(phone);
mRingingCalls.remove(phone.getRingingCall());
mBackgroundCalls.remove(phone.getBackgroundCall());
diff --git a/telephony/java/com/android/internal/telephony/PhoneFactory.java b/telephony/java/com/android/internal/telephony/PhoneFactory.java
index 803b73685587..2e391cb96cb2 100644
--- a/telephony/java/com/android/internal/telephony/PhoneFactory.java
+++ b/telephony/java/com/android/internal/telephony/PhoneFactory.java
@@ -24,6 +24,8 @@ import android.util.Log;
import com.android.internal.telephony.cdma.CDMAPhone;
import com.android.internal.telephony.gsm.GSMPhone;
+import com.android.internal.telephony.sip.SipPhone;
+import com.android.internal.telephony.sip.SipPhoneFactory;
/**
* {@hide}
@@ -175,4 +177,13 @@ public class PhoneFactory {
return phone;
}
}
+
+ /**
+ * Makes a {@link SipPhone} object.
+ * @param sipUri the local SIP URI the phone runs on
+ * @return the {@code SipPhone} object or null if the SIP URI is not valid
+ */
+ public static SipPhone makeSipPhone(String sipUri) {
+ return SipPhoneFactory.makePhone(sipUri, sContext, sPhoneNotifier);
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/cat/AppInterface.java b/telephony/java/com/android/internal/telephony/cat/AppInterface.java
index 0ba3e11cabab..2eb6ccb5ee2e 100644
--- a/telephony/java/com/android/internal/telephony/cat/AppInterface.java
+++ b/telephony/java/com/android/internal/telephony/cat/AppInterface.java
@@ -58,7 +58,8 @@ public interface AppInterface {
SET_UP_EVENT_LIST(0x05),
SET_UP_IDLE_MODE_TEXT(0x28),
SET_UP_MENU(0x25),
- SET_UP_CALL(0x10);
+ SET_UP_CALL(0x10),
+ PROVIDE_LOCAL_INFORMATION(0x26);
private int mValue;
diff --git a/telephony/java/com/android/internal/telephony/cat/CatService.java b/telephony/java/com/android/internal/telephony/cat/CatService.java
index b916713f6025..1e23e34a3312 100644
--- a/telephony/java/com/android/internal/telephony/cat/CatService.java
+++ b/telephony/java/com/android/internal/telephony/cat/CatService.java
@@ -22,6 +22,7 @@ import android.os.AsyncResult;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
+import android.os.SystemProperties;
import com.android.internal.telephony.IccUtils;
import com.android.internal.telephony.CommandsInterface;
@@ -245,48 +246,46 @@ public class CatService extends Handler implements AppInterface {
CatCmdMessage cmdMsg = new CatCmdMessage(cmdParams);
switch (cmdParams.getCommandType()) {
- case SET_UP_MENU:
- if (removeMenu(cmdMsg.getMenu())) {
- mMenuCmd = null;
- } else {
- mMenuCmd = cmdMsg;
- }
- sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0,
- null);
- break;
- case DISPLAY_TEXT:
- // when application is not required to respond, send an immediate
- // response.
- if (!cmdMsg.geTextMessage().responseNeeded) {
- sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false,
- 0, null);
- }
- break;
- case REFRESH:
- // ME side only handles refresh commands which meant to remove IDLE
- // MODE TEXT.
- cmdParams.cmdDet.typeOfCommand = CommandType.SET_UP_IDLE_MODE_TEXT
- .value();
- break;
- case SET_UP_IDLE_MODE_TEXT:
- sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false,
- 0, null);
- break;
- case LAUNCH_BROWSER:
- case SELECT_ITEM:
- case GET_INPUT:
- case GET_INKEY:
- case SEND_DTMF:
- case SEND_SMS:
- case SEND_SS:
- case SEND_USSD:
- case PLAY_TONE:
- case SET_UP_CALL:
- // nothing to do on telephony!
- break;
- default:
- CatLog.d(this, "Unsupported command");
- return;
+ case SET_UP_MENU:
+ if (removeMenu(cmdMsg.getMenu())) {
+ mMenuCmd = null;
+ } else {
+ mMenuCmd = cmdMsg;
+ }
+ sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
+ break;
+ case DISPLAY_TEXT:
+ // when application is not required to respond, send an immediate response.
+ if (!cmdMsg.geTextMessage().responseNeeded) {
+ sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
+ }
+ break;
+ case REFRESH:
+ // ME side only handles refresh commands which meant to remove IDLE
+ // MODE TEXT.
+ cmdParams.cmdDet.typeOfCommand = CommandType.SET_UP_IDLE_MODE_TEXT.value();
+ break;
+ case SET_UP_IDLE_MODE_TEXT:
+ sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
+ break;
+ case PROVIDE_LOCAL_INFORMATION:
+ sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
+ return;
+ case LAUNCH_BROWSER:
+ case SELECT_ITEM:
+ case GET_INPUT:
+ case GET_INKEY:
+ case SEND_DTMF:
+ case SEND_SMS:
+ case SEND_SS:
+ case SEND_USSD:
+ case PLAY_TONE:
+ case SET_UP_CALL:
+ // nothing to do on telephony!
+ break;
+ default:
+ CatLog.d(this, "Unsupported command");
+ return;
}
mCurrntCmd = cmdMsg;
Intent intent = new Intent(AppInterface.CAT_CMD_ACTION);
@@ -315,6 +314,11 @@ public class CatService extends Handler implements AppInterface {
}
ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ Input cmdInput = null;
+ if (mCurrntCmd != null) {
+ cmdInput = mCurrntCmd.geInput();
+ }
+
// command details
int tag = ComprehensionTlvTag.COMMAND_DETAILS.value();
if (cmdDet.compRequired) {
@@ -327,7 +331,13 @@ public class CatService extends Handler implements AppInterface {
buf.write(cmdDet.commandQualifier);
// device identities
- tag = 0x80 | ComprehensionTlvTag.DEVICE_IDENTITIES.value();
+ // According to TS102.223/TS31.111 section 6.8 Structure of
+ // TERMINAL RESPONSE, "For all SIMPLE-TLV objects with Min=N,
+ // the ME should set the CR(comprehension required) flag to
+ // comprehension not required.(CR=0)"
+ // Since DEVICE_IDENTITIES and DURATION TLVs have Min=N,
+ // the CR flag is not set.
+ tag = ComprehensionTlvTag.DEVICE_IDENTITIES.value();
buf.write(tag);
buf.write(0x02); // length
buf.write(DEV_ID_TERMINAL); // source device id
@@ -348,6 +358,8 @@ public class CatService extends Handler implements AppInterface {
// Fill optional data for each corresponding command
if (resp != null) {
resp.format(buf);
+ } else {
+ encodeOptionalTags(cmdDet, resultCode, cmdInput, buf);
}
byte[] rawData = buf.toByteArray();
@@ -359,6 +371,52 @@ public class CatService extends Handler implements AppInterface {
mCmdIf.sendTerminalResponse(hexString, null);
}
+ private void encodeOptionalTags(CommandDetails cmdDet,
+ ResultCode resultCode, Input cmdInput, ByteArrayOutputStream buf) {
+ switch (AppInterface.CommandType.fromInt(cmdDet.typeOfCommand)) {
+ case GET_INKEY:
+ // ETSI TS 102 384,27.22.4.2.8.4.2.
+ // If it is a response for GET_INKEY command and the response timeout
+ // occured, then add DURATION TLV for variable timeout case.
+ if ((resultCode.value() == ResultCode.NO_RESPONSE_FROM_USER.value()) &&
+ (cmdInput != null) && (cmdInput.duration != null)) {
+ getInKeyResponse(buf, cmdInput);
+ }
+ break;
+ case PROVIDE_LOCAL_INFORMATION:
+ if ((cmdDet.commandQualifier == CommandParamsFactory.LANGUAGE_SETTING) &&
+ (resultCode.value() == ResultCode.OK.value())) {
+ getPliResponse(buf);
+ }
+ break;
+ default:
+ CatLog.d(this, "encodeOptionalTags() Unsupported Cmd:" + cmdDet.typeOfCommand);
+ break;
+ }
+ }
+
+ private void getInKeyResponse(ByteArrayOutputStream buf, Input cmdInput) {
+ int tag = ComprehensionTlvTag.DURATION.value();
+
+ buf.write(tag);
+ buf.write(0x02); // length
+ buf.write(cmdInput.duration.timeUnit.SECOND.value()); // Time (Unit,Seconds)
+ buf.write(cmdInput.duration.timeInterval); // Time Duration
+ }
+
+ private void getPliResponse(ByteArrayOutputStream buf) {
+
+ // Locale Language Setting
+ String lang = SystemProperties.get("persist.sys.language");
+
+ if (lang != null) {
+ // tag
+ int tag = ComprehensionTlvTag.LANGUAGE.value();
+ buf.write(tag);
+ ResponseData.writeLength(buf, lang.length());
+ buf.write(lang.getBytes(), 0, lang.length());
+ }
+ }
private void sendMenuSelection(int menuId, boolean helpRequired) {
diff --git a/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java b/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java
index edb2dc899d0f..12204a009024 100644
--- a/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java
+++ b/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java
@@ -52,6 +52,9 @@ class CommandParamsFactory extends Handler {
static final int REFRESH_NAA_INIT = 0x03;
static final int REFRESH_UICC_RESET = 0x04;
+ // Command Qualifier values for PLI command
+ static final int LANGUAGE_SETTING = 0x04;
+
static synchronized CommandParamsFactory getInstance(RilMessageDecoder caller,
IccFileHandler fh) {
if (sInstance != null) {
@@ -112,7 +115,10 @@ class CommandParamsFactory extends Handler {
AppInterface.CommandType cmdType = AppInterface.CommandType
.fromInt(cmdDet.typeOfCommand);
if (cmdType == null) {
- sendCmdParams(ResultCode.CMD_TYPE_NOT_UNDERSTOOD);
+ // This PROACTIVE COMMAND is presently not handled. Hence set
+ // result code as BEYOND_TERMINAL_CAPABILITY in TR.
+ mCmdParams = new CommandParams(cmdDet);
+ sendCmdParams(ResultCode.BEYOND_TERMINAL_CAPABILITY);
return;
}
@@ -155,10 +161,13 @@ class CommandParamsFactory extends Handler {
case PLAY_TONE:
cmdPending = processPlayTone(cmdDet, ctlvs);
break;
+ case PROVIDE_LOCAL_INFORMATION:
+ cmdPending = processProvideLocalInfo(cmdDet, ctlvs);
+ break;
default:
// unsupported proactive commands
mCmdParams = new CommandParams(cmdDet);
- sendCmdParams(ResultCode.CMD_TYPE_NOT_UNDERSTOOD);
+ sendCmdParams(ResultCode.BEYOND_TERMINAL_CAPABILITY);
return;
}
} catch (ResultException e) {
@@ -380,6 +389,12 @@ class CommandParamsFactory extends Handler {
iconId = ValueParser.retrieveIconId(ctlv);
}
+ // parse duration
+ ctlv = searchForTag(ComprehensionTlvTag.DURATION, ctlvs);
+ if (ctlv != null) {
+ input.duration = ValueParser.retrieveDuration(ctlv);
+ }
+
input.minLen = 1;
input.maxLen = 1;
@@ -863,4 +878,20 @@ class CommandParamsFactory extends Handler {
}
return false;
}
+
+ private boolean processProvideLocalInfo(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)
+ throws ResultException {
+ CatLog.d(this, "process ProvideLocalInfo");
+ switch (cmdDet.commandQualifier) {
+ case LANGUAGE_SETTING:
+ CatLog.d(this, "PLI [LANGUAGE_SETTING]");
+ mCmdParams = new CommandParams(cmdDet);
+ break;
+ default:
+ CatLog.d(this, "PLI[" + cmdDet.commandQualifier + "] Command Not Supported");
+ mCmdParams = new CommandParams(cmdDet);
+ throw new ResultException(ResultCode.BEYOND_TERMINAL_CAPABILITY);
+ }
+ return false;
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/cat/Input.java b/telephony/java/com/android/internal/telephony/cat/Input.java
index 8bcaab974b95..13a5ad46cb32 100644
--- a/telephony/java/com/android/internal/telephony/cat/Input.java
+++ b/telephony/java/com/android/internal/telephony/cat/Input.java
@@ -36,6 +36,7 @@ public class Input implements Parcelable {
public boolean echo;
public boolean yesNo;
public boolean helpAvailable;
+ public Duration duration;
Input() {
text = "";
@@ -49,6 +50,7 @@ public class Input implements Parcelable {
echo = false;
yesNo = false;
helpAvailable = false;
+ duration = null;
}
private Input(Parcel in) {
@@ -63,6 +65,7 @@ public class Input implements Parcelable {
echo = in.readInt() == 1 ? true : false;
yesNo = in.readInt() == 1 ? true : false;
helpAvailable = in.readInt() == 1 ? true : false;
+ duration = in.readParcelable(null);
}
public int describeContents() {
@@ -81,6 +84,7 @@ public class Input implements Parcelable {
dest.writeInt(echo ? 1 : 0);
dest.writeInt(yesNo ? 1 : 0);
dest.writeInt(helpAvailable ? 1 : 0);
+ dest.writeParcelable(duration, 0);
}
public static final Parcelable.Creator<Input> CREATOR = new Parcelable.Creator<Input>() {
diff --git a/telephony/java/com/android/internal/telephony/cat/ResponseData.java b/telephony/java/com/android/internal/telephony/cat/ResponseData.java
index 84c08f8bb506..677d66bb8265 100644
--- a/telephony/java/com/android/internal/telephony/cat/ResponseData.java
+++ b/telephony/java/com/android/internal/telephony/cat/ResponseData.java
@@ -28,6 +28,16 @@ abstract class ResponseData {
* the ByteArrayOutputStream object.
*/
public abstract void format(ByteArrayOutputStream buf);
+
+ public static void writeLength(ByteArrayOutputStream buf, int length) {
+ // As per ETSI 102.220 Sec7.1.2, if the total length is greater
+ // than 0x7F, it should be coded in two bytes and the first byte
+ // should be 0x81.
+ if (length > 0x7F) {
+ buf.write(0x81);
+ }
+ buf.write(length);
+ }
}
class SelectItemResponseData extends ResponseData {
@@ -120,7 +130,7 @@ class GetInkeyInputResponseData extends ResponseData {
}
// length - one more for data coding scheme.
- buf.write(data.length + 1);
+ writeLength(buf, data.length + 1);
// data coding scheme
if (mIsUcs2) {
diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
index 876c223de6a8..2ae5a3c3ce9d 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
@@ -974,7 +974,9 @@ public class GSMPhone extends PhoneBase {
}
public void getCallWaiting(Message onComplete) {
- mCM.queryCallWaiting(CommandsInterface.SERVICE_CLASS_VOICE, onComplete);
+ //As per 3GPP TS 24.083, section 1.6 UE doesn't need to send service
+ //class parameter in call waiting interrogation to network
+ mCM.queryCallWaiting(CommandsInterface.SERVICE_CLASS_NONE, onComplete);
}
public void setCallWaiting(boolean enable, Message onComplete) {
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
index e11bd429903e..b35814c0a100 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
@@ -73,8 +73,6 @@ public class SipPhone extends SipPhoneBase {
private static final String LOG_TAG = "SipPhone";
private static final boolean LOCAL_DEBUG = true;
- //private List<SipConnection> connections = new ArrayList<SipConnection>();
-
// A call that is ringing or (call) waiting
private SipCall ringingCall = new SipCall();
private SipCall foregroundCall = new SipCall();
@@ -111,6 +109,10 @@ public class SipPhone extends SipPhoneBase {
return mProfile.getProfileName();
}
+ public String getSipUri() {
+ return mProfile.getUriString();
+ }
+
public boolean canTake(Object incomingCall) {
synchronized (SipPhone.class) {
if (!(incomingCall instanceof SipAudioCall)) return false;
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhoneFactory.java b/telephony/java/com/android/internal/telephony/sip/SipPhoneFactory.java
index c9e9762a213f..611e3ea020ec 100644
--- a/telephony/java/com/android/internal/telephony/sip/SipPhoneFactory.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhoneFactory.java
@@ -16,7 +16,6 @@
package com.android.internal.telephony.sip;
-import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneNotifier;
import android.content.Context;
@@ -26,42 +25,25 @@ import android.util.Log;
import java.text.ParseException;
/**
- * @hide
+ * {@hide}
*/
public class SipPhoneFactory {
- private static PhoneNotifier sPhoneNotifier = makeDefaultPhoneNotifier();
- private static Context sContext;
-
- public static void makeDefaultPhones(Context context) {
- makeDefaultPhone(context);
- }
-
- public static void makeDefaultPhone(Context context) {
- sContext = context;
- SipPhoneProxy.getInstance().setPhone(
- makePhone("sip:anonymous@localhost"));
- }
-
- public static Phone getDefaultPhone() {
- return SipPhoneProxy.getInstance();
- }
-
- public static SipPhone makePhone(String sipProfileUri) {
+ /**
+ * Makes a {@link SipPhone} object.
+ * @param sipUri the local SIP URI the phone runs on
+ * @param context {@code Context} needed to create a Phone object
+ * @param phoneNotifier {@code PhoneNotifier} needed to create a Phone
+ * object
+ * @return the {@code SipPhone} object or null if the SIP URI is not valid
+ */
+ public static SipPhone makePhone(String sipUri, Context context,
+ PhoneNotifier phoneNotifier) {
try {
- SipProfile profile = new SipProfile.Builder(sipProfileUri).build();
- return new SipPhone(sContext, sPhoneNotifier, profile);
+ SipProfile profile = new SipProfile.Builder(sipUri).build();
+ return new SipPhone(context, phoneNotifier, profile);
} catch (ParseException e) {
- Log.v("SipPhoneProxy", "setPhone", e);
+ Log.w("SipPhoneFactory", "makePhone", e);
return null;
}
}
-
- private static PhoneNotifier makeDefaultPhoneNotifier() {
- try {
- return new com.android.internal.telephony.SipPhoneNotifier();
- } catch (Error e) {
- Log.e("SipPhoneProxy", "makeDefaultPhoneNotifier", e);
- throw e;
- }
- }
}
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhoneProxy.java b/telephony/java/com/android/internal/telephony/sip/SipPhoneProxy.java
deleted file mode 100644
index 8fa29630a5f4..000000000000
--- a/telephony/java/com/android/internal/telephony/sip/SipPhoneProxy.java
+++ /dev/null
@@ -1,763 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.sip;
-
-import com.android.internal.telephony.*;
-import com.android.internal.telephony.gsm.NetworkInfo;
-import com.android.internal.telephony.test.SimulatedRadioControl;
-
-import android.content.Context;
-import android.net.NetworkProperties;
-import android.os.Handler;
-import android.os.Message;
-import android.telephony.CellLocation;
-import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
-import android.util.Log;
-
-import java.util.List;
-
-/**
- * Temporary. Will be removed after integrating with CallManager.
- * (TODO)
- * @hide
- */
-public class SipPhoneProxy implements Phone {
- private static final String LOG_TAG = "PHONE";
-
- private static SipPhoneProxy sPhoneProxy = new SipPhoneProxy();
-
- public static SipPhoneProxy getInstance() {
- return sPhoneProxy;
- }
-
- private SipPhone mActivePhone;
- private CallProxy mRingingCall = new CallProxy();
- private CallProxy mForegroundCall = new CallProxy();
- private CallProxy mBackgroundCall = new CallProxy();
-
- private SipPhoneProxy() {
- }
-
- public void onNewCall(Object call) {
- if (mActivePhone.canTake(call)) {
- Log.v("SipPhoneProxy", "onNewCall(): call taken: " + call);
- } else {
- Log.v("SipPhoneProxy", "onNewCall(): call dropped: " + call);
- }
- }
-
- public synchronized void setPhone(SipPhone phone) {
- if (phone == null) return;
- if (mActivePhone != null) phone.migrateFrom(mActivePhone);
- mActivePhone = phone;
- mForegroundCall.setTarget(phone.getForegroundCall());
- mBackgroundCall.setTarget(phone.getBackgroundCall());
- mRingingCall.setTarget(phone.getRingingCall());
- }
-
- public synchronized Call getForegroundCall() {
- return mForegroundCall;
- }
-
- public synchronized Call getBackgroundCall() {
- return mBackgroundCall;
- }
-
- public synchronized Call getRingingCall() {
- return mRingingCall;
- }
-
-
- public ServiceState getServiceState() {
- return mActivePhone.getServiceState();
- }
-
- public CellLocation getCellLocation() {
- return mActivePhone.getCellLocation();
- }
-
- public DataState getDataConnectionState() {
- return mActivePhone.getDataConnectionState();
- }
-
- public DataState getDataConnectionState(String apnType) {
- return mActivePhone.getDataConnectionState(apnType);
- }
-
- public DataActivityState getDataActivityState() {
- return mActivePhone.getDataActivityState();
- }
-
- public Context getContext() {
- return mActivePhone.getContext();
- }
-
- public void disableDnsCheck(boolean b) {
- mActivePhone.disableDnsCheck(b);
- }
-
- public boolean isDnsCheckDisabled() {
- return mActivePhone.isDnsCheckDisabled();
- }
-
- public State getState() {
- return mActivePhone.getState();
- }
-
- public String getPhoneName() {
- return mActivePhone.getPhoneName();
- }
-
- public int getPhoneType() {
- return mActivePhone.getPhoneType();
- }
-
- public String[] getActiveApnTypes() {
- return mActivePhone.getActiveApnTypes();
- }
-
- public String getActiveApn() {
- return mActivePhone.getActiveApn();
- }
-
- public SignalStrength getSignalStrength() {
- return mActivePhone.getSignalStrength();
- }
-
- public void registerForUnknownConnection(Handler h, int what, Object obj) {
- mActivePhone.registerForUnknownConnection(h, what, obj);
- }
-
- public void unregisterForUnknownConnection(Handler h) {
- mActivePhone.unregisterForUnknownConnection(h);
- }
-
- public void registerForPreciseCallStateChanged(Handler h, int what, Object obj) {
- mActivePhone.registerForPreciseCallStateChanged(h, what, obj);
- }
-
- public void unregisterForPreciseCallStateChanged(Handler h) {
- mActivePhone.unregisterForPreciseCallStateChanged(h);
- }
-
- public void registerForNewRingingConnection(Handler h, int what, Object obj) {
- mActivePhone.registerForNewRingingConnection(h, what, obj);
- }
-
- public void unregisterForNewRingingConnection(Handler h) {
- mActivePhone.unregisterForNewRingingConnection(h);
- }
-
- public void registerForIncomingRing(Handler h, int what, Object obj) {
- mActivePhone.registerForIncomingRing(h, what, obj);
- }
-
- public void unregisterForIncomingRing(Handler h) {
- mActivePhone.unregisterForIncomingRing(h);
- }
-
- public void registerForDisconnect(Handler h, int what, Object obj) {
- mActivePhone.registerForDisconnect(h, what, obj);
- }
-
- public void unregisterForDisconnect(Handler h) {
- mActivePhone.unregisterForDisconnect(h);
- }
-
- public void registerForMmiInitiate(Handler h, int what, Object obj) {
- mActivePhone.registerForMmiInitiate(h, what, obj);
- }
-
- public void unregisterForMmiInitiate(Handler h) {
- mActivePhone.unregisterForMmiInitiate(h);
- }
-
- public void registerForMmiComplete(Handler h, int what, Object obj) {
- mActivePhone.registerForMmiComplete(h, what, obj);
- }
-
- public void unregisterForMmiComplete(Handler h) {
- mActivePhone.unregisterForMmiComplete(h);
- }
-
- public List<? extends MmiCode> getPendingMmiCodes() {
- return mActivePhone.getPendingMmiCodes();
- }
-
- public void sendUssdResponse(String ussdMessge) {
- mActivePhone.sendUssdResponse(ussdMessge);
- }
-
- public void registerForServiceStateChanged(Handler h, int what, Object obj) {
- mActivePhone.registerForServiceStateChanged(h, what, obj);
- }
-
- public void unregisterForServiceStateChanged(Handler h) {
- mActivePhone.unregisterForServiceStateChanged(h);
- }
-
- public void registerForSuppServiceNotification(Handler h, int what, Object obj) {
- mActivePhone.registerForSuppServiceNotification(h, what, obj);
- }
-
- public void unregisterForSuppServiceNotification(Handler h) {
- mActivePhone.unregisterForSuppServiceNotification(h);
- }
-
- public void registerForSuppServiceFailed(Handler h, int what, Object obj) {
- mActivePhone.registerForSuppServiceFailed(h, what, obj);
- }
-
- public void unregisterForSuppServiceFailed(Handler h) {
- mActivePhone.unregisterForSuppServiceFailed(h);
- }
-
- public void registerForInCallVoicePrivacyOn(Handler h, int what, Object obj){
- mActivePhone.registerForInCallVoicePrivacyOn(h,what,obj);
- }
-
- public void unregisterForInCallVoicePrivacyOn(Handler h){
- mActivePhone.unregisterForInCallVoicePrivacyOn(h);
- }
-
- public void registerForInCallVoicePrivacyOff(Handler h, int what, Object obj){
- mActivePhone.registerForInCallVoicePrivacyOff(h,what,obj);
- }
-
- public void unregisterForInCallVoicePrivacyOff(Handler h){
- mActivePhone.unregisterForInCallVoicePrivacyOff(h);
- }
-
- public void registerForCdmaOtaStatusChange(Handler h, int what, Object obj) {
- mActivePhone.registerForCdmaOtaStatusChange(h,what,obj);
- }
-
- public void unregisterForCdmaOtaStatusChange(Handler h) {
- mActivePhone.unregisterForCdmaOtaStatusChange(h);
- }
-
- public void registerForSubscriptionInfoReady(Handler h, int what, Object obj) {
- mActivePhone.registerForSubscriptionInfoReady(h, what, obj);
- }
-
- public void unregisterForSubscriptionInfoReady(Handler h) {
- mActivePhone.unregisterForSubscriptionInfoReady(h);
- }
-
- public void registerForEcmTimerReset(Handler h, int what, Object obj) {
- mActivePhone.registerForEcmTimerReset(h,what,obj);
- }
-
- public void unregisterForEcmTimerReset(Handler h) {
- mActivePhone.unregisterForEcmTimerReset(h);
- }
-
- public void registerForRingbackTone(Handler h, int what, Object obj) {
- mActivePhone.registerForRingbackTone(h,what,obj);
- }
-
- public void unregisterForRingbackTone(Handler h) {
- mActivePhone.unregisterForRingbackTone(h);
- }
-
- public void registerForResendIncallMute(Handler h, int what, Object obj) {
- mActivePhone.registerForResendIncallMute(h,what,obj);
- }
-
- public void unregisterForResendIncallMute(Handler h) {
- mActivePhone.unregisterForResendIncallMute(h);
- }
-
- public boolean getIccRecordsLoaded() {
- return mActivePhone.getIccRecordsLoaded();
- }
-
- public IccCard getIccCard() {
- return mActivePhone.getIccCard();
- }
-
- public void acceptCall() throws CallStateException {
- mActivePhone.acceptCall();
- }
-
- public void rejectCall() throws CallStateException {
- mActivePhone.rejectCall();
- }
-
- public void switchHoldingAndActive() throws CallStateException {
- mActivePhone.switchHoldingAndActive();
- }
-
- public boolean canConference() {
- return mActivePhone.canConference();
- }
-
- public void conference() throws CallStateException {
- mActivePhone.conference();
- }
-
- public void enableEnhancedVoicePrivacy(boolean enable, Message onComplete) {
- mActivePhone.enableEnhancedVoicePrivacy(enable, onComplete);
- }
-
- public void getEnhancedVoicePrivacy(Message onComplete) {
- mActivePhone.getEnhancedVoicePrivacy(onComplete);
- }
-
- public boolean canTransfer() {
- return mActivePhone.canTransfer();
- }
-
- public void explicitCallTransfer() throws CallStateException {
- mActivePhone.explicitCallTransfer();
- }
-
- public void clearDisconnected() {
- mActivePhone.clearDisconnected();
- }
-
- public Connection dial(String dialString) throws CallStateException {
- return mActivePhone.dial(dialString);
- }
-
- public boolean handlePinMmi(String dialString) {
- return mActivePhone.handlePinMmi(dialString);
- }
-
- public boolean handleInCallMmiCommands(String command) throws CallStateException {
- return mActivePhone.handleInCallMmiCommands(command);
- }
-
- public void sendDtmf(char c) {
- mActivePhone.sendDtmf(c);
- }
-
- public void startDtmf(char c) {
- mActivePhone.startDtmf(c);
- }
-
- public void stopDtmf() {
- mActivePhone.stopDtmf();
- }
-
- public void setRadioPower(boolean power) {
- mActivePhone.setRadioPower(power);
- }
-
- public boolean getMessageWaitingIndicator() {
- return mActivePhone.getMessageWaitingIndicator();
- }
-
- public boolean getCallForwardingIndicator() {
- return mActivePhone.getCallForwardingIndicator();
- }
-
- public String getLine1Number() {
- return mActivePhone.getLine1Number();
- }
-
- public String getCdmaMin() {
- return mActivePhone.getCdmaMin();
- }
-
- public boolean isMinInfoReady() {
- return mActivePhone.isMinInfoReady();
- }
-
- public String getCdmaPrlVersion() {
- return mActivePhone.getCdmaPrlVersion();
- }
-
- public String getLine1AlphaTag() {
- return mActivePhone.getLine1AlphaTag();
- }
-
- public void setLine1Number(String alphaTag, String number, Message onComplete) {
- mActivePhone.setLine1Number(alphaTag, number, onComplete);
- }
-
- public String getVoiceMailNumber() {
- return mActivePhone.getVoiceMailNumber();
- }
-
- /** @hide */
- public int getVoiceMessageCount(){
- return mActivePhone.getVoiceMessageCount();
- }
-
- public String getVoiceMailAlphaTag() {
- return mActivePhone.getVoiceMailAlphaTag();
- }
-
- public void setVoiceMailNumber(String alphaTag,String voiceMailNumber,
- Message onComplete) {
- mActivePhone.setVoiceMailNumber(alphaTag, voiceMailNumber, onComplete);
- }
-
- public void getCallForwardingOption(int commandInterfaceCFReason,
- Message onComplete) {
- mActivePhone.getCallForwardingOption(commandInterfaceCFReason,
- onComplete);
- }
-
- public void setCallForwardingOption(int commandInterfaceCFReason,
- int commandInterfaceCFAction, String dialingNumber,
- int timerSeconds, Message onComplete) {
- mActivePhone.setCallForwardingOption(commandInterfaceCFReason,
- commandInterfaceCFAction, dialingNumber, timerSeconds, onComplete);
- }
-
- public void getOutgoingCallerIdDisplay(Message onComplete) {
- mActivePhone.getOutgoingCallerIdDisplay(onComplete);
- }
-
- public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode,
- Message onComplete) {
- mActivePhone.setOutgoingCallerIdDisplay(commandInterfaceCLIRMode,
- onComplete);
- }
-
- public void getCallWaiting(Message onComplete) {
- mActivePhone.getCallWaiting(onComplete);
- }
-
- public void setCallWaiting(boolean enable, Message onComplete) {
- mActivePhone.setCallWaiting(enable, onComplete);
- }
-
- public void getAvailableNetworks(Message response) {
- mActivePhone.getAvailableNetworks(response);
- }
-
- public void setNetworkSelectionModeAutomatic(Message response) {
- mActivePhone.setNetworkSelectionModeAutomatic(response);
- }
-
- public void selectNetworkManually(NetworkInfo network, Message response) {
- mActivePhone.selectNetworkManually(network, response);
- }
-
- public void setPreferredNetworkType(int networkType, Message response) {
- mActivePhone.setPreferredNetworkType(networkType, response);
- }
-
- public void getPreferredNetworkType(Message response) {
- mActivePhone.getPreferredNetworkType(response);
- }
-
- public void getNeighboringCids(Message response) {
- mActivePhone.getNeighboringCids(response);
- }
-
- public void setOnPostDialCharacter(Handler h, int what, Object obj) {
- mActivePhone.setOnPostDialCharacter(h, what, obj);
- }
-
- public void setMute(boolean muted) {
- mActivePhone.setMute(muted);
- }
-
- public boolean getMute() {
- return mActivePhone.getMute();
- }
-
- public void invokeOemRilRequestRaw(byte[] data, Message response) {
- mActivePhone.invokeOemRilRequestRaw(data, response);
- }
-
- public void invokeOemRilRequestStrings(String[] strings, Message response) {
- mActivePhone.invokeOemRilRequestStrings(strings, response);
- }
-
- public void getDataCallList(Message response) {
- mActivePhone.getDataCallList(response);
- }
-
- public List<DataConnection> getCurrentDataConnectionList() {
- return mActivePhone.getCurrentDataConnectionList();
- }
-
- public void updateServiceLocation() {
- mActivePhone.updateServiceLocation();
- }
-
- public void enableLocationUpdates() {
- mActivePhone.enableLocationUpdates();
- }
-
- public void disableLocationUpdates() {
- mActivePhone.disableLocationUpdates();
- }
-
- public void setUnitTestMode(boolean f) {
- mActivePhone.setUnitTestMode(f);
- }
-
- public boolean getUnitTestMode() {
- return mActivePhone.getUnitTestMode();
- }
-
- public void setBandMode(int bandMode, Message response) {
- mActivePhone.setBandMode(bandMode, response);
- }
-
- public void queryAvailableBandMode(Message response) {
- mActivePhone.queryAvailableBandMode(response);
- }
-
- public boolean getDataRoamingEnabled() {
- return mActivePhone.getDataRoamingEnabled();
- }
-
- public void setDataRoamingEnabled(boolean enable) {
- mActivePhone.setDataRoamingEnabled(enable);
- }
-
- public void queryCdmaRoamingPreference(Message response) {
- mActivePhone.queryCdmaRoamingPreference(response);
- }
-
- public void setCdmaRoamingPreference(int cdmaRoamingType, Message response) {
- mActivePhone.setCdmaRoamingPreference(cdmaRoamingType, response);
- }
-
- public void setCdmaSubscription(int cdmaSubscriptionType, Message response) {
- mActivePhone.setCdmaSubscription(cdmaSubscriptionType, response);
- }
-
- public SimulatedRadioControl getSimulatedRadioControl() {
- return mActivePhone.getSimulatedRadioControl();
- }
-
- public boolean enableDataConnectivity() {
- return mActivePhone.enableDataConnectivity();
- }
-
- public boolean disableDataConnectivity() {
- return mActivePhone.disableDataConnectivity();
- }
-
- public int enableApnType(String type) {
- return mActivePhone.enableApnType(type);
- }
-
- public int disableApnType(String type) {
- return mActivePhone.disableApnType(type);
- }
-
- public boolean isDataConnectivityEnabled() {
- return mActivePhone.isDataConnectivityEnabled();
- }
-
- public boolean isDataConnectivityPossible() {
- return mActivePhone.isDataConnectivityPossible();
- }
-
- public String getInterfaceName(String apnType) {
- return mActivePhone.getInterfaceName(apnType);
- }
-
- public String getIpAddress(String apnType) {
- return mActivePhone.getIpAddress(apnType);
- }
-
- public String getGateway(String apnType) {
- return mActivePhone.getGateway(apnType);
- }
-
- public String[] getDnsServers(String apnType) {
- return mActivePhone.getDnsServers(apnType);
- }
-
- public String getDeviceId() {
- return mActivePhone.getDeviceId();
- }
-
- public String getDeviceSvn() {
- return mActivePhone.getDeviceSvn();
- }
-
- public String getSubscriberId() {
- return mActivePhone.getSubscriberId();
- }
-
- public String getIccSerialNumber() {
- return mActivePhone.getIccSerialNumber();
- }
-
- public String getEsn() {
- return mActivePhone.getEsn();
- }
-
- public String getMeid() {
- return mActivePhone.getMeid();
- }
-
- public PhoneSubInfo getPhoneSubInfo(){
- return mActivePhone.getPhoneSubInfo();
- }
-
- public IccSmsInterfaceManager getIccSmsInterfaceManager(){
- return mActivePhone.getIccSmsInterfaceManager();
- }
-
- public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager(){
- return mActivePhone.getIccPhoneBookInterfaceManager();
- }
-
- public void setTTYMode(int ttyMode, Message onComplete) {
- mActivePhone.setTTYMode(ttyMode, onComplete);
- }
-
- public void queryTTYMode(Message onComplete) {
- mActivePhone.queryTTYMode(onComplete);
- }
-
- public void activateCellBroadcastSms(int activate, Message response) {
- mActivePhone.activateCellBroadcastSms(activate, response);
- }
-
- public void getCellBroadcastSmsConfig(Message response) {
- mActivePhone.getCellBroadcastSmsConfig(response);
- }
-
- public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response) {
- mActivePhone.setCellBroadcastSmsConfig(configValuesArray, response);
- }
-
- public void notifyDataActivity() {
- mActivePhone.notifyDataActivity();
- }
-
- public void getSmscAddress(Message result) {
- mActivePhone.getSmscAddress(result);
- }
-
- public void setSmscAddress(String address, Message result) {
- mActivePhone.setSmscAddress(address, result);
- }
-
- public int getCdmaEriIconIndex() {
- return mActivePhone.getCdmaEriIconIndex();
- }
-
- public String getCdmaEriText() {
- return mActivePhone.getCdmaEriText();
- }
-
- public int getCdmaEriIconMode() {
- return mActivePhone.getCdmaEriIconMode();
- }
-
- public void sendBurstDtmf(String dtmfString, int on, int off, Message onComplete){
- mActivePhone.sendBurstDtmf(dtmfString, on, off, onComplete);
- }
-
- public void exitEmergencyCallbackMode(){
- mActivePhone.exitEmergencyCallbackMode();
- }
-
- public boolean isOtaSpNumber(String dialStr){
- return mActivePhone.isOtaSpNumber(dialStr);
- }
-
- public void registerForCallWaiting(Handler h, int what, Object obj){
- mActivePhone.registerForCallWaiting(h,what,obj);
- }
-
- public void unregisterForCallWaiting(Handler h){
- mActivePhone.unregisterForCallWaiting(h);
- }
-
- public void registerForSignalInfo(Handler h, int what, Object obj) {
- mActivePhone.registerForSignalInfo(h,what,obj);
- }
-
- public void unregisterForSignalInfo(Handler h) {
- mActivePhone.unregisterForSignalInfo(h);
- }
-
- public void registerForDisplayInfo(Handler h, int what, Object obj) {
- mActivePhone.registerForDisplayInfo(h,what,obj);
- }
-
- public void unregisterForDisplayInfo(Handler h) {
- mActivePhone.unregisterForDisplayInfo(h);
- }
-
- public void registerForNumberInfo(Handler h, int what, Object obj) {
- mActivePhone.registerForNumberInfo(h, what, obj);
- }
-
- public void unregisterForNumberInfo(Handler h) {
- mActivePhone.unregisterForNumberInfo(h);
- }
-
- public void registerForRedirectedNumberInfo(Handler h, int what, Object obj) {
- mActivePhone.registerForRedirectedNumberInfo(h, what, obj);
- }
-
- public void unregisterForRedirectedNumberInfo(Handler h) {
- mActivePhone.unregisterForRedirectedNumberInfo(h);
- }
-
- public void registerForLineControlInfo(Handler h, int what, Object obj) {
- mActivePhone.registerForLineControlInfo( h, what, obj);
- }
-
- public void unregisterForLineControlInfo(Handler h) {
- mActivePhone.unregisterForLineControlInfo(h);
- }
-
- public void registerFoT53ClirlInfo(Handler h, int what, Object obj) {
- mActivePhone.registerFoT53ClirlInfo(h, what, obj);
- }
-
- public void unregisterForT53ClirInfo(Handler h) {
- mActivePhone.unregisterForT53ClirInfo(h);
- }
-
- public void registerForT53AudioControlInfo(Handler h, int what, Object obj) {
- mActivePhone.registerForT53AudioControlInfo( h, what, obj);
- }
-
- public void unregisterForT53AudioControlInfo(Handler h) {
- mActivePhone.unregisterForT53AudioControlInfo(h);
- }
-
- public void setOnEcbModeExitResponse(Handler h, int what, Object obj){
- mActivePhone.setOnEcbModeExitResponse(h,what,obj);
- }
-
- public void unsetOnEcbModeExitResponse(Handler h){
- mActivePhone.unsetOnEcbModeExitResponse(h);
- }
-
- public Connection dial(String dialString, UUSInfo uusInfo)
- throws CallStateException {
- return mActivePhone.dial(dialString, uusInfo);
- }
-
- public boolean needsOtaServiceProvisioning() {
- return mActivePhone.needsOtaServiceProvisioning();
- }
-
- public NetworkProperties getNetworkProperties(String apnType) {
- return mActivePhone.getNetworkProperties(apnType);
- }
-}
diff --git a/test-runner/src/android/test/mock/MockContentProvider.java b/test-runner/src/android/test/mock/MockContentProvider.java
index 3fd71c8426d0..b63ff3dca8df 100644
--- a/test-runner/src/android/test/mock/MockContentProvider.java
+++ b/test-runner/src/android/test/mock/MockContentProvider.java
@@ -127,6 +127,16 @@ public class MockContentProvider extends ContentProvider {
throw new UnsupportedOperationException();
}
+ @SuppressWarnings("unused")
+ public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException {
+ return MockContentProvider.this.getStreamTypes(url, mimeTypeFilter);
+ }
+
+ @SuppressWarnings("unused")
+ public AssetFileDescriptor openTypedAssetFile(Uri url, String mimeType, Bundle opts)
+ throws RemoteException, FileNotFoundException {
+ return MockContentProvider.this.openTypedAssetFile(url, mimeType, opts);
+ }
}
private final InversionIContentProvider mIContentProvider = new InversionIContentProvider();
@@ -222,6 +232,14 @@ public class MockContentProvider extends ContentProvider {
throw new UnsupportedOperationException("unimplemented mock method call");
}
+ public String[] getStreamTypes(Uri url, String mimeTypeFilter) {
+ throw new UnsupportedOperationException("unimplemented mock method call");
+ }
+
+ public AssetFileDescriptor openTypedAssetFile(Uri url, String mimeType, Bundle opts) {
+ throw new UnsupportedOperationException("unimplemented mock method call");
+ }
+
/**
* Returns IContentProvider which calls back same methods in this class.
* By overriding this class, we avoid the mechanism hidden behind ContentProvider
diff --git a/test-runner/src/android/test/mock/MockIContentProvider.java b/test-runner/src/android/test/mock/MockIContentProvider.java
index 0be5bea276f3..183be415880c 100644
--- a/test-runner/src/android/test/mock/MockIContentProvider.java
+++ b/test-runner/src/android/test/mock/MockIContentProvider.java
@@ -32,6 +32,7 @@ import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import java.io.FileNotFoundException;
import java.util.ArrayList;
/**
@@ -102,4 +103,13 @@ public class MockIContentProvider implements IContentProvider {
public IBinder asBinder() {
throw new UnsupportedOperationException("unimplemented mock method");
}
+
+ public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
+ public AssetFileDescriptor openTypedAssetFile(Uri url, String mimeType, Bundle opts)
+ throws RemoteException, FileNotFoundException {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java b/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
index b62db513c185..740f544c4d97 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
@@ -510,6 +510,14 @@ public class CallbackProxy extends Handler implements EventSender, LayoutTestCon
obtainMessage(SET_GEOLOCATION_PERMISSION, allow ? 1 : 0, 0).sendToTarget();
}
+ public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+ boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
+ // Configuration is in WebKit, so stay on WebCore thread, but go via the TestShellActivity
+ // as we need access to the Webview.
+ mLayoutTestController.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
+ canProvideGamma, gamma);
+ }
+
public void overridePreference(String key, boolean value) {
Message message = obtainMessage(OVERRIDE_PREFERENCE);
message.getData().putString("key", key);
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java
index 15288b5ce725..9be2f1c917a5 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java
@@ -71,4 +71,8 @@ public interface LayoutTestController {
// For XSSAuditor tests
public void setXSSAuditorEnabled(boolean flag);
+
+ // For DeviceOrientation tests
+ public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+ boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma);
}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index 8c7254c4c15b..db076daaa1a1 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -150,6 +150,9 @@ public class TestShellActivity extends Activity implements LayoutTestController
if (intent != null) {
executeIntent(intent);
}
+
+ // This is asynchronous, but it gets processed by WebCore before it starts loading pages.
+ mWebView.useMockDeviceOrientation();
}
@Override
@@ -494,6 +497,12 @@ public class TestShellActivity extends Activity implements LayoutTestController
mGeolocationPermission = allow;
}
+ public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+ boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
+ mWebView.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
+ canProvideGamma, gamma);
+ }
+
public void overridePreference(String key, boolean value) {
// TODO: We should look up the correct WebView for the frame which
// called the layoutTestController method. Currently, we just use the
diff --git a/tests/DumpRenderTree2/Android.mk b/tests/DumpRenderTree2/Android.mk
index 948ad72cd5df..eddbb4bf7b76 100644
--- a/tests/DumpRenderTree2/Android.mk
+++ b/tests/DumpRenderTree2/Android.mk
@@ -5,6 +5,8 @@ LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
LOCAL_STATIC_JAVA_LIBRARIES := diff_match_patch
LOCAL_PACKAGE_NAME := DumpRenderTree2
diff --git a/tests/DumpRenderTree2/AndroidManifest.xml b/tests/DumpRenderTree2/AndroidManifest.xml
index 9f6097a960ac..dd0c4e9f416a 100644
--- a/tests/DumpRenderTree2/AndroidManifest.xml
+++ b/tests/DumpRenderTree2/AndroidManifest.xml
@@ -16,6 +16,8 @@ limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.dumprendertree2">
<application>
+ <uses-library android:name="android.test.runner" />
+
<activity android:name=".ui.DirListActivity"
android:label="Dump Render Tree 2"
android:configChanges="orientation">
@@ -25,8 +27,15 @@ limitations under the License.
</intent-filter>
</activity>
+ <!-- android:launchMode="singleTask" is there so we only have a one instance
+ of this activity. However, it doesn't seem to work exactly like described in the
+ documentation, because the behaviour of the application suggest
+ there is only a single task for all 3 activities. We don't understand
+ how exactly it all works, but at the moment it works just fine.
+ It can lead to some weird behaviour in the future. -->
<activity android:name=".TestsListActivity"
- android:label="Tests' list activity">
+ android:label="Tests' list activity"
+ android:launchMode="singleTask">
</activity>
<activity android:name=".LayoutTestsExecutor"
@@ -38,6 +47,10 @@ limitations under the License.
</service>
</application>
+ <instrumentation android:name="com.android.dumprendertree2.scriptsupport.ScriptTestRunner"
+ android:targetPackage="com.android.dumprendertree2"
+ android:label="Layout tests script runner" />
+
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_SDCARD" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
diff --git a/tests/DumpRenderTree2/assets/run_layout_tests.py b/tests/DumpRenderTree2/assets/run_layout_tests.py
new file mode 100644
index 000000000000..b13d8c9f3d38
--- /dev/null
+++ b/tests/DumpRenderTree2/assets/run_layout_tests.py
@@ -0,0 +1,65 @@
+#!/usr/bin/python
+
+"""Run layout tests on the device.
+
+ It runs the specified tests on the device, downloads the summaries to the temporary directory
+ and opens html details in the default browser.
+
+ Usage:
+ run_layout_tests.py PATH
+"""
+
+import sys
+import os
+import subprocess
+import logging
+import webbrowser
+import tempfile
+
+#TODO: These should not be hardcoded
+RESULTS_ABSOLUTE_PATH = "/sdcard/android/LayoutTests-results/"
+DETAILS_HTML = "details.html"
+SUMMARY_TXT = "summary.txt"
+
+def main():
+ if len(sys.argv) > 1:
+ path = sys.argv[1]
+ else:
+ path = ""
+
+ logging.basicConfig(level=logging.INFO, format='%(message)s')
+
+ tmpdir = tempfile.gettempdir()
+
+ # Run the tests in path
+ cmd = "adb shell am instrument "
+ cmd += "-e class com.android.dumprendertree2.scriptsupport.Starter#startLayoutTests "
+ cmd += "-e path \"" + path + "\" "
+ cmd +="-w com.android.dumprendertree2/com.android.dumprendertree2.scriptsupport.ScriptTestRunner"
+
+ logging.info("Running the tests...")
+ subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
+
+ logging.info("Downloading the summaries...")
+
+ # Download the txt summary to tmp folder
+ summary_txt_tmp_path = os.path.join(tmpdir, SUMMARY_TXT)
+ cmd = "adb pull " + RESULTS_ABSOLUTE_PATH + SUMMARY_TXT + " " + summary_txt_tmp_path
+ subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
+
+ # Download the html summary to tmp folder
+ details_html_tmp_path = os.path.join(tmpdir, DETAILS_HTML)
+ cmd = "adb pull " + RESULTS_ABSOLUTE_PATH + DETAILS_HTML + " " + details_html_tmp_path
+ subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
+
+ # Print summary to console
+ logging.info("All done.\n")
+ cmd = "cat " + summary_txt_tmp_path
+ os.system(cmd)
+ logging.info("")
+
+ # Open the browser with summary
+ webbrowser.open(details_html_tmp_path)
+
+if __name__ == "__main__":
+ main();
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/CrashedDummyResult.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/CrashedDummyResult.java
new file mode 100644
index 000000000000..a793dab3aa95
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/CrashedDummyResult.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2;
+
+import android.os.Bundle;
+import android.os.Message;
+import android.webkit.WebView;
+
+/**
+ * A dummy class representing test that crashed.
+ */
+public class CrashedDummyResult extends AbstractResult {
+ String mRelativePath;
+
+ public CrashedDummyResult(String relativePath) {
+ mRelativePath = relativePath;
+ }
+
+ @Override
+ public byte[] getActualImageResult() {
+ return null;
+ }
+
+ @Override
+ public String getActualTextResult() {
+ return null;
+ }
+
+ @Override
+ public Bundle getBundle() {
+ /** TODO: */
+ return null;
+ }
+
+ @Override
+ public String getDiffAsHtml() {
+ /** TODO: Probably show at least expected results */
+ return "Ooops, I crashed...";
+ }
+
+ @Override
+ public String getRelativePath() {
+ return mRelativePath;
+ }
+
+ @Override
+ public ResultCode getResultCode() {
+ return ResultCode.FAIL_CRASHED;
+ }
+
+ @Override
+ public TestType getType() {
+ return null;
+ }
+
+ @Override
+ public void obtainActualResults(WebView webview, Message resultObtainedMsg) {
+ /** This method is not applicable for this type of result */
+ assert false;
+ }
+
+ @Override
+ public void setExpectedImageResult(byte[] expectedResult) {
+ /** TODO */
+ }
+
+ @Override
+ public void setExpectedTextResult(String expectedResult) {
+ /** TODO */
+ }
+} \ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSender.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSender.java
new file mode 100644
index 000000000000..5b7cbc4fe83a
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSender.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2;
+
+import android.webkit.WebView;
+
+/**
+ * A class that acts as a JS interface for webview to mock various touch events,
+ * mouse actions and key presses.
+ *
+ * The methods here just call corresponding methods on EventSenderImpl
+ * that contains the logic of how to execute the methods.
+ */
+public class EventSender {
+ EventSenderImpl mEventSenderImpl = new EventSenderImpl();
+
+ public void reset(WebView webView) {
+ mEventSenderImpl.reset(webView);
+ }
+
+ public void enableDOMUIEventLogging(int domNode) {
+ mEventSenderImpl.enableDOMUIEventLogging(domNode);
+ }
+
+ public void fireKeyboardEventsToElement(int domNode) {
+ mEventSenderImpl.fireKeyboardEventsToElement(domNode);
+ }
+
+ public void keyDown(String character, String[] withModifiers) {
+ mEventSenderImpl.keyDown(character, withModifiers);
+ }
+
+ public void keyDown(String character) {
+ keyDown(character, null);
+ }
+
+ public void leapForward(int milliseconds) {
+ mEventSenderImpl.leapForward(milliseconds);
+ }
+
+ public void mouseClick() {
+ mEventSenderImpl.mouseClick();
+ }
+
+ public void mouseDown() {
+ mEventSenderImpl.mouseDown();
+ }
+
+ public void mouseMoveTo(int x, int y) {
+ mEventSenderImpl.mouseMoveTo(x, y);
+ }
+
+ public void mouseUp() {
+ mEventSenderImpl.mouseUp();
+ }
+
+ public void touchStart() {
+ mEventSenderImpl.touchStart();
+ }
+
+ public void addTouchPoint(int x, int y) {
+ mEventSenderImpl.addTouchPoint(x, y);
+ }
+
+ public void updateTouchPoint(int id, int x, int y) {
+ mEventSenderImpl.updateTouchPoint(id, x, y);
+ }
+
+ public void setTouchModifier(String modifier, boolean enabled) {
+ mEventSenderImpl.setTouchModifier(modifier, enabled);
+ }
+
+ public void touchMove() {
+ mEventSenderImpl.touchMove();
+ }
+
+ public void releaseTouchPoint(int id) {
+ mEventSenderImpl.releaseTouchPoint(id);
+ }
+
+ public void touchEnd() {
+ mEventSenderImpl.touchEnd();
+ }
+
+ public void touchCancel() {
+ mEventSenderImpl.touchCancel();
+ }
+
+ public void clearTouchPoints() {
+ mEventSenderImpl.clearTouchPoints();
+ }
+
+ public void cancelTouchPoint(int id) {
+ mEventSenderImpl.cancelTouchPoint(id);
+ }
+} \ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSenderImpl.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSenderImpl.java
new file mode 100644
index 000000000000..93e61378b071
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSenderImpl.java
@@ -0,0 +1,563 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.webkit.WebView;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * An implementation of EventSender
+ */
+public class EventSenderImpl {
+ private static final String LOG_TAG = "EventSenderImpl";
+
+ private static final int MSG_ENABLE_DOM_UI_EVENT_LOGGING = 0;
+ private static final int MSG_FIRE_KEYBOARD_EVENTS_TO_ELEMENT = 1;
+ private static final int MSG_LEAP_FORWARD = 2;
+
+ private static final int MSG_KEY_DOWN = 3;
+
+ private static final int MSG_MOUSE_DOWN = 4;
+ private static final int MSG_MOUSE_UP = 5;
+ private static final int MSG_MOUSE_CLICK = 6;
+ private static final int MSG_MOUSE_MOVE_TO = 7;
+
+ private static final int MSG_ADD_TOUCH_POINT = 8;
+ private static final int MSG_TOUCH_START = 9;
+ private static final int MSG_UPDATE_TOUCH_POINT = 10;
+ private static final int MSG_TOUCH_MOVE = 11;
+ private static final int MSG_CLEAR_TOUCH_POINTS = 12;
+ private static final int MSG_TOUCH_CANCEL = 13;
+ private static final int MSG_RELEASE_TOUCH_POINT = 14;
+ private static final int MSG_TOUCH_END = 15;
+ private static final int MSG_SET_TOUCH_MODIFIER = 16;
+ private static final int MSG_CANCEL_TOUCH_POINT = 17;
+
+ public static class TouchPoint {
+ WebView mWebView;
+ private int mX;
+ private int mY;
+ private long mDownTime;
+ private boolean mReleased = false;
+ private boolean mMoved = false;
+ private boolean mCancelled = false;
+
+ public TouchPoint(WebView webView, int x, int y) {
+ mWebView = webView;
+ mX = scaleX(x);
+ mY = scaleY(y);
+ }
+
+ public int getX() {
+ return mX;
+ }
+
+ public int getY() {
+ return mY;
+ }
+
+ public boolean hasMoved() {
+ return mMoved;
+ }
+
+ public void move(int newX, int newY) {
+ mX = scaleX(newX);
+ mY = scaleY(newY);
+ mMoved = true;
+ }
+
+ public void resetHasMoved() {
+ mMoved = false;
+ }
+
+ public long getDownTime() {
+ return mDownTime;
+ }
+
+ public void setDownTime(long downTime) {
+ mDownTime = downTime;
+ }
+
+ public boolean isReleased() {
+ return mReleased;
+ }
+
+ public void release() {
+ mReleased = true;
+ }
+
+ public boolean isCancelled() {
+ return mCancelled;
+ }
+
+ public void cancel() {
+ mCancelled = true;
+ }
+
+ private int scaleX(int x) {
+ return (int)(x * mWebView.getScale()) - mWebView.getScrollX();
+ }
+
+ private int scaleY(int y) {
+ return (int)(y * mWebView.getScale()) - mWebView.getScrollY();
+ }
+ }
+
+ private List<TouchPoint> mTouchPoints;
+ private int mTouchMetaState;
+ private int mMouseX;
+ private int mMouseY;
+
+ private WebView mWebView;
+
+ private Handler mEventSenderHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ TouchPoint touchPoint;
+ Bundle bundle;
+ KeyEvent event;
+
+ switch (msg.what) {
+ case MSG_ENABLE_DOM_UI_EVENT_LOGGING:
+ /** TODO: implement */
+ break;
+
+ case MSG_FIRE_KEYBOARD_EVENTS_TO_ELEMENT:
+ /** TODO: implement */
+ break;
+
+ case MSG_LEAP_FORWARD:
+ /** TODO: implement */
+ break;
+
+ case MSG_KEY_DOWN:
+ bundle = (Bundle)msg.obj;
+ String character = bundle.getString("character");
+ String[] withModifiers = bundle.getStringArray("withModifiers");
+
+ if (withModifiers != null && withModifiers.length > 0) {
+ for (int i = 0; i < withModifiers.length; i++) {
+ executeKeyEvent(KeyEvent.ACTION_DOWN,
+ modifierToKeyCode(withModifiers[i]));
+ }
+ }
+ executeKeyEvent(KeyEvent.ACTION_DOWN,
+ charToKeyCode(character.toLowerCase().toCharArray()[0]));
+ break;
+
+ /** MOUSE */
+
+ case MSG_MOUSE_DOWN:
+ /** TODO: Implement */
+ break;
+
+ case MSG_MOUSE_UP:
+ /** TODO: Implement */
+ break;
+
+ case MSG_MOUSE_CLICK:
+ /** TODO: Implement */
+ break;
+
+ case MSG_MOUSE_MOVE_TO:
+ int x = msg.arg1;
+ int y = msg.arg2;
+
+ event = null;
+ if (x > mMouseX) {
+ event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT);
+ } else if (x < mMouseX) {
+ event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT);
+ }
+ if (event != null) {
+ mWebView.onKeyDown(event.getKeyCode(), event);
+ mWebView.onKeyUp(event.getKeyCode(), event);
+ }
+
+ event = null;
+ if (y > mMouseY) {
+ event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_DOWN);
+ } else if (y < mMouseY) {
+ event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_UP);
+ }
+ if (event != null) {
+ mWebView.onKeyDown(event.getKeyCode(), event);
+ mWebView.onKeyUp(event.getKeyCode(), event);
+ }
+
+ mMouseX = x;
+ mMouseY = y;
+ break;
+
+ /** TOUCH */
+
+ case MSG_ADD_TOUCH_POINT:
+ getTouchPoints().add(new TouchPoint(mWebView,
+ msg.arg1, msg.arg2));
+ if (getTouchPoints().size() > 1) {
+ Log.w(LOG_TAG + "::MSG_ADD_TOUCH_POINT", "Added more than one touch point");
+ }
+ break;
+
+ case MSG_TOUCH_START:
+ /**
+ * FIXME: At the moment we don't support multi-touch. Hence, we only examine
+ * the first touch point. In future this method will need rewriting.
+ */
+ if (getTouchPoints().isEmpty()) {
+ return;
+ }
+ touchPoint = getTouchPoints().get(0);
+
+ touchPoint.setDownTime(SystemClock.uptimeMillis());
+ executeTouchEvent(touchPoint, MotionEvent.ACTION_DOWN);
+ break;
+
+ case MSG_UPDATE_TOUCH_POINT:
+ bundle = (Bundle)msg.obj;
+
+ int id = bundle.getInt("id");
+ if (id >= getTouchPoints().size()) {
+ Log.w(LOG_TAG + "::MSG_UPDATE_TOUCH_POINT", "TouchPoint out of bounds: "
+ + id);
+ break;
+ }
+
+ getTouchPoints().get(id).move(bundle.getInt("x"), bundle.getInt("y"));
+ break;
+
+ case MSG_TOUCH_MOVE:
+ /**
+ * FIXME: At the moment we don't support multi-touch. Hence, we only examine
+ * the first touch point. In future this method will need rewriting.
+ */
+ if (getTouchPoints().isEmpty()) {
+ return;
+ }
+ touchPoint = getTouchPoints().get(0);
+
+ if (!touchPoint.hasMoved()) {
+ return;
+ }
+ executeTouchEvent(touchPoint, MotionEvent.ACTION_MOVE);
+ touchPoint.resetHasMoved();
+ break;
+
+ case MSG_CANCEL_TOUCH_POINT:
+ if (msg.arg1 >= getTouchPoints().size()) {
+ Log.w(LOG_TAG + "::MSG_RELEASE_TOUCH_POINT", "TouchPoint out of bounds: "
+ + msg.arg1);
+ break;
+ }
+
+ getTouchPoints().get(msg.arg1).cancel();
+ break;
+
+ case MSG_TOUCH_CANCEL:
+ /**
+ * FIXME: At the moment we don't support multi-touch. Hence, we only examine
+ * the first touch point. In future this method will need rewriting.
+ */
+ if (getTouchPoints().isEmpty()) {
+ return;
+ }
+ touchPoint = getTouchPoints().get(0);
+
+ if (touchPoint.isCancelled()) {
+ executeTouchEvent(touchPoint, MotionEvent.ACTION_CANCEL);
+ }
+ break;
+
+ case MSG_RELEASE_TOUCH_POINT:
+ if (msg.arg1 >= getTouchPoints().size()) {
+ Log.w(LOG_TAG + "::MSG_RELEASE_TOUCH_POINT", "TouchPoint out of bounds: "
+ + msg.arg1);
+ break;
+ }
+
+ getTouchPoints().get(msg.arg1).release();
+ break;
+
+ case MSG_TOUCH_END:
+ /**
+ * FIXME: At the moment we don't support multi-touch. Hence, we only examine
+ * the first touch point. In future this method will need rewriting.
+ */
+ if (getTouchPoints().isEmpty()) {
+ return;
+ }
+ touchPoint = getTouchPoints().get(0);
+
+ executeTouchEvent(touchPoint, MotionEvent.ACTION_UP);
+ if (touchPoint.isReleased()) {
+ getTouchPoints().remove(0);
+ touchPoint = null;
+ }
+ break;
+
+ case MSG_SET_TOUCH_MODIFIER:
+ bundle = (Bundle)msg.obj;
+ String modifier = bundle.getString("modifier");
+ boolean enabled = bundle.getBoolean("enabled");
+
+ int mask = 0;
+ if ("alt".equals(modifier.toLowerCase())) {
+ mask = KeyEvent.META_ALT_ON;
+ } else if ("shift".equals(modifier.toLowerCase())) {
+ mask = KeyEvent.META_SHIFT_ON;
+ } else if ("ctrl".equals(modifier.toLowerCase())) {
+ mask = KeyEvent.META_SYM_ON;
+ }
+
+ if (enabled) {
+ mTouchMetaState |= mask;
+ } else {
+ mTouchMetaState &= ~mask;
+ }
+
+ break;
+
+ case MSG_CLEAR_TOUCH_POINTS:
+ getTouchPoints().clear();
+ break;
+
+ default:
+ break;
+ }
+ }
+ };
+
+ public void reset(WebView webView) {
+ mWebView = webView;
+ mTouchPoints = null;
+ mTouchMetaState = 0;
+ mMouseX = 0;
+ mMouseY = 0;
+ }
+
+ public void enableDOMUIEventLogging(int domNode) {
+ Message msg = mEventSenderHandler.obtainMessage(MSG_ENABLE_DOM_UI_EVENT_LOGGING);
+ msg.arg1 = domNode;
+ msg.sendToTarget();
+ }
+
+ public void fireKeyboardEventsToElement(int domNode) {
+ Message msg = mEventSenderHandler.obtainMessage(MSG_FIRE_KEYBOARD_EVENTS_TO_ELEMENT);
+ msg.arg1 = domNode;
+ msg.sendToTarget();
+ }
+
+ public void leapForward(int milliseconds) {
+ Message msg = mEventSenderHandler.obtainMessage(MSG_LEAP_FORWARD);
+ msg.arg1 = milliseconds;
+ msg.sendToTarget();
+ }
+
+ public void keyDown(String character, String[] withModifiers) {
+ Bundle bundle = new Bundle();
+ bundle.putString("character", character);
+ bundle.putStringArray("withModifiers", withModifiers);
+ mEventSenderHandler.obtainMessage(MSG_KEY_DOWN, bundle).sendToTarget();
+ }
+
+ /** MOUSE */
+
+ public void mouseDown() {
+ mEventSenderHandler.sendEmptyMessage(MSG_MOUSE_DOWN);
+ }
+
+ public void mouseUp() {
+ mEventSenderHandler.sendEmptyMessage(MSG_MOUSE_UP);
+ }
+
+ public void mouseClick() {
+ mEventSenderHandler.sendEmptyMessage(MSG_MOUSE_CLICK);
+ }
+
+ public void mouseMoveTo(int x, int y) {
+ mEventSenderHandler.obtainMessage(MSG_MOUSE_MOVE_TO, x, y).sendToTarget();
+ }
+
+ /** TOUCH */
+
+ public void addTouchPoint(int x, int y) {
+ mEventSenderHandler.obtainMessage(MSG_ADD_TOUCH_POINT, x, y).sendToTarget();
+ }
+
+ public void touchStart() {
+ mEventSenderHandler.sendEmptyMessage(MSG_TOUCH_START);
+ }
+
+ public void updateTouchPoint(int id, int x, int y) {
+ Bundle bundle = new Bundle();
+ bundle.putInt("id", id);
+ bundle.putInt("x", x);
+ bundle.putInt("y", y);
+ mEventSenderHandler.obtainMessage(MSG_UPDATE_TOUCH_POINT, bundle).sendToTarget();
+ }
+
+ public void touchMove() {
+ mEventSenderHandler.sendEmptyMessage(MSG_TOUCH_MOVE);
+ }
+
+ public void cancelTouchPoint(int id) {
+ Message msg = mEventSenderHandler.obtainMessage(MSG_CANCEL_TOUCH_POINT);
+ msg.arg1 = id;
+ msg.sendToTarget();
+ }
+
+ public void touchCancel() {
+ mEventSenderHandler.sendEmptyMessage(MSG_TOUCH_CANCEL);
+ }
+
+ public void releaseTouchPoint(int id) {
+ Message msg = mEventSenderHandler.obtainMessage(MSG_RELEASE_TOUCH_POINT);
+ msg.arg1 = id;
+ msg.sendToTarget();
+ }
+
+ public void touchEnd() {
+ mEventSenderHandler.sendEmptyMessage(MSG_TOUCH_END);
+ }
+
+ public void setTouchModifier(String modifier, boolean enabled) {
+ Bundle bundle = new Bundle();
+ bundle.putString("modifier", modifier);
+ bundle.putBoolean("enabled", enabled);
+ mEventSenderHandler.obtainMessage(MSG_SET_TOUCH_MODIFIER, bundle).sendToTarget();
+ }
+
+ public void clearTouchPoints() {
+ mEventSenderHandler.sendEmptyMessage(MSG_CLEAR_TOUCH_POINTS);
+ }
+
+ private List<TouchPoint> getTouchPoints() {
+ if (mTouchPoints == null) {
+ mTouchPoints = new LinkedList<TouchPoint>();
+ }
+
+ return mTouchPoints;
+ }
+
+ private void executeTouchEvent(TouchPoint touchPoint, int action) {
+ MotionEvent event =
+ MotionEvent.obtain(touchPoint.getDownTime(), SystemClock.uptimeMillis(),
+ action, touchPoint.getX(), touchPoint.getY(), mTouchMetaState);
+ mWebView.onTouchEvent(event);
+ }
+
+ private void executeKeyEvent(int action, int keyCode) {
+ KeyEvent event = new KeyEvent(action, keyCode);
+ mWebView.onKeyDown(event.getKeyCode(), event);
+ }
+
+ /**
+ * Assumes lowercase chars, case needs to be handled by calling function.
+ */
+ private static int charToKeyCode(char c) {
+ // handle numbers
+ if (c >= '0' && c <= '9') {
+ int offset = c - '0';
+ return KeyEvent.KEYCODE_0 + offset;
+ }
+
+ // handle characters
+ if (c >= 'a' && c <= 'z') {
+ int offset = c - 'a';
+ return KeyEvent.KEYCODE_A + offset;
+ }
+
+ // handle all others
+ switch (c) {
+ case '*':
+ return KeyEvent.KEYCODE_STAR;
+
+ case '#':
+ return KeyEvent.KEYCODE_POUND;
+
+ case ',':
+ return KeyEvent.KEYCODE_COMMA;
+
+ case '.':
+ return KeyEvent.KEYCODE_PERIOD;
+
+ case '\t':
+ return KeyEvent.KEYCODE_TAB;
+
+ case ' ':
+ return KeyEvent.KEYCODE_SPACE;
+
+ case '\n':
+ return KeyEvent.KEYCODE_ENTER;
+
+ case '\b':
+ case 0x7F:
+ return KeyEvent.KEYCODE_DEL;
+
+ case '~':
+ return KeyEvent.KEYCODE_GRAVE;
+
+ case '-':
+ return KeyEvent.KEYCODE_MINUS;
+
+ case '=':
+ return KeyEvent.KEYCODE_EQUALS;
+
+ case '(':
+ return KeyEvent.KEYCODE_LEFT_BRACKET;
+
+ case ')':
+ return KeyEvent.KEYCODE_RIGHT_BRACKET;
+
+ case '\\':
+ return KeyEvent.KEYCODE_BACKSLASH;
+
+ case ';':
+ return KeyEvent.KEYCODE_SEMICOLON;
+
+ case '\'':
+ return KeyEvent.KEYCODE_APOSTROPHE;
+
+ case '/':
+ return KeyEvent.KEYCODE_SLASH;
+
+ default:
+ return c;
+ }
+ }
+
+ private static int modifierToKeyCode(String modifier) {
+ if (modifier.equals("ctrlKey")) {
+ return KeyEvent.KEYCODE_ALT_LEFT;
+ } else if (modifier.equals("shiftKey")) {
+ return KeyEvent.KEYCODE_SHIFT_LEFT;
+ } else if (modifier.equals("altKey")) {
+ return KeyEvent.KEYCODE_SYM;
+ }
+
+ return KeyEvent.KEYCODE_UNKNOWN;
+ }
+} \ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java
index 8ff5e630d01f..6db957128560 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestController.java
@@ -89,4 +89,12 @@ public class LayoutTestController {
Log.w(LOG_TAG + "::setMockGeolocationError", "code: " + code + " message: " + message);
MockGeolocation.getInstance().setError(code, message);
}
-} \ No newline at end of file
+
+ public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+ boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
+ // Configuration is in WebKit, so stay on WebCore thread, but go via LayoutTestsExecutor
+ // as we need access to the Webview.
+ mLayoutTestsExecutor.setMockDeviceOrientation(
+ canProvideAlpha, alpha, canProvideBeta, beta, canProvideGamma, gamma);
+ }
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
index 063cb7303c04..8cc4921d9055 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
@@ -82,6 +82,7 @@ public class LayoutTestsExecutor extends Activity {
private static final int DEFAULT_TIME_OUT_MS = 15 * 1000;
+ /** A list of tests that remain to run since last crash */
private List<String> mTestsList;
/**
@@ -91,6 +92,7 @@ public class LayoutTestsExecutor extends Activity {
*/
private int mCurrentTestIndex;
+ /** The total number of tests to run, doesn't reset after crash */
private int mTotalTestCount;
private WebView mCurrentWebView;
@@ -108,6 +110,8 @@ public class LayoutTestsExecutor extends Activity {
private boolean mSetGeolocationPermissionCalled;
private boolean mGeolocationPermission;
+ private EventSender mEventSender = new EventSender();
+
private WakeLock mScreenDimLock;
/** COMMUNICATION WITH ManagerService */
@@ -119,7 +123,7 @@ public class LayoutTestsExecutor extends Activity {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mManagerServiceMessenger = new Messenger(service);
- runNextTest();
+ startTests();
}
@Override
@@ -246,7 +250,7 @@ public class LayoutTestsExecutor extends Activity {
mCurrentTestIndex = intent.getIntExtra(EXTRA_TEST_INDEX, -1);
mTotalTestCount = mCurrentTestIndex + mTestsList.size();
- PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
+ PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
mScreenDimLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK
| PowerManager.ON_AFTER_RELEASE, "WakeLock in LayoutTester");
mScreenDimLock.acquire();
@@ -267,6 +271,8 @@ public class LayoutTestsExecutor extends Activity {
mCurrentWebView = new WebView(this);
setupWebView(mCurrentWebView);
+ mEventSender.reset(mCurrentWebView);
+
setContentView(mCurrentWebView);
if (previousWebView != null) {
Log.d(LOG_TAG + "::reset", "previousWebView != null");
@@ -278,6 +284,7 @@ public class LayoutTestsExecutor extends Activity {
webView.setWebViewClient(mWebViewClient);
webView.setWebChromeClient(mWebChromeClient);
webView.addJavascriptInterface(mLayoutTestController, "layoutTestController");
+ webView.addJavascriptInterface(mEventSender, "eventSender");
/**
* Setting a touch interval of -1 effectively disables the optimisation in WebView
@@ -301,6 +308,29 @@ public class LayoutTestsExecutor extends Activity {
webViewSettings.setDomStorageEnabled(true);
webViewSettings.setWorkersEnabled(false);
webViewSettings.setXSSAuditorEnabled(false);
+
+ // This is asynchronous, but it gets processed by WebCore before it starts loading pages.
+ mCurrentWebView.useMockDeviceOrientation();
+ }
+
+ private void startTests() {
+ try {
+ Message serviceMsg =
+ Message.obtain(null, ManagerService.MSG_FIRST_TEST);
+
+ Bundle bundle = new Bundle();
+ if (!mTestsList.isEmpty()) {
+ bundle.putString("firstTest", mTestsList.get(0));
+ bundle.putInt("index", mCurrentTestIndex);
+ }
+
+ serviceMsg.setData(bundle);
+ mManagerServiceMessenger.send(serviceMsg);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG + "::startTests", e.getMessage());
+ }
+
+ runNextTest();
}
private void runNextTest() {
@@ -312,6 +342,8 @@ public class LayoutTestsExecutor extends Activity {
}
mCurrentTestRelativePath = mTestsList.remove(0);
+ Log.d(LOG_TAG + "::runNextTest", "Start: " + mCurrentTestRelativePath +
+ "(" + mCurrentTestIndex + ")");
mCurrentTestUri =
Uri.fromFile(new File(TESTS_ROOT_DIR_PATH, mCurrentTestRelativePath)).toString();
@@ -386,6 +418,9 @@ public class LayoutTestsExecutor extends Activity {
if (mCurrentTestTimedOut) {
bundle.putString("resultCode", AbstractResult.ResultCode.FAIL_TIMED_OUT.name());
}
+ if (!mTestsList.isEmpty()) {
+ bundle.putString("nextTest", mTestsList.get(0));
+ }
serviceMsg.setData(bundle);
mManagerServiceMessenger.send(serviceMsg);
@@ -435,8 +470,7 @@ public class LayoutTestsExecutor extends Activity {
Handler mLayoutTestControllerHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
- assert mCurrentState.isRunningState()
- : "mCurrentState = " + mCurrentState.name();
+ assert mCurrentState.isRunningState() : "mCurrentState = " + mCurrentState.name();
switch (msg.what) {
case MSG_WAIT_UNTIL_DONE:
@@ -538,4 +572,10 @@ public class LayoutTestsExecutor extends Activity {
msg.arg1 = allow ? 1 : 0;
msg.sendToTarget();
}
-} \ No newline at end of file
+
+ public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
+ boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
+ mCurrentWebView.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
+ canProvideGamma, gamma);
+ }
+}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java
index 3c77a55d4502..7ec3dd7cf1dc 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java
@@ -38,6 +38,10 @@ public class ManagerService extends Service {
private static final String LOG_TAG = "ManagerService";
+ private static final int MSG_TEST_CRASHED = 0;
+
+ private static final int CRASH_TIMEOUT_MS = 20 * 1000;
+
/** TODO: make it a setting */
static final String TESTS_ROOT_DIR_PATH =
Environment.getExternalStorageDirectory() +
@@ -67,11 +71,22 @@ public class ManagerService extends Service {
static final int MSG_PROCESS_ACTUAL_RESULTS = 0;
static final int MSG_ALL_TESTS_FINISHED = 1;
+ static final int MSG_FIRST_TEST = 2;
+ /**
+ * This handler is purely for IPC. It is used to create mMessenger
+ * that generates a binder returned in onBind method.
+ */
private Handler mIncomingHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
+ case MSG_FIRST_TEST:
+ mSummarizer.reset();
+ Bundle bundle = msg.getData();
+ ensureNextTestSetup(bundle.getString("firstTest"), bundle.getInt("index"));
+ break;
+
case MSG_PROCESS_ACTUAL_RESULTS:
Log.d(LOG_TAG + ".mIncomingHandler", msg.getData().getString("relativePath"));
onActualResultsObtained(msg.getData());
@@ -79,6 +94,11 @@ public class ManagerService extends Service {
case MSG_ALL_TESTS_FINISHED:
mSummarizer.summarize();
+ Intent intent = new Intent(ManagerService.this, TestsListActivity.class);
+ intent.setAction(Intent.ACTION_SHUTDOWN);
+ /** This flag is needed because we send the intent from the service */
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
break;
}
}
@@ -86,9 +106,21 @@ public class ManagerService extends Service {
private Messenger mMessenger = new Messenger(mIncomingHandler);
+ private Handler mCrashMessagesHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_TEST_CRASHED) {
+ onTestCrashed();
+ }
+ }
+ };
+
private FileFilter mFileFilter;
private Summarizer mSummarizer;
+ private String mCurrentlyRunningTest;
+ private int mCurrentlyRunningTestIndex;
+
@Override
public void onCreate() {
super.onCreate();
@@ -98,13 +130,55 @@ public class ManagerService extends Service {
}
@Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ return START_STICKY;
+ }
+
+ @Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
private void onActualResultsObtained(Bundle bundle) {
+ mCrashMessagesHandler.removeMessages(MSG_TEST_CRASHED);
+ ensureNextTestSetup(bundle.getString("nextTest"), bundle.getInt("testIndex") + 1);
+
AbstractResult results =
AbstractResult.TestType.valueOf(bundle.getString("type")).createResult(bundle);
+
+ handleResults(results);
+ }
+
+ private void ensureNextTestSetup(String nextTest, int index) {
+ if (nextTest == null) {
+ return;
+ }
+
+ mCurrentlyRunningTest = nextTest;
+ mCurrentlyRunningTestIndex = index;
+ mCrashMessagesHandler.sendEmptyMessageDelayed(MSG_TEST_CRASHED, CRASH_TIMEOUT_MS);
+ }
+
+ /**
+ * This sends an intent to TestsListActivity to restart LayoutTestsExecutor.
+ * The more detailed description of the flow is in the comment of onNewIntent
+ * method in TestsListActivity.
+ */
+ private void onTestCrashed() {
+ handleResults(new CrashedDummyResult(mCurrentlyRunningTest));
+
+ Log.w(LOG_TAG + "::onTestCrashed", mCurrentlyRunningTest +
+ "(" + mCurrentlyRunningTestIndex + ")");
+
+ Intent intent = new Intent(this, TestsListActivity.class);
+ intent.setAction(Intent.ACTION_REBOOT);
+ /** This flag is needed because we send the intent from the service */
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra("crashedTestIndex", mCurrentlyRunningTestIndex);
+ startActivity(intent);
+ }
+
+ private void handleResults(AbstractResult results) {
String relativePath = results.getRelativePath();
results.setExpectedTextResult(getExpectedTextResult(relativePath));
results.setExpectedImageResult(getExpectedImageResult(relativePath));
@@ -134,7 +208,8 @@ public class ManagerService extends Service {
return;
}
- String resultPath = FileFilter.setPathEnding(testPath, "-actual." + IMAGE_RESULT_EXTENSION);
+ String resultPath = FileFilter.setPathEnding(testPath,
+ "-actual." + IMAGE_RESULT_EXTENSION);
FsUtils.writeDataToStorage(new File(RESULTS_ROOT_DIR_PATH, resultPath),
actualImageResult, false);
}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java
index 1b73f97afe5e..e27ecc963380 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java
@@ -166,7 +166,8 @@ public class Summarizer {
"</script>";
/** TODO: Make it a setting */
- private static final String HTML_SUMMARY_RELATIVE_PATH = "summary.html";
+ private static final String HTML_DETAILS_RELATIVE_PATH = "details.html";
+ private static final String TXT_SUMMARY_RELATIVE_PATH = "summary.txt";
private int mCrashedTestsCount = 0;
private List<AbstractResult> mFailedNotIgnoredTests = new ArrayList<AbstractResult>();
@@ -176,6 +177,8 @@ public class Summarizer {
private FileFilter mFileFilter;
private String mResultsRootDirPath;
+ private String mTitleString;
+
public Summarizer(FileFilter fileFilter, String resultsRootDirPath) {
mFileFilter = fileFilter;
mResultsRootDirPath = resultsRootDirPath;
@@ -198,6 +201,35 @@ public class Summarizer {
}
public void summarize() {
+ createHtmlDetails();
+ createTxtSummary();
+ }
+
+ public void reset() {
+ mCrashedTestsCount = 0;
+ mFailedNotIgnoredTests.clear();
+ mIgnoredTests.clear();
+ mPassedNotIgnoredTests.clear();
+ mTitleString = null;
+ }
+
+ private void createTxtSummary() {
+ StringBuilder txt = new StringBuilder();
+
+ txt.append(getTitleString() + "\n");
+ if (mCrashedTestsCount > 0) {
+ txt.append("CRASHED (total among all tests): " + mCrashedTestsCount + "\n");
+ txt.append("-------------");
+ }
+ txt.append("FAILED: " + mFailedNotIgnoredTests.size() + "\n");
+ txt.append("IGNORED: " + mIgnoredTests.size() + "\n");
+ txt.append("PASSED: " + mPassedNotIgnoredTests.size() + "\n");
+
+ FsUtils.writeDataToStorage(new File(mResultsRootDirPath, TXT_SUMMARY_RELATIVE_PATH),
+ txt.toString().getBytes(), false);
+ }
+
+ private void createHtmlDetails() {
StringBuilder html = new StringBuilder();
html.append("<html><head>");
@@ -215,17 +247,24 @@ public class Summarizer {
html.append("</body></html>");
- FsUtils.writeDataToStorage(new File(mResultsRootDirPath, HTML_SUMMARY_RELATIVE_PATH),
+ FsUtils.writeDataToStorage(new File(mResultsRootDirPath, HTML_DETAILS_RELATIVE_PATH),
html.toString().getBytes(), false);
}
+ private String getTitleString() {
+ if (mTitleString == null) {
+ int total = mFailedNotIgnoredTests.size() +
+ mPassedNotIgnoredTests.size() +
+ mIgnoredTests.size();
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
+ mTitleString = " - total of " + total + " tests - " + dateFormat.format(new Date());
+ }
+
+ return mTitleString;
+ }
+
private void createTopSummaryTable(StringBuilder html) {
- int total = mFailedNotIgnoredTests.size() +
- mPassedNotIgnoredTests.size() +
- mIgnoredTests.size();
- SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
- html.append("<h1> - total of " + total + " tests - ");
- html.append(dateFormat.format(new Date()) + "</h1>");
+ html.append("<h1>" + getTitleString() + "</h1>");
html.append("<table class=\"summary\">");
createSummaryTableRow(html, "CRASHED", mCrashedTestsCount);
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListActivity.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListActivity.java
index a402ae124a40..c95199ffb74a 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListActivity.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListActivity.java
@@ -24,12 +24,14 @@ import android.os.Handler;
import android.os.Message;
import android.view.Window;
+import com.android.dumprendertree2.scriptsupport.OnEverythingFinishedCallback;
+
import java.util.ArrayList;
/**
* An Activity that generates a list of tests and sends the intent to
* LayoutTestsExecuter to run them. It also restarts the LayoutTestsExecuter
- * after it crashes (TODO).
+ * after it crashes.
*/
public class TestsListActivity extends Activity {
@@ -57,6 +59,9 @@ public class TestsListActivity extends Activity {
private ArrayList<String> mTestsList;
private int mTotalTestCount;
+ private OnEverythingFinishedCallback mOnEverythingFinishedCallback;
+ private boolean mEverythingFinished;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -79,9 +84,68 @@ public class TestsListActivity extends Activity {
sProgressDialog.show();
Message doneMsg = Message.obtain(mHandler, MSG_TEST_LIST_PRELOADER_DONE);
+ Intent serviceIntent = new Intent(this, ManagerService.class);
+ startService(serviceIntent);
+
new TestsListPreloaderThread(path, doneMsg).start();
}
+ @Override
+ protected void onNewIntent(Intent intent) {
+ if (intent.getAction().equals(Intent.ACTION_REBOOT)) {
+ onCrashIntent(intent);
+ } else if (intent.getAction().equals(Intent.ACTION_SHUTDOWN)) {
+ onEverythingFinishedIntent(intent);
+ }
+ }
+
+ /**
+ * This method handles an intent that comes from ManageService when crash is detected.
+ * The intent contains an index in mTestsList of the test that crashed. TestsListActivity
+ * restarts the LayoutTestsExecutor from the following test in mTestsList, by sending
+ * an intent to it. This new intent contains a list of remaining tests to run,
+ * total count of all tests, and the index of the first test to run after restarting.
+ * LayoutTestExecutor runs then as usual, sending reports to ManagerService. If it
+ * detects the crash it sends a new intent and the flow repeats.
+ */
+ private void onCrashIntent(Intent intent) {
+ int nextTestToRun = intent.getIntExtra("crashedTestIndex", -1) + 1;
+ if (nextTestToRun > 0 && nextTestToRun <= mTotalTestCount) {
+ restartExecutor(nextTestToRun);
+ }
+ }
+
+ public void registerOnEverythingFinishedCallback(OnEverythingFinishedCallback callback) {
+ mOnEverythingFinishedCallback = callback;
+ if (mEverythingFinished) {
+ mOnEverythingFinishedCallback.onFinished();
+ }
+ }
+
+ private void onEverythingFinishedIntent(Intent intent) {
+ /** TODO: Show some kind of summary to the user */
+ mEverythingFinished = true;
+ if (mOnEverythingFinishedCallback != null) {
+ mOnEverythingFinishedCallback.onFinished();
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ outState.putStringArrayList("testsList", mTestsList);
+ outState.putInt("totalCount", mTotalTestCount);
+
+ super.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+
+ mTestsList = savedInstanceState.getStringArrayList("testsList");
+ mTotalTestCount = savedInstanceState.getInt("totalCount");
+ }
+
/**
* (Re)starts the executer activity from the given test number (inclusive, 0-based).
* This number is an index in mTestsList, not the sublist passed in the intent.
@@ -93,9 +157,16 @@ public class TestsListActivity extends Activity {
Intent intent = new Intent();
intent.setClass(this, LayoutTestsExecutor.class);
intent.setAction(Intent.ACTION_RUN);
- intent.putStringArrayListExtra(LayoutTestsExecutor.EXTRA_TESTS_LIST,
- new ArrayList<String>(mTestsList.subList(startFrom, mTotalTestCount)));
- intent.putExtra(LayoutTestsExecutor.EXTRA_TEST_INDEX, startFrom);
+
+ if (startFrom < mTotalTestCount) {
+ intent.putStringArrayListExtra(LayoutTestsExecutor.EXTRA_TESTS_LIST,
+ new ArrayList<String>(mTestsList.subList(startFrom, mTotalTestCount)));
+ intent.putExtra(LayoutTestsExecutor.EXTRA_TEST_INDEX, startFrom);
+ } else {
+ intent.putStringArrayListExtra(LayoutTestsExecutor.EXTRA_TESTS_LIST,
+ new ArrayList<String>());
+ }
+
startActivity(intent);
}
} \ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java
index f76105d0fc56..2145af77b230 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/TestsListPreloaderThread.java
@@ -68,15 +68,15 @@ public class TestsListPreloaderThread extends Thread {
File file = new File(TESTS_ROOT_DIR_PATH, mRelativePath);
if (!file.exists()) {
Log.e(LOG_TAG + "::run", "Path does not exist: " + mRelativePath);
- return;
- }
-
- /** Populate the tests' list accordingly */
- if (file.isDirectory()) {
- preloadTests(mRelativePath);
} else {
- mTestsList.add(mRelativePath);
+ /** Populate the tests' list accordingly */
+ if (file.isDirectory()) {
+ preloadTests(mRelativePath);
+ } else {
+ mTestsList.add(mRelativePath);
+ }
}
+
mDoneMsg.obj = mTestsList;
mDoneMsg.sendToTarget();
}
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/OnEverythingFinishedCallback.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/OnEverythingFinishedCallback.java
new file mode 100644
index 000000000000..e1d436447f0f
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/OnEverythingFinishedCallback.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2.scriptsupport;
+
+/**
+ * Callback used to inform scriptsupport.Starter that everything is finished and
+ * we can exit
+ */
+public interface OnEverythingFinishedCallback {
+ public void onFinished();
+} \ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/ScriptTestRunner.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/ScriptTestRunner.java
new file mode 100644
index 000000000000..78f58d537e39
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/ScriptTestRunner.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2.scriptsupport;
+
+import android.os.Bundle;
+import android.test.InstrumentationTestRunner;
+
+/**
+ * Extends InstrumentationTestRunner to allow the script to pass arguments to the application
+ */
+public class ScriptTestRunner extends InstrumentationTestRunner {
+ String mTestsRelativePath;
+
+ @Override
+ public void onCreate(Bundle arguments) {
+ mTestsRelativePath = arguments.getString("path");
+ super.onCreate(arguments);
+ }
+
+ public String getTestsRelativePath() {
+ return mTestsRelativePath;
+ }
+} \ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/Starter.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/Starter.java
new file mode 100644
index 000000000000..ddfae69c8b79
--- /dev/null
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/scriptsupport/Starter.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dumprendertree2.scriptsupport;
+
+import android.content.Intent;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import com.android.dumprendertree2.TestsListActivity;
+
+/**
+ * A class which provides methods that can be invoked by a script running on the host machine to
+ * run the tests.
+ *
+ * It starts a TestsListActivity and does not return until all the tests finish executing.
+ */
+public class Starter extends ActivityInstrumentationTestCase2<TestsListActivity> {
+ private static final String LOG_TAG = "Starter";
+ private boolean mEverythingFinished;
+
+ public Starter() {
+ super(TestsListActivity.class);
+ }
+
+ /**
+ * This method is called from adb to start executing the tests. It doesn't return
+ * until everything is finished so that the script can wait for the end if it needs
+ * to.
+ */
+ public void startLayoutTests() {
+ ScriptTestRunner runner = (ScriptTestRunner)getInstrumentation();
+ String relativePath = runner.getTestsRelativePath();
+
+ Intent intent = new Intent();
+ intent.setClassName("com.android.dumprendertree2", "TestsListActivity");
+ intent.setAction(Intent.ACTION_RUN);
+ intent.putExtra(TestsListActivity.EXTRA_TEST_PATH, relativePath);
+ setActivityIntent(intent);
+ getActivity().registerOnEverythingFinishedCallback(new OnEverythingFinishedCallback() {
+ /** This method is safe to call on any thread */
+ @Override
+ public void onFinished() {
+ synchronized (Starter.this) {
+ mEverythingFinished = true;
+ Starter.this.notifyAll();
+ }
+ }
+ });
+
+ synchronized (this) {
+ while (!mEverythingFinished) {
+ try {
+ this.wait();
+ } catch (InterruptedException e) {
+ Log.e(LOG_TAG + "::startLayoutTests", e.getMessage());
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java
index 661a8ec02fb9..af0d7d166cbc 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ui/DirListActivity.java
@@ -378,6 +378,10 @@ public class DirListActivity extends ListActivity {
private ListItem[] getDirList(String dirPath) {
File dir = new File(mRootDirPath, dirPath);
+ if (!dir.exists()) {
+ return new ListItem[0];
+ }
+
List<ListItem> subDirs = new ArrayList<ListItem>();
List<ListItem> subFiles = new ArrayList<ListItem>();
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 73994f76264f..181b4c861ef7 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -171,5 +171,23 @@
</intent-filter>
</activity>
+ <activity
+ android:name="SimplePathsActivity"
+ android:label="_SimplePaths">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="AdvancedBlendActivity"
+ android:label="_AdvancedBlend">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
</application>
</manifest>
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/AdvancedBlendActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/AdvancedBlendActivity.java
new file mode 100644
index 000000000000..6c80a6d9dae3
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/AdvancedBlendActivity.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ComposeShader;
+import android.graphics.LinearGradient;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.Shader;
+import android.os.Bundle;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class AdvancedBlendActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(new ShadersView(this));
+ }
+
+ static class ShadersView extends View {
+ private BitmapShader mScaledShader;
+ private int mTexWidth;
+ private int mTexHeight;
+ private Paint mPaint;
+ private float mDrawWidth;
+ private float mDrawHeight;
+ private LinearGradient mHorGradient;
+ private ComposeShader mComposeShader;
+ private ComposeShader mCompose2Shader;
+ private ComposeShader mCompose3Shader;
+ private ComposeShader mCompose4Shader;
+ private ComposeShader mCompose5Shader;
+ private ComposeShader mCompose6Shader;
+ private BitmapShader mScaled2Shader;
+
+ ShadersView(Context c) {
+ super(c);
+
+ Bitmap texture = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
+ mTexWidth = texture.getWidth();
+ mTexHeight = texture.getHeight();
+ mDrawWidth = mTexWidth * 2.2f;
+ mDrawHeight = mTexHeight * 1.2f;
+
+ mScaledShader = new BitmapShader(texture, Shader.TileMode.MIRROR,
+ Shader.TileMode.MIRROR);
+ Matrix m2 = new Matrix();
+ m2.setScale(0.5f, 0.5f);
+ mScaledShader.setLocalMatrix(m2);
+
+ mScaled2Shader = new BitmapShader(texture, Shader.TileMode.MIRROR,
+ Shader.TileMode.MIRROR);
+ Matrix m3 = new Matrix();
+ m3.setScale(0.1f, 0.1f);
+ mScaled2Shader.setLocalMatrix(m3);
+
+ mHorGradient = new LinearGradient(0.0f, 0.0f, mDrawWidth, 0.0f,
+ Color.BLACK, Color.WHITE, Shader.TileMode.CLAMP);
+
+ mComposeShader = new ComposeShader(mScaledShader, mHorGradient,
+ PorterDuff.Mode.DARKEN);
+ mCompose2Shader = new ComposeShader(mScaledShader, mHorGradient,
+ PorterDuff.Mode.LIGHTEN);
+ mCompose3Shader = new ComposeShader(mScaledShader, mHorGradient,
+ PorterDuff.Mode.MULTIPLY);
+ mCompose4Shader = new ComposeShader(mScaledShader, mHorGradient,
+ PorterDuff.Mode.SCREEN);
+ mCompose5Shader = new ComposeShader(mScaledShader, mHorGradient,
+ PorterDuff.Mode.ADD);
+ mCompose6Shader = new ComposeShader(mHorGradient, mScaledShader,
+ PorterDuff.Mode.OVERLAY);
+
+ mPaint = new Paint();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ canvas.drawRGB(255, 255, 255);
+
+ canvas.save();
+ canvas.translate(40.0f, 40.0f);
+
+ mPaint.setShader(mComposeShader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.translate(0.0f, 40.0f + mDrawHeight);
+ mPaint.setShader(mCompose2Shader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.translate(0.0f, 40.0f + mDrawHeight);
+ mPaint.setShader(mCompose3Shader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.restore();
+
+ canvas.save();
+ canvas.translate(40.0f + mDrawWidth + 40.0f, 40.0f);
+
+ mPaint.setShader(mCompose4Shader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.translate(0.0f, 40.0f + mDrawHeight);
+ mPaint.setShader(mCompose5Shader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.translate(0.0f, 40.0f + mDrawHeight);
+ mPaint.setShader(mCompose6Shader);
+ canvas.drawRect(0.0f, 0.0f, mDrawWidth, mDrawHeight, mPaint);
+
+ canvas.restore();
+ }
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/SimplePathsActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/SimplePathsActivity.java
new file mode 100644
index 000000000000..071a118796aa
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/SimplePathsActivity.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.test.hwui;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.widget.EditText;
+import android.widget.FrameLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class SimplePathsActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ FrameLayout layout = new FrameLayout(this);
+ EditText text = new EditText(this);
+ layout.addView(text, new FrameLayout.LayoutParams(600, 350, Gravity.CENTER));
+ text.setText("This is an example of an EditText widget \n" +
+ "using simple paths to create the selection.");
+ //text.setSelection(0, text.getText().length());
+
+ setContentView(layout);
+ }
+} \ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java
index 3b5cf43e1bb4..59f665cad96a 100644
--- a/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java
+++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java
@@ -35,6 +35,7 @@ public class TextActivity extends Activity {
static class CustomTextView extends View {
private final Paint mMediumPaint;
private final Paint mLargePaint;
+ private final Paint mStrikePaint;
CustomTextView(Context c) {
super(c);
@@ -45,6 +46,11 @@ public class TextActivity extends Activity {
mLargePaint = new Paint();
mLargePaint.setAntiAlias(true);
mLargePaint.setTextSize(36.0f);
+ mStrikePaint = new Paint();
+ mStrikePaint.setAntiAlias(true);
+ mStrikePaint.setTextSize(16.0f);
+ mStrikePaint.setUnderlineText(true);
+
}
@Override
@@ -61,6 +67,15 @@ public class TextActivity extends Activity {
canvas.drawText("Hello OpenGL renderer!", 100, 100, mMediumPaint);
canvas.drawText("Hello OpenGL renderer!", 100, 200, mLargePaint);
+
+ canvas.drawText("Hello OpenGL renderer!", 500, 40, mStrikePaint);
+ mStrikePaint.setStrikeThruText(true);
+ canvas.drawText("Hello OpenGL renderer!", 500, 70, mStrikePaint);
+ mStrikePaint.setUnderlineText(false);
+ canvas.drawText("Hello OpenGL renderer!", 500, 100, mStrikePaint);
+ mStrikePaint.setStrikeThruText(false);
+ mStrikePaint.setUnderlineText(true);
+
canvas.save();
canvas.clipRect(150.0f, 220.0f, 450.0f, 320.0f);
canvas.drawText("Hello OpenGL renderer!", 100, 300, mLargePaint);
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index b339a2cd2eb0..094b7db3b4c4 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -41,7 +41,7 @@ LOCAL_STATIC_LIBRARIES := \
libpng
ifeq ($(HOST_OS),linux)
-LOCAL_LDLIBS += -lrt
+LOCAL_LDLIBS += -lrt -lpthread
endif
# Statically link libz for MinGW (Win SDK under Linux),
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas.java b/tools/layoutlib/bridge/src/android/graphics/Canvas.java
index d5d315ec19ef..1e1aba93db8d 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas.java
@@ -69,11 +69,6 @@ public class Canvas extends _Original_Canvas {
throw new UnsupportedOperationException("Can't create Canvas(int)");
}
- public Canvas(javax.microedition.khronos.opengles.GL gl) {
- mLogger = null;
- throw new UnsupportedOperationException("Can't create Canvas(javax.microedition.khronos.opengles.GL)");
- }
-
// custom constructors for our use.
public Canvas(int width, int height, ILayoutLog logger) {
mLogger = logger;
@@ -1174,15 +1169,6 @@ public class Canvas extends _Original_Canvas {
}
/* (non-Javadoc)
- * @see android.graphics.Canvas#getGL()
- */
- @Override
- public GL getGL() {
- // TODO Auto-generated method stub
- return super.getGL();
- }
-
- /* (non-Javadoc)
* @see android.graphics.Canvas#getMatrix()
*/
@Override
@@ -1257,15 +1243,6 @@ public class Canvas extends _Original_Canvas {
}
/* (non-Javadoc)
- * @see android.graphics.Canvas#setViewport(int, int)
- */
- @Override
- public void setViewport(int width, int height) {
- // TODO Auto-generated method stub
- super.setViewport(width, height);
- }
-
- /* (non-Javadoc)
* @see android.graphics.Canvas#skew(float, float)
*/
@Override
diff --git a/tools/localize/Android.mk b/tools/localize/Android.mk
index ab79f8dbdcfc..f284e865878a 100644
--- a/tools/localize/Android.mk
+++ b/tools/localize/Android.mk
@@ -34,7 +34,7 @@ LOCAL_STATIC_LIBRARIES := \
libcutils
ifeq ($(HOST_OS),linux)
-LOCAL_LDLIBS += -lrt
+LOCAL_LDLIBS += -lrt -lpthread
endif
diff --git a/tools/obbtool/Android.mk b/tools/obbtool/Android.mk
new file mode 100644
index 000000000000..b02c1cb13b63
--- /dev/null
+++ b/tools/obbtool/Android.mk
@@ -0,0 +1,30 @@
+#
+# Copyright 2010 The Android Open Source Project
+#
+# Opaque Binary Blob (OBB) Tool
+#
+
+# This tool is prebuilt if we're doing an app-only build.
+ifeq ($(TARGET_BUILD_APPS),)
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ Main.cpp
+
+#LOCAL_C_INCLUDES +=
+
+LOCAL_STATIC_LIBRARIES := \
+ libutils \
+ libcutils
+
+ifeq ($(HOST_OS),linux)
+LOCAL_LDLIBS += -lpthread
+endif
+
+LOCAL_MODULE := obbtool
+
+include $(BUILD_HOST_EXECUTABLE)
+
+endif # TARGET_BUILD_APPS
diff --git a/tools/obbtool/Main.cpp b/tools/obbtool/Main.cpp
new file mode 100644
index 000000000000..2a9bf0420102
--- /dev/null
+++ b/tools/obbtool/Main.cpp
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <utils/ObbFile.h>
+#include <utils/String8.h>
+
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+using namespace android;
+
+static const char* gProgName = "obbtool";
+static const char* gProgVersion = "1.0";
+
+static int wantUsage = 0;
+static int wantVersion = 0;
+
+#define ADD_OPTS "n:v:f:c:"
+static const struct option longopts[] = {
+ {"help", no_argument, &wantUsage, 1},
+ {"version", no_argument, &wantVersion, 1},
+
+ /* Args for "add" */
+ {"name", required_argument, NULL, 'n'},
+ {"version", required_argument, NULL, 'v'},
+ {"filesystem", required_argument, NULL, 'f'},
+ {"crypto", required_argument, NULL, 'c'},
+
+ {NULL, 0, NULL, '\0'}
+};
+
+struct package_info_t {
+ char* packageName;
+ int packageVersion;
+ char* filesystem;
+ char* crypto;
+};
+
+/*
+ * Print usage info.
+ */
+void usage(void)
+{
+ fprintf(stderr, "Opaque Binary Blob (OBB) Tool\n\n");
+ fprintf(stderr, "Usage:\n");
+ fprintf(stderr,
+ " %s a[dd] [ OPTIONS ] FILENAME\n"
+ " Adds an OBB signature to the file.\n\n", gProgName);
+ fprintf(stderr,
+ " %s r[emove] FILENAME\n"
+ " Removes the OBB signature from the file.\n\n", gProgName);
+ fprintf(stderr,
+ " %s i[nfo] FILENAME\n"
+ " Prints the OBB signature information of a file.\n\n", gProgName);
+}
+
+void doAdd(const char* filename, struct package_info_t* info) {
+ ObbFile *obb = new ObbFile();
+ if (obb->readFrom(filename)) {
+ fprintf(stderr, "ERROR: %s: OBB signature already present\n", filename);
+ return;
+ }
+
+ obb->setPackageName(String8(info->packageName));
+ obb->setVersion(info->packageVersion);
+
+ if (!obb->writeTo(filename)) {
+ fprintf(stderr, "ERROR: %s: couldn't write OBB signature: %s\n",
+ filename, strerror(errno));
+ return;
+ }
+
+ fprintf(stderr, "OBB signature successfully written\n");
+}
+
+void doRemove(const char* filename) {
+ ObbFile *obb = new ObbFile();
+ if (!obb->readFrom(filename)) {
+ fprintf(stderr, "ERROR: %s: no OBB signature present\n", filename);
+ return;
+ }
+
+ if (!obb->removeFrom(filename)) {
+ fprintf(stderr, "ERROR: %s: couldn't remove OBB signature\n", filename);
+ return;
+ }
+
+ fprintf(stderr, "OBB signature successfully removed\n");
+}
+
+void doInfo(const char* filename) {
+ ObbFile *obb = new ObbFile();
+ if (!obb->readFrom(filename)) {
+ fprintf(stderr, "ERROR: %s: couldn't read OBB signature\n", filename);
+ return;
+ }
+
+ printf("OBB info for '%s':\n", filename);
+ printf("Package name: %s\n", obb->getPackageName().string());
+ printf(" Version: %d\n", obb->getVersion());
+}
+
+/*
+ * Parse args.
+ */
+int main(int argc, char* const argv[])
+{
+ const char *prog = argv[0];
+ struct options *options;
+ int opt;
+ int option_index = 0;
+ struct package_info_t package_info;
+
+ int result = 1; // pessimistically assume an error.
+
+ if (argc < 2) {
+ wantUsage = 1;
+ goto bail;
+ }
+
+ while ((opt = getopt_long(argc, argv, ADD_OPTS, longopts, &option_index)) != -1) {
+ switch (opt) {
+ case 0:
+ if (longopts[option_index].flag)
+ break;
+ fprintf(stderr, "'%s' requires an argument\n", longopts[option_index].name);
+ wantUsage = 1;
+ goto bail;
+ case 'n':
+ package_info.packageName = optarg;
+ break;
+ case 'v':
+ char *end;
+ package_info.packageVersion = strtol(optarg, &end, 10);
+ if (*optarg == '\0' || *end != '\0') {
+ fprintf(stderr, "ERROR: invalid version; should be integer!\n\n");
+ wantUsage = 1;
+ goto bail;
+ }
+ break;
+ case 'f':
+ package_info.filesystem = optarg;
+ break;
+ case 'c':
+ package_info.crypto = optarg;
+ break;
+ case '?':
+ wantUsage = 1;
+ goto bail;
+ }
+ }
+
+ if (wantVersion) {
+ fprintf(stderr, "%s %s\n", gProgName, gProgVersion);
+ }
+
+ if (wantUsage) {
+ goto bail;
+ }
+
+#define CHECK_OP(name) \
+ if (strncmp(op, name, opsize)) { \
+ fprintf(stderr, "ERROR: unknown function '%s'!\n\n", op); \
+ wantUsage = 1; \
+ goto bail; \
+ }
+
+ if (optind < argc) {
+ const char* op = argv[optind++];
+ const int opsize = strlen(op);
+
+ if (optind >= argc) {
+ fprintf(stderr, "ERROR: filename required!\n\n");
+ wantUsage = 1;
+ goto bail;
+ }
+
+ const char* filename = argv[optind++];
+
+ switch (op[0]) {
+ case 'a':
+ CHECK_OP("add");
+ if (package_info.packageName == NULL) {
+ fprintf(stderr, "ERROR: arguments required 'packageName' and 'version'\n");
+ goto bail;
+ }
+ doAdd(filename, &package_info);
+ break;
+ case 'r':
+ CHECK_OP("remove");
+ doRemove(filename);
+ break;
+ case 'i':
+ CHECK_OP("info");
+ doInfo(filename);
+ break;
+ default:
+ fprintf(stderr, "ERROR: unknown command '%s'!\n\n", op);
+ wantUsage = 1;
+ goto bail;
+ }
+ }
+
+bail:
+ if (wantUsage) {
+ usage();
+ result = 2;
+ }
+
+ return result;
+}
diff --git a/voip/java/android/net/sip/BinderHelper.java b/voip/java/android/net/sip/BinderHelper.java
deleted file mode 100644
index bd3da32383c1..000000000000
--- a/voip/java/android/net/sip/BinderHelper.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.sip;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.ConditionVariable;
-import android.os.IBinder;
-import android.os.IInterface;
-import android.os.Looper;
-import android.util.Log;
-
-// TODO: throw away this class after moving SIP classes to framework
-// This class helps to get IBinder instance of a service in a blocking call.
-// The method cannot be called in app's main thread as the ServiceConnection
-// callback will.
-class BinderHelper<T extends IInterface> {
- private Context mContext;
- private IBinder mBinder;
- private Class<T> mClass;
-
- BinderHelper(Context context, Class<T> klass) {
- mContext = context;
- mClass = klass;
- }
-
- void startService() {
- mContext.startService(new Intent(mClass.getName()));
- }
-
- void stopService() {
- mContext.stopService(new Intent(mClass.getName()));
- }
-
- IBinder getBinder() {
- // cannot call this method in app's main thread
- if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
- throw new RuntimeException(
- "This method cannot be called in app's main thread");
- }
-
- final ConditionVariable cv = new ConditionVariable();
- cv.close();
- ServiceConnection c = new ServiceConnection() {
- public synchronized void onServiceConnected(
- ComponentName className, IBinder binder) {
- Log.v("BinderHelper", "service connected!");
- mBinder = binder;
- cv.open();
- mContext.unbindService(this);
- }
-
- public void onServiceDisconnected(ComponentName className) {
- cv.open();
- mContext.unbindService(this);
- }
- };
- if (mContext.bindService(new Intent(mClass.getName()), c, 0)) {
- cv.block(4500);
- }
- return mBinder;
- }
-}
diff --git a/voip/java/android/net/sip/SipAudioCallImpl.java b/voip/java/android/net/sip/SipAudioCallImpl.java
index 57e0bd2790c4..474bc4b198d0 100644
--- a/voip/java/android/net/sip/SipAudioCallImpl.java
+++ b/voip/java/android/net/sip/SipAudioCallImpl.java
@@ -343,8 +343,11 @@ public class SipAudioCallImpl extends SipSessionAdapter
public synchronized void endCall() throws SipException {
try {
stopRinging();
- if (mSipSession != null) mSipSession.endCall();
stopCall(true);
+ mInCall = false;
+
+ // perform the above local ops first and then network op
+ if (mSipSession != null) mSipSession.endCall();
} catch (Throwable e) {
throwSipException(e);
}
diff --git a/voip/java/android/net/sip/SipManager.java b/voip/java/android/net/sip/SipManager.java
index f28b41cc5ed7..287a13ad3bd7 100644
--- a/voip/java/android/net/sip/SipManager.java
+++ b/voip/java/android/net/sip/SipManager.java
@@ -68,9 +68,6 @@ public class SipManager {
private ISipService mSipService;
- // Will be removed once the SIP service is integrated into framework
- private BinderHelper<ISipService> mBinderHelper;
-
/**
* Creates a manager instance and initializes the background SIP service.
* Will be removed once the SIP service is integrated into framework.
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 6e0bc9d04f3f..3426af7019af 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -85,5 +85,13 @@ interface IWifiManager
WifiConfiguration getWifiApConfiguration();
void setWifiApConfiguration(in WifiConfiguration wifiConfig);
+
+ void startWifi();
+
+ void stopWifi();
+
+ void addToBlacklist(String bssid);
+
+ void clearBlacklist();
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 6fac902bdd0a..339763a3c90d 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -293,6 +293,21 @@ public class WifiManager {
public static final String EXTRA_NEW_RSSI = "newRssi";
/**
+ * Broadcast intent action indicating that the IP configuration
+ * changed on wifi.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String CONFIG_CHANGED_ACTION = "android.net.wifi.CONFIG_CHANGED";
+ /**
+ * The lookup key for a {@link android.net.NetworkProperties} object associated with the
+ * Wi-Fi network. Retrieve with
+ * {@link android.content.Intent#getParcelableExtra(String)}.
+ * @hide
+ */
+ public static final String EXTRA_NETWORK_PROPERTIES = "networkProperties";
+
+ /**
* The network IDs of the configured networks could have changed.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@@ -838,6 +853,82 @@ public class WifiManager {
}
}
+ /**
+ * Start the driver and connect to network.
+ *
+ * This function will over-ride WifiLock and device idle status. For example,
+ * even if the device is idle or there is only a scan-only lock held,
+ * a start wifi would mean that wifi connection is kept active until
+ * a stopWifi() is sent.
+ *
+ * This API is used by WifiStateTracker
+ *
+ * @return {@code true} if the operation succeeds else {@code false}
+ * @hide
+ */
+ public boolean startWifi() {
+ try {
+ mService.startWifi();
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Disconnect from a network (if any) and stop the driver.
+ *
+ * This function will over-ride WifiLock and device idle status. Wi-Fi
+ * stays inactive until a startWifi() is issued.
+ *
+ * This API is used by WifiStateTracker
+ *
+ * @return {@code true} if the operation succeeds else {@code false}
+ * @hide
+ */
+ public boolean stopWifi() {
+ try {
+ mService.stopWifi();
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Add a bssid to the supplicant blacklist
+ *
+ * This API is used by WifiWatchdogService
+ *
+ * @return {@code true} if the operation succeeds else {@code false}
+ * @hide
+ */
+ public boolean addToBlacklist(String bssid) {
+ try {
+ mService.addToBlacklist(bssid);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Clear the supplicant blacklist
+ *
+ * This API is used by WifiWatchdogService
+ *
+ * @return {@code true} if the operation succeeds else {@code false}
+ * @hide
+ */
+ public boolean clearBlacklist() {
+ try {
+ mService.clearBlacklist();
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
/**
* Allows an application to keep the Wi-Fi radio awake.
* Normally the Wi-Fi radio may turn off when the user has not used the device in a while.
diff --git a/wifi/java/android/net/wifi/WifiMonitor.java b/wifi/java/android/net/wifi/WifiMonitor.java
index f2f83438e4ed..af3132ff402a 100644
--- a/wifi/java/android/net/wifi/WifiMonitor.java
+++ b/wifi/java/android/net/wifi/WifiMonitor.java
@@ -19,14 +19,13 @@ package android.net.wifi;
import android.util.Log;
import android.util.Config;
import android.net.NetworkInfo;
-import android.net.NetworkStateTracker;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
/**
* Listens for events from the wpa_supplicant server, and passes them on
- * to the {@link WifiStateTracker} for handling. Runs in its own thread.
+ * to the {@link WifiStateMachine} for handling. Runs in its own thread.
*
* @hide
*/
@@ -117,7 +116,7 @@ public class WifiMonitor {
private static Pattern mConnectedEventPattern =
Pattern.compile("((?:[0-9a-f]{2}:){5}[0-9a-f]{2}) .* \\[id=([0-9]+) ");
- private final WifiStateTracker mWifiStateTracker;
+ private final WifiStateMachine mWifiStateMachine;
/**
* This indicates the supplicant connection for the monitor is closed
@@ -139,18 +138,14 @@ public class WifiMonitor {
*/
private static final int MAX_RECV_ERRORS = 10;
- public WifiMonitor(WifiStateTracker tracker) {
- mWifiStateTracker = tracker;
+ public WifiMonitor(WifiStateMachine wifiStateMachine) {
+ mWifiStateMachine = wifiStateMachine;
}
public void startMonitoring() {
new MonitorThread().start();
}
- public NetworkStateTracker getNetworkStateTracker() {
- return mWifiStateTracker;
- }
-
class MonitorThread extends Thread {
public MonitorThread() {
super("WifiMonitor");
@@ -161,9 +156,9 @@ public class WifiMonitor {
if (connectToSupplicant()) {
// Send a message indicating that it is now possible to send commands
// to the supplicant
- mWifiStateTracker.notifySupplicantConnection();
+ mWifiStateMachine.notifySupplicantConnection();
} else {
- mWifiStateTracker.notifySupplicantLost();
+ mWifiStateMachine.notifySupplicantLost();
return;
}
@@ -259,7 +254,7 @@ public class WifiMonitor {
}
// notify and exit
- mWifiStateTracker.notifySupplicantLost();
+ mWifiStateMachine.notifySupplicantLost();
break;
} else {
handleEvent(event, eventData);
@@ -285,7 +280,7 @@ public class WifiMonitor {
}
private void handlePasswordKeyMayBeIncorrect() {
- mWifiStateTracker.notifyPasswordKeyMayBeIncorrect();
+ mWifiStateMachine.notifyPasswordKeyMayBeIncorrect();
}
private void handleDriverEvent(String state) {
@@ -293,11 +288,11 @@ public class WifiMonitor {
return;
}
if (state.equals("STOPPED")) {
- mWifiStateTracker.notifyDriverStopped();
+ mWifiStateMachine.notifyDriverStopped();
} else if (state.equals("STARTED")) {
- mWifiStateTracker.notifyDriverStarted();
+ mWifiStateMachine.notifyDriverStarted();
} else if (state.equals("HANGED")) {
- mWifiStateTracker.notifyDriverHung();
+ mWifiStateMachine.notifyDriverHung();
}
}
@@ -318,7 +313,7 @@ public class WifiMonitor {
break;
case SCAN_RESULTS:
- mWifiStateTracker.notifyScanResultsAvailable();
+ mWifiStateMachine.notifyScanResultsAvailable();
break;
case UNKNOWN:
@@ -375,7 +370,7 @@ public class WifiMonitor {
if (newSupplicantState == SupplicantState.INVALID) {
Log.w(TAG, "Invalid supplicant state: " + newState);
}
- mWifiStateTracker.notifySupplicantStateChange(networkId, BSSID, newSupplicantState);
+ mWifiStateMachine.notifySupplicantStateChange(networkId, BSSID, newSupplicantState);
}
}
@@ -395,7 +390,7 @@ public class WifiMonitor {
}
}
}
- mWifiStateTracker.notifyNetworkStateChange(newState, BSSID, networkId);
+ mWifiStateMachine.notifyNetworkStateChange(newState, BSSID, networkId);
}
/**
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
new file mode 100644
index 000000000000..845508b4bae1
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -0,0 +1,3571 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
+import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING;
+import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
+import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
+import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
+
+/**
+ * TODO: Add soft AP states as part of WIFI_STATE_XXX
+ * Retain WIFI_STATE_ENABLING that indicates driver is loading
+ * Add WIFI_STATE_AP_ENABLED to indicate soft AP has started
+ * and WIFI_STATE_FAILED for failure
+ * Deprecate WIFI_STATE_UNKNOWN
+ *
+ * Doing this will simplify the logic for sending broadcasts
+ */
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
+
+import android.app.ActivityManagerNative;
+import android.net.NetworkInfo;
+import android.net.DhcpInfo;
+import android.net.NetworkUtils;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkProperties;
+import android.os.Binder;
+import android.os.Message;
+import android.os.Parcelable;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.INetworkManagementService;
+import android.os.PowerManager;
+import android.os.SystemProperties;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.Process;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.Slog;
+import android.app.backup.IBackupManager;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothA2dp;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.Context;
+import android.database.ContentObserver;
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.util.HierarchicalState;
+import com.android.internal.util.HierarchicalStateMachine;
+
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.Pattern;
+
+/**
+ * Track the state of Wifi connectivity. All event handling is done here,
+ * and all changes in connectivity state are initiated here.
+ *
+ * @hide
+ */
+//TODO: we still need frequent scanning for the case when
+// we issue disconnect but need scan results for open network notification
+public class WifiStateMachine extends HierarchicalStateMachine {
+
+ private static final String TAG = "WifiStateMachine";
+ private static final String NETWORKTYPE = "WIFI";
+ private static final boolean DBG = false;
+
+ /* TODO: fetch a configurable interface */
+ private static final String SOFTAP_IFACE = "wl0.1";
+
+ private WifiMonitor mWifiMonitor;
+ private INetworkManagementService nwService;
+ private ConnectivityManager mCm;
+
+ /* Scan results handling */
+ private List<ScanResult> mScanResults;
+ private static final Pattern scanResultPattern = Pattern.compile("\t+");
+ private static final int SCAN_RESULT_CACHE_SIZE = 80;
+ private final LinkedHashMap<String, ScanResult> mScanResultCache;
+
+ private String mInterfaceName;
+
+ private int mNumAllowedChannels = 0;
+ private int mLastSignalLevel = -1;
+ private String mLastBssid;
+ private int mLastNetworkId;
+ private boolean mEnableRssiPolling = false;
+ private boolean mPasswordKeyMayBeIncorrect = false;
+ private boolean mUseStaticIp = false;
+ private int mReconnectCount = 0;
+ private boolean mIsScanMode = false;
+ private boolean mConfigChanged = false;
+
+ /**
+ * Instance of the bluetooth headset helper. This needs to be created
+ * early because there is a delay before it actually 'connects', as
+ * noted by its javadoc. If we check before it is connected, it will be
+ * in an error state and we will not disable coexistence.
+ */
+ private BluetoothHeadset mBluetoothHeadset;
+
+ private BluetoothA2dp mBluetoothA2dp;
+
+ /**
+ * Observes the static IP address settings.
+ */
+ private SettingsObserver mSettingsObserver;
+ private NetworkProperties mNetworkProperties;
+
+ // Held during driver load and unload
+ private static PowerManager.WakeLock sWakeLock;
+
+ private Context mContext;
+
+ private DhcpInfo mDhcpInfo;
+ private WifiInfo mWifiInfo;
+ private NetworkInfo mNetworkInfo;
+ private SupplicantStateTracker mSupplicantStateTracker;
+
+ // Event log tags (must be in sync with event-log-tags)
+ private static final int EVENTLOG_WIFI_STATE_CHANGED = 50021;
+ private static final int EVENTLOG_WIFI_EVENT_HANDLED = 50022;
+ private static final int EVENTLOG_SUPPLICANT_STATE_CHANGED = 50023;
+
+ /* Load the driver */
+ private static final int CMD_LOAD_DRIVER = 1;
+ /* Unload the driver */
+ private static final int CMD_UNLOAD_DRIVER = 2;
+ /* Indicates driver load succeeded */
+ private static final int CMD_LOAD_DRIVER_SUCCESS = 3;
+ /* Indicates driver load failed */
+ private static final int CMD_LOAD_DRIVER_FAILURE = 4;
+ /* Indicates driver unload succeeded */
+ private static final int CMD_UNLOAD_DRIVER_SUCCESS = 5;
+ /* Indicates driver unload failed */
+ private static final int CMD_UNLOAD_DRIVER_FAILURE = 6;
+
+ /* Start the supplicant */
+ private static final int CMD_START_SUPPLICANT = 11;
+ /* Stop the supplicant */
+ private static final int CMD_STOP_SUPPLICANT = 12;
+ /* Start the driver */
+ private static final int CMD_START_DRIVER = 13;
+ /* Start the driver */
+ private static final int CMD_STOP_DRIVER = 14;
+ /* Indicates DHCP succeded */
+ private static final int CMD_IP_CONFIG_SUCCESS = 15;
+ /* Indicates DHCP failed */
+ private static final int CMD_IP_CONFIG_FAILURE = 16;
+ /* Re-configure interface */
+ private static final int CMD_RECONFIGURE_IP = 17;
+
+
+ /* Start the soft access point */
+ private static final int CMD_START_AP = 21;
+ /* Stop the soft access point */
+ private static final int CMD_STOP_AP = 22;
+
+
+ /* Supplicant events */
+ /* Connection to supplicant established */
+ private static final int SUP_CONNECTION_EVENT = 31;
+ /* Connection to supplicant lost */
+ private static final int SUP_DISCONNECTION_EVENT = 32;
+ /* Driver start completed */
+ private static final int DRIVER_START_EVENT = 33;
+ /* Driver stop completed */
+ private static final int DRIVER_STOP_EVENT = 34;
+ /* Network connection completed */
+ private static final int NETWORK_CONNECTION_EVENT = 36;
+ /* Network disconnection completed */
+ private static final int NETWORK_DISCONNECTION_EVENT = 37;
+ /* Scan results are available */
+ private static final int SCAN_RESULTS_EVENT = 38;
+ /* Supplicate state changed */
+ private static final int SUPPLICANT_STATE_CHANGE_EVENT = 39;
+ /* Password may be incorrect */
+ private static final int PASSWORD_MAY_BE_INCORRECT_EVENT = 40;
+
+ /* Supplicant commands */
+ /* Is supplicant alive ? */
+ private static final int CMD_PING_SUPPLICANT = 51;
+ /* Add/update a network configuration */
+ private static final int CMD_ADD_OR_UPDATE_NETWORK = 52;
+ /* Delete a network */
+ private static final int CMD_REMOVE_NETWORK = 53;
+ /* Enable a network. The device will attempt a connection to the given network. */
+ private static final int CMD_ENABLE_NETWORK = 54;
+ /* Disable a network. The device does not attempt a connection to the given network. */
+ private static final int CMD_DISABLE_NETWORK = 55;
+ /* Blacklist network. De-prioritizes the given BSSID for connection. */
+ private static final int CMD_BLACKLIST_NETWORK = 56;
+ /* Clear the blacklist network list */
+ private static final int CMD_CLEAR_BLACKLIST = 57;
+ /* Get the configured networks */
+ private static final int CMD_GET_NETWORK_CONFIG = 58;
+ /* Save configuration */
+ private static final int CMD_SAVE_CONFIG = 59;
+ /* Connection status */
+ private static final int CMD_CONNECTION_STATUS = 60;
+
+ /* Supplicant commands after driver start*/
+ /* Initiate a scan */
+ private static final int CMD_START_SCAN = 71;
+ /* Set scan mode. CONNECT_MODE or SCAN_ONLY_MODE */
+ private static final int CMD_SET_SCAN_MODE = 72;
+ /* Set scan type. SCAN_ACTIVE or SCAN_PASSIVE */
+ private static final int CMD_SET_SCAN_TYPE = 73;
+ /* Disconnect from a network */
+ private static final int CMD_DISCONNECT = 74;
+ /* Reconnect to a network */
+ private static final int CMD_RECONNECT = 75;
+ /* Reassociate to a network */
+ private static final int CMD_REASSOCIATE = 76;
+ /* Set power mode
+ * POWER_MODE_ACTIVE
+ * POWER_MODE_AUTO
+ */
+ private static final int CMD_SET_POWER_MODE = 77;
+ /* Set bluetooth co-existence
+ * BLUETOOTH_COEXISTENCE_MODE_ENABLED
+ * BLUETOOTH_COEXISTENCE_MODE_DISABLED
+ * BLUETOOTH_COEXISTENCE_MODE_SENSE
+ */
+ private static final int CMD_SET_BLUETOOTH_COEXISTENCE = 78;
+ /* Enable/disable bluetooth scan mode
+ * true(1)
+ * false(0)
+ */
+ private static final int CMD_SET_BLUETOOTH_SCAN_MODE = 79;
+ /* Set number of allowed channels */
+ private static final int CMD_SET_NUM_ALLOWED_CHANNELS = 80;
+ /* Request connectivity manager wake lock before driver stop */
+ private static final int CMD_REQUEST_CM_WAKELOCK = 81;
+ /* Enables RSSI poll */
+ private static final int CMD_ENABLE_RSSI_POLL = 82;
+ /* RSSI poll */
+ private static final int CMD_RSSI_POLL = 83;
+ /* Get current RSSI */
+ private static final int CMD_GET_RSSI = 84;
+ /* Get approx current RSSI */
+ private static final int CMD_GET_RSSI_APPROX = 85;
+ /* Get link speed on connection */
+ private static final int CMD_GET_LINK_SPEED = 86;
+ /* Radio mac address */
+ private static final int CMD_GET_MAC_ADDR = 87;
+ /* Set up packet filtering */
+ private static final int CMD_START_PACKET_FILTERING = 88;
+ /* Clear packet filter */
+ private static final int CMD_STOP_PACKET_FILTERING = 89;
+
+ /**
+ * Interval in milliseconds between polling for connection
+ * status items that are not sent via asynchronous events.
+ * An example is RSSI (signal strength).
+ */
+ private static final int POLL_RSSI_INTERVAL_MSECS = 3000;
+
+ private static final int CONNECT_MODE = 1;
+ private static final int SCAN_ONLY_MODE = 2;
+
+ private static final int SCAN_ACTIVE = 1;
+ private static final int SCAN_PASSIVE = 2;
+
+ /**
+ * The maximum number of times we will retry a connection to an access point
+ * for which we have failed in acquiring an IP address from DHCP. A value of
+ * N means that we will make N+1 connection attempts in all.
+ * <p>
+ * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
+ * value if a Settings value is not present.
+ */
+ private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
+
+ private static final int DRIVER_POWER_MODE_ACTIVE = 1;
+ private static final int DRIVER_POWER_MODE_AUTO = 0;
+
+ /* Default parent state */
+ private HierarchicalState mDefaultState = new DefaultState();
+ /* Temporary initial state */
+ private HierarchicalState mInitialState = new InitialState();
+ /* Unloading the driver */
+ private HierarchicalState mDriverUnloadingState = new DriverUnloadingState();
+ /* Loading the driver */
+ private HierarchicalState mDriverUnloadedState = new DriverUnloadedState();
+ /* Driver load/unload failed */
+ private HierarchicalState mDriverFailedState = new DriverFailedState();
+ /* Driver loading */
+ private HierarchicalState mDriverLoadingState = new DriverLoadingState();
+ /* Driver loaded */
+ private HierarchicalState mDriverLoadedState = new DriverLoadedState();
+ /* Driver loaded, waiting for supplicant to start */
+ private HierarchicalState mWaitForSupState = new WaitForSupState();
+
+ /* Driver loaded and supplicant ready */
+ private HierarchicalState mDriverSupReadyState = new DriverSupReadyState();
+ /* Driver start issued, waiting for completed event */
+ private HierarchicalState mDriverStartingState = new DriverStartingState();
+ /* Driver started */
+ private HierarchicalState mDriverStartedState = new DriverStartedState();
+ /* Driver stopping */
+ private HierarchicalState mDriverStoppingState = new DriverStoppingState();
+ /* Driver stopped */
+ private HierarchicalState mDriverStoppedState = new DriverStoppedState();
+ /* Scan for networks, no connection will be established */
+ private HierarchicalState mScanModeState = new ScanModeState();
+ /* Connecting to an access point */
+ private HierarchicalState mConnectModeState = new ConnectModeState();
+ /* Fetching IP after network connection (assoc+auth complete) */
+ private HierarchicalState mConnectingState = new ConnectingState();
+ /* Connected with IP addr */
+ private HierarchicalState mConnectedState = new ConnectedState();
+ /* disconnect issued, waiting for network disconnect confirmation */
+ private HierarchicalState mDisconnectingState = new DisconnectingState();
+ /* Network is not connected, supplicant assoc+auth is not complete */
+ private HierarchicalState mDisconnectedState = new DisconnectedState();
+
+ /* Soft Ap is running */
+ private HierarchicalState mSoftApStartedState = new SoftApStartedState();
+
+ /* Argument for Message object to indicate a synchronous call */
+ private static final int SYNCHRONOUS_CALL = 1;
+ private static final int ASYNCHRONOUS_CALL = 0;
+
+
+ /**
+ * One of {@link WifiManager#WIFI_STATE_DISABLED},
+ * {@link WifiManager#WIFI_STATE_DISABLING},
+ * {@link WifiManager#WIFI_STATE_ENABLED},
+ * {@link WifiManager#WIFI_STATE_ENABLING},
+ * {@link WifiManager#WIFI_STATE_UNKNOWN}
+ *
+ */
+ private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_DISABLED);
+
+ /**
+ * One of {@link WifiManager#WIFI_AP_STATE_DISABLED},
+ * {@link WifiManager#WIFI_AP_STATE_DISABLING},
+ * {@link WifiManager#WIFI_AP_STATE_ENABLED},
+ * {@link WifiManager#WIFI_AP_STATE_ENABLING},
+ * {@link WifiManager#WIFI_AP_STATE_FAILED}
+ *
+ */
+ private final AtomicInteger mWifiApState = new AtomicInteger(WIFI_AP_STATE_DISABLED);
+
+ private final AtomicInteger mLastEnableUid = new AtomicInteger(Process.myUid());
+ private final AtomicInteger mLastApEnableUid = new AtomicInteger(Process.myUid());
+
+ private final IBatteryStats mBatteryStats;
+
+ public WifiStateMachine(Context context) {
+ super(TAG);
+
+ mContext = context;
+
+ mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
+ mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
+
+ IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ nwService = INetworkManagementService.Stub.asInterface(b);
+
+ mWifiMonitor = new WifiMonitor(this);
+ mDhcpInfo = new DhcpInfo();
+ mWifiInfo = new WifiInfo();
+ mInterfaceName = SystemProperties.get("wifi.interface", "tiwlan0");
+ mSupplicantStateTracker = new SupplicantStateTracker(context, getHandler());
+
+ mBluetoothHeadset = new BluetoothHeadset(mContext, null);
+ mNetworkProperties = new NetworkProperties();
+
+ mNetworkInfo.setIsAvailable(false);
+ mNetworkProperties.clear();
+ mLastBssid = null;
+ mLastNetworkId = -1;
+ mLastSignalLevel = -1;
+
+ mScanResultCache = new LinkedHashMap<String, ScanResult>(
+ SCAN_RESULT_CACHE_SIZE, 0.75f, true) {
+ /*
+ * Limit the cache size by SCAN_RESULT_CACHE_SIZE
+ * elements
+ */
+ @Override
+ public boolean removeEldestEntry(Map.Entry eldest) {
+ return SCAN_RESULT_CACHE_SIZE < this.size();
+ }
+ };
+
+ mSettingsObserver = new SettingsObserver(new Handler());
+
+ PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+ sWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+
+ addState(mDefaultState);
+ addState(mInitialState, mDefaultState);
+ addState(mDriverUnloadingState, mDefaultState);
+ addState(mDriverUnloadedState, mDefaultState);
+ addState(mDriverFailedState, mDriverUnloadedState);
+ addState(mDriverLoadingState, mDefaultState);
+ addState(mDriverLoadedState, mDefaultState);
+ addState(mWaitForSupState, mDriverLoadedState);
+ addState(mDriverSupReadyState, mDefaultState);
+ addState(mDriverStartingState, mDriverSupReadyState);
+ addState(mDriverStartedState, mDriverSupReadyState);
+ addState(mScanModeState, mDriverStartedState);
+ addState(mConnectModeState, mDriverStartedState);
+ addState(mConnectingState, mConnectModeState);
+ addState(mConnectedState, mConnectModeState);
+ addState(mDisconnectingState, mConnectModeState);
+ addState(mDisconnectedState, mConnectModeState);
+ addState(mDriverStoppingState, mDriverSupReadyState);
+ addState(mDriverStoppedState, mDriverSupReadyState);
+ addState(mSoftApStartedState, mDefaultState);
+
+ setInitialState(mInitialState);
+
+ if (DBG) setDbg(true);
+
+ //start the state machine
+ start();
+ }
+
+ /*********************************************************
+ * Methods exposed for public use
+ ********************************************************/
+
+ /**
+ * TODO: doc
+ */
+ public boolean pingSupplicant() {
+ return sendSyncMessage(CMD_PING_SUPPLICANT).boolValue;
+ }
+
+ /**
+ * TODO: doc
+ */
+ public boolean startScan(boolean forceActive) {
+ return sendSyncMessage(obtainMessage(CMD_START_SCAN, forceActive ?
+ SCAN_ACTIVE : SCAN_PASSIVE, 0)).boolValue;
+ }
+
+ /**
+ * TODO: doc
+ */
+ public void setWifiEnabled(boolean enable) {
+ mLastEnableUid.set(Binder.getCallingUid());
+ if (enable) {
+ /* Argument is the state that is entered prior to load */
+ sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_STATE_ENABLING, 0));
+ sendMessage(CMD_START_SUPPLICANT);
+ } else {
+ sendMessage(CMD_STOP_SUPPLICANT);
+ /* Argument is the state that is entered upon success */
+ sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_DISABLED, 0));
+ }
+ }
+
+ /**
+ * TODO: doc
+ */
+ public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enable) {
+ mLastApEnableUid.set(Binder.getCallingUid());
+ if (enable) {
+ /* Argument is the state that is entered prior to load */
+ sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_AP_STATE_ENABLING, 0));
+ sendMessage(obtainMessage(CMD_START_AP, wifiConfig));
+ } else {
+ sendMessage(CMD_STOP_AP);
+ /* Argument is the state that is entered upon success */
+ sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_DISABLED, 0));
+ }
+ }
+
+ /**
+ * TODO: doc
+ */
+ public int getWifiState() {
+ return mWifiState.get();
+ }
+
+ /**
+ * TODO: doc
+ */
+ public String getWifiStateByName() {
+ switch (mWifiState.get()) {
+ case WIFI_STATE_DISABLING:
+ return "disabling";
+ case WIFI_STATE_DISABLED:
+ return "disabled";
+ case WIFI_STATE_ENABLING:
+ return "enabling";
+ case WIFI_STATE_ENABLED:
+ return "enabled";
+ case WIFI_STATE_UNKNOWN:
+ return "unknown state";
+ default:
+ return "[invalid state]";
+ }
+ }
+
+ /**
+ * TODO: doc
+ */
+ public int getWifiApState() {
+ return mWifiApState.get();
+ }
+
+ /**
+ * TODO: doc
+ */
+ public String getWifiApStateByName() {
+ switch (mWifiApState.get()) {
+ case WIFI_AP_STATE_DISABLING:
+ return "disabling";
+ case WIFI_AP_STATE_DISABLED:
+ return "disabled";
+ case WIFI_AP_STATE_ENABLING:
+ return "enabling";
+ case WIFI_AP_STATE_ENABLED:
+ return "enabled";
+ case WIFI_AP_STATE_FAILED:
+ return "failed";
+ default:
+ return "[invalid state]";
+ }
+ }
+
+ /**
+ * Get status information for the current connection, if any.
+ * @return a {@link WifiInfo} object containing information about the current connection
+ *
+ */
+ public WifiInfo requestConnectionInfo() {
+ return mWifiInfo;
+ }
+
+ public DhcpInfo getDhcpInfo() {
+ return mDhcpInfo;
+ }
+
+ /**
+ * TODO: doc
+ */
+ public void setDriverStart(boolean enable) {
+ if (enable) {
+ sendMessage(CMD_START_DRIVER);
+ } else {
+ sendMessage(CMD_STOP_DRIVER);
+ }
+ }
+
+ /**
+ * TODO: doc
+ */
+ public void setScanOnlyMode(boolean enable) {
+ if (enable) {
+ sendMessage(obtainMessage(CMD_SET_SCAN_MODE, SCAN_ONLY_MODE, 0));
+ } else {
+ sendMessage(obtainMessage(CMD_SET_SCAN_MODE, CONNECT_MODE, 0));
+ }
+ }
+
+ /**
+ * TODO: doc
+ */
+ public void setScanType(boolean active) {
+ if (active) {
+ sendMessage(obtainMessage(CMD_SET_SCAN_TYPE, SCAN_ACTIVE, 0));
+ } else {
+ sendMessage(obtainMessage(CMD_SET_SCAN_TYPE, SCAN_PASSIVE, 0));
+ }
+ }
+
+ /**
+ * TODO: doc
+ */
+ public List<ScanResult> getScanResultsList() {
+ return mScanResults;
+ }
+
+ /**
+ * Disconnect from Access Point
+ */
+ public boolean disconnectCommand() {
+ return sendSyncMessage(CMD_DISCONNECT).boolValue;
+ }
+
+ /**
+ * Initiate a reconnection to AP
+ */
+ public boolean reconnectCommand() {
+ return sendSyncMessage(CMD_RECONNECT).boolValue;
+ }
+
+ /**
+ * Initiate a re-association to AP
+ */
+ public boolean reassociateCommand() {
+ return sendSyncMessage(CMD_REASSOCIATE).boolValue;
+ }
+
+ /**
+ * Add a network synchronously
+ *
+ * @return network id of the new network
+ */
+ public int addOrUpdateNetwork(WifiConfiguration config) {
+ return sendSyncMessage(CMD_ADD_OR_UPDATE_NETWORK, config).intValue;
+ }
+
+ public List<WifiConfiguration> getConfiguredNetworks() {
+ return sendSyncMessage(CMD_GET_NETWORK_CONFIG).configList;
+ }
+
+ /**
+ * Delete a network
+ *
+ * @param networkId id of the network to be removed
+ */
+ public boolean removeNetwork(int networkId) {
+ return sendSyncMessage(obtainMessage(CMD_REMOVE_NETWORK, networkId, 0)).boolValue;
+ }
+
+ private class EnableNetParams {
+ private int netId;
+ private boolean disableOthers;
+ EnableNetParams(int n, boolean b) {
+ netId = n;
+ disableOthers = b;
+ }
+ }
+ /**
+ * Enable a network
+ *
+ * @param netId network id of the network
+ * @param disableOthers true, if all other networks have to be disabled
+ * @return {@code true} if the operation succeeds, {@code false} otherwise
+ */
+ public boolean enableNetwork(int netId, boolean disableOthers) {
+ return sendSyncMessage(CMD_ENABLE_NETWORK,
+ new EnableNetParams(netId, disableOthers)).boolValue;
+ }
+
+ /**
+ * Disable a network
+ *
+ * @param netId network id of the network
+ * @return {@code true} if the operation succeeds, {@code false} otherwise
+ */
+ public boolean disableNetwork(int netId) {
+ return sendSyncMessage(obtainMessage(CMD_DISABLE_NETWORK, netId, 0)).boolValue;
+ }
+
+ /**
+ * Blacklist a BSSID. This will avoid the AP if there are
+ * alternate APs to connect
+ *
+ * @param bssid BSSID of the network
+ */
+ public void addToBlacklist(String bssid) {
+ sendMessage(obtainMessage(CMD_BLACKLIST_NETWORK, bssid));
+ }
+
+ /**
+ * Clear the blacklist list
+ *
+ */
+ public void clearBlacklist() {
+ sendMessage(obtainMessage(CMD_CLEAR_BLACKLIST));
+ }
+
+ /**
+ * Get detailed status of the connection
+ *
+ * @return Example status result
+ * bssid=aa:bb:cc:dd:ee:ff
+ * ssid=TestNet
+ * id=3
+ * pairwise_cipher=NONE
+ * group_cipher=NONE
+ * key_mgmt=NONE
+ * wpa_state=COMPLETED
+ * ip_address=X.X.X.X
+ */
+ public String status() {
+ return sendSyncMessage(CMD_CONNECTION_STATUS).stringValue;
+ }
+
+ public void enableRssiPolling(boolean enabled) {
+ sendMessage(obtainMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0));
+ }
+ /**
+ * Get RSSI to currently connected network
+ *
+ * @return RSSI value, -1 on failure
+ */
+ public int getRssi() {
+ return sendSyncMessage(CMD_GET_RSSI).intValue;
+ }
+
+ /**
+ * Get approx RSSI to currently connected network
+ *
+ * @return RSSI value, -1 on failure
+ */
+ public int getRssiApprox() {
+ return sendSyncMessage(CMD_GET_RSSI_APPROX).intValue;
+ }
+
+ /**
+ * Get link speed to currently connected network
+ *
+ * @return link speed, -1 on failure
+ */
+ public int getLinkSpeed() {
+ return sendSyncMessage(CMD_GET_LINK_SPEED).intValue;
+ }
+
+ /**
+ * Get MAC address of radio
+ *
+ * @return MAC address, null on failure
+ */
+ public String getMacAddress() {
+ return sendSyncMessage(CMD_GET_MAC_ADDR).stringValue;
+ }
+
+ /**
+ * Start packet filtering
+ */
+ public void startPacketFiltering() {
+ sendMessage(CMD_START_PACKET_FILTERING);
+ }
+
+ /**
+ * Stop packet filtering
+ */
+ public void stopPacketFiltering() {
+ sendMessage(CMD_STOP_PACKET_FILTERING);
+ }
+
+ /**
+ * Set power mode
+ * @param mode
+ * DRIVER_POWER_MODE_AUTO
+ * DRIVER_POWER_MODE_ACTIVE
+ */
+ public void setPowerMode(int mode) {
+ sendMessage(obtainMessage(CMD_SET_POWER_MODE, mode, 0));
+ }
+
+ /**
+ * Set the number of allowed radio frequency channels from the system
+ * setting value, if any.
+ */
+ public void setNumAllowedChannels() {
+ try {
+ setNumAllowedChannels(
+ Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS));
+ } catch (Settings.SettingNotFoundException e) {
+ if (mNumAllowedChannels != 0) {
+ setNumAllowedChannels(mNumAllowedChannels);
+ }
+ // otherwise, use the driver default
+ }
+ }
+
+ /**
+ * Set the number of radio frequency channels that are allowed to be used
+ * in the current regulatory domain.
+ * @param numChannels the number of allowed channels. Must be greater than 0
+ * and less than or equal to 16.
+ */
+ public void setNumAllowedChannels(int numChannels) {
+ sendMessage(obtainMessage(CMD_SET_NUM_ALLOWED_CHANNELS, numChannels, 0));
+ }
+
+ /**
+ * Get number of allowed channels
+ *
+ * @return channel count, -1 on failure
+ *
+ * TODO: this is not a public API and needs to be removed in favor
+ * of asynchronous reporting. unused for now.
+ */
+ public int getNumAllowedChannels() {
+ return -1;
+ }
+
+ /**
+ * Set bluetooth coex mode:
+ *
+ * @param mode
+ * BLUETOOTH_COEXISTENCE_MODE_ENABLED
+ * BLUETOOTH_COEXISTENCE_MODE_DISABLED
+ * BLUETOOTH_COEXISTENCE_MODE_SENSE
+ */
+ public void setBluetoothCoexistenceMode(int mode) {
+ sendMessage(obtainMessage(CMD_SET_BLUETOOTH_COEXISTENCE, mode, 0));
+ }
+
+ /**
+ * Enable or disable Bluetooth coexistence scan mode. When this mode is on,
+ * some of the low-level scan parameters used by the driver are changed to
+ * reduce interference with A2DP streaming.
+ *
+ * @param isBluetoothPlaying whether to enable or disable this mode
+ */
+ public void setBluetoothScanMode(boolean isBluetoothPlaying) {
+ sendMessage(obtainMessage(CMD_SET_BLUETOOTH_SCAN_MODE, isBluetoothPlaying ? 1 : 0, 0));
+ }
+
+ /**
+ * Save configuration on supplicant
+ *
+ * @return {@code true} if the operation succeeds, {@code false} otherwise
+ *
+ * TODO: deprecate this
+ */
+ public boolean saveConfig() {
+ return sendSyncMessage(CMD_SAVE_CONFIG).boolValue;
+ }
+
+ /**
+ * TODO: doc
+ */
+ public void requestCmWakeLock() {
+ sendMessage(CMD_REQUEST_CM_WAKELOCK);
+ }
+
+ /*********************************************************
+ * Internal private functions
+ ********************************************************/
+
+ class SyncReturn {
+ boolean boolValue;
+ int intValue;
+ String stringValue;
+ Object objValue;
+ List<WifiConfiguration> configList;
+ }
+
+ class SyncParams {
+ Object mParameter;
+ SyncReturn mSyncReturn;
+ SyncParams() {
+ mSyncReturn = new SyncReturn();
+ }
+ SyncParams(Object p) {
+ mParameter = p;
+ mSyncReturn = new SyncReturn();
+ }
+ }
+
+ /**
+ * message.arg2 is reserved to indicate synchronized
+ * message.obj is used to store SyncParams
+ */
+ private SyncReturn syncedSend(Message msg) {
+ SyncParams syncParams = (SyncParams) msg.obj;
+ msg.arg2 = SYNCHRONOUS_CALL;
+ synchronized(syncParams) {
+ if (DBG) Log.d(TAG, "syncedSend " + msg);
+ sendMessage(msg);
+ try {
+ syncParams.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "sendSyncMessage: unexpected interruption of wait()");
+ return null;
+ }
+ }
+ return syncParams.mSyncReturn;
+ }
+
+ private SyncReturn sendSyncMessage(Message msg) {
+ SyncParams syncParams = new SyncParams();
+ msg.obj = syncParams;
+ return syncedSend(msg);
+ }
+
+ private SyncReturn sendSyncMessage(int what, Object param) {
+ SyncParams syncParams = new SyncParams(param);
+ Message msg = obtainMessage(what, syncParams);
+ return syncedSend(msg);
+ }
+
+
+ private SyncReturn sendSyncMessage(int what) {
+ return sendSyncMessage(obtainMessage(what));
+ }
+
+ private void notifyOnMsgObject(Message msg) {
+ SyncParams syncParams = (SyncParams) msg.obj;
+ if (syncParams != null) {
+ synchronized(syncParams) {
+ if (DBG) Log.d(TAG, "notifyOnMsgObject " + msg);
+ syncParams.notify();
+ }
+ }
+ else {
+ Log.e(TAG, "Error! syncParams in notifyOnMsgObject is null");
+ }
+ }
+
+ private void setWifiState(int wifiState) {
+ final int previousWifiState = mWifiState.get();
+
+ try {
+ if (wifiState == WIFI_STATE_ENABLED) {
+ mBatteryStats.noteWifiOn(mLastEnableUid.get());
+ } else if (wifiState == WIFI_STATE_DISABLED) {
+ mBatteryStats.noteWifiOff(mLastEnableUid.get());
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to note battery stats in wifi");
+ }
+
+ mWifiState.set(wifiState);
+
+ if (DBG) Log.d(TAG, "setWifiState: " + getWifiStateByName());
+
+ final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);
+ intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState);
+ mContext.sendStickyBroadcast(intent);
+ }
+
+ private void setWifiApState(int wifiApState) {
+ final int previousWifiApState = mWifiApState.get();
+
+ try {
+ if (wifiApState == WIFI_AP_STATE_ENABLED) {
+ mBatteryStats.noteWifiOn(mLastApEnableUid.get());
+ } else if (wifiApState == WIFI_AP_STATE_DISABLED) {
+ mBatteryStats.noteWifiOff(mLastApEnableUid.get());
+ }
+ } catch (RemoteException e) {
+ Log.d(TAG, "Failed to note battery stats in wifi");
+ }
+
+ // Update state
+ mWifiApState.set(wifiApState);
+
+ if (DBG) Log.d(TAG, "setWifiApState: " + getWifiApStateByName());
+
+ final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState);
+ intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState);
+ mContext.sendStickyBroadcast(intent);
+ }
+
+ /**
+ * Parse the scan result line passed to us by wpa_supplicant (helper).
+ * @param line the line to parse
+ * @return the {@link ScanResult} object
+ */
+ private ScanResult parseScanResult(String line) {
+ ScanResult scanResult = null;
+ if (line != null) {
+ /*
+ * Cache implementation (LinkedHashMap) is not synchronized, thus,
+ * must synchronized here!
+ */
+ synchronized (mScanResultCache) {
+ String[] result = scanResultPattern.split(line);
+ if (3 <= result.length && result.length <= 5) {
+ String bssid = result[0];
+ // bssid | frequency | level | flags | ssid
+ int frequency;
+ int level;
+ try {
+ frequency = Integer.parseInt(result[1]);
+ level = Integer.parseInt(result[2]);
+ /* some implementations avoid negative values by adding 256
+ * so we need to adjust for that here.
+ */
+ if (level > 0) level -= 256;
+ } catch (NumberFormatException e) {
+ frequency = 0;
+ level = 0;
+ }
+
+ /*
+ * The formatting of the results returned by
+ * wpa_supplicant is intended to make the fields
+ * line up nicely when printed,
+ * not to make them easy to parse. So we have to
+ * apply some heuristics to figure out which field
+ * is the SSID and which field is the flags.
+ */
+ String ssid;
+ String flags;
+ if (result.length == 4) {
+ if (result[3].charAt(0) == '[') {
+ flags = result[3];
+ ssid = "";
+ } else {
+ flags = "";
+ ssid = result[3];
+ }
+ } else if (result.length == 5) {
+ flags = result[3];
+ ssid = result[4];
+ } else {
+ // Here, we must have 3 fields: no flags and ssid
+ // set
+ flags = "";
+ ssid = "";
+ }
+
+ // bssid + ssid is the hash key
+ String key = bssid + ssid;
+ scanResult = mScanResultCache.get(key);
+ if (scanResult != null) {
+ scanResult.level = level;
+ scanResult.SSID = ssid;
+ scanResult.capabilities = flags;
+ scanResult.frequency = frequency;
+ } else {
+ // Do not add scan results that have no SSID set
+ if (0 < ssid.trim().length()) {
+ scanResult =
+ new ScanResult(
+ ssid, bssid, flags, level, frequency);
+ mScanResultCache.put(key, scanResult);
+ }
+ }
+ } else {
+ Log.w(TAG, "Misformatted scan result text with " +
+ result.length + " fields: " + line);
+ }
+ }
+ }
+
+ return scanResult;
+ }
+
+ /**
+ * scanResults input format
+ * 00:bb:cc:dd:cc:ee 2427 166 [WPA-EAP-TKIP][WPA2-EAP-CCMP] Net1
+ * 00:bb:cc:dd:cc:ff 2412 165 [WPA-EAP-TKIP][WPA2-EAP-CCMP] Net2
+ */
+ private void setScanResults(String scanResults) {
+ if (scanResults == null) {
+ return;
+ }
+
+ List<ScanResult> scanList = new ArrayList<ScanResult>();
+
+ int lineCount = 0;
+
+ int scanResultsLen = scanResults.length();
+ // Parse the result string, keeping in mind that the last line does
+ // not end with a newline.
+ for (int lineBeg = 0, lineEnd = 0; lineEnd <= scanResultsLen; ++lineEnd) {
+ if (lineEnd == scanResultsLen || scanResults.charAt(lineEnd) == '\n') {
+ ++lineCount;
+
+ if (lineCount == 1) {
+ lineBeg = lineEnd + 1;
+ continue;
+ }
+ if (lineEnd > lineBeg) {
+ String line = scanResults.substring(lineBeg, lineEnd);
+ ScanResult scanResult = parseScanResult(line);
+ if (scanResult != null) {
+ scanList.add(scanResult);
+ } else {
+ Log.w(TAG, "misformatted scan result for: " + line);
+ }
+ }
+ lineBeg = lineEnd + 1;
+ }
+ }
+
+ mScanResults = scanList;
+ }
+
+ private void configureNetworkProperties() {
+ try {
+ mNetworkProperties.setInterface(NetworkInterface.getByName(mInterfaceName));
+ } catch (SocketException e) {
+ Log.e(TAG, "SocketException creating NetworkInterface from " + mInterfaceName +
+ ". e=" + e);
+ return;
+ } catch (NullPointerException e) {
+ Log.e(TAG, "NPE creating NetworkInterface. e=" + e);
+ return;
+ }
+ // TODO - fix this for v6
+ try {
+ mNetworkProperties.addAddress(InetAddress.getByAddress(
+ NetworkUtils.v4IntToArray(mDhcpInfo.ipAddress)));
+ } catch (UnknownHostException e) {
+ Log.e(TAG, "Exception setting IpAddress using " + mDhcpInfo + ", e=" + e);
+ }
+
+ try {
+ mNetworkProperties.setGateway(InetAddress.getByAddress(NetworkUtils.v4IntToArray(
+ mDhcpInfo.gateway)));
+ } catch (UnknownHostException e) {
+ Log.e(TAG, "Exception setting Gateway using " + mDhcpInfo + ", e=" + e);
+ }
+
+ try {
+ mNetworkProperties.addDns(InetAddress.getByAddress(
+ NetworkUtils.v4IntToArray(mDhcpInfo.dns1)));
+ } catch (UnknownHostException e) {
+ Log.e(TAG, "Exception setting Dns1 using " + mDhcpInfo + ", e=" + e);
+ }
+ try {
+ mNetworkProperties.addDns(InetAddress.getByAddress(
+ NetworkUtils.v4IntToArray(mDhcpInfo.dns2)));
+
+ } catch (UnknownHostException e) {
+ Log.e(TAG, "Exception setting Dns2 using " + mDhcpInfo + ", e=" + e);
+ }
+ // TODO - add proxy info
+ }
+
+
+ private void checkUseStaticIp() {
+ mUseStaticIp = false;
+ final ContentResolver cr = mContext.getContentResolver();
+ try {
+ if (Settings.System.getInt(cr, Settings.System.WIFI_USE_STATIC_IP) == 0) {
+ return;
+ }
+ } catch (Settings.SettingNotFoundException e) {
+ return;
+ }
+
+ try {
+ String addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_IP);
+ if (addr != null) {
+ mDhcpInfo.ipAddress = stringToIpAddr(addr);
+ } else {
+ return;
+ }
+ addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_GATEWAY);
+ if (addr != null) {
+ mDhcpInfo.gateway = stringToIpAddr(addr);
+ } else {
+ return;
+ }
+ addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_NETMASK);
+ if (addr != null) {
+ mDhcpInfo.netmask = stringToIpAddr(addr);
+ } else {
+ return;
+ }
+ addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS1);
+ if (addr != null) {
+ mDhcpInfo.dns1 = stringToIpAddr(addr);
+ } else {
+ return;
+ }
+ addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS2);
+ if (addr != null) {
+ mDhcpInfo.dns2 = stringToIpAddr(addr);
+ } else {
+ mDhcpInfo.dns2 = 0;
+ }
+ } catch (UnknownHostException e) {
+ return;
+ }
+ mUseStaticIp = true;
+ }
+
+ private static int stringToIpAddr(String addrString) throws UnknownHostException {
+ try {
+ String[] parts = addrString.split("\\.");
+ if (parts.length != 4) {
+ throw new UnknownHostException(addrString);
+ }
+
+ int a = Integer.parseInt(parts[0]) ;
+ int b = Integer.parseInt(parts[1]) << 8;
+ int c = Integer.parseInt(parts[2]) << 16;
+ int d = Integer.parseInt(parts[3]) << 24;
+
+ return a | b | c | d;
+ } catch (NumberFormatException ex) {
+ throw new UnknownHostException(addrString);
+ }
+ }
+
+ private int getMaxDhcpRetries() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT,
+ DEFAULT_MAX_DHCP_RETRIES);
+ }
+
+ private class SettingsObserver extends ContentObserver {
+ public SettingsObserver(Handler handler) {
+ super(handler);
+ ContentResolver cr = mContext.getContentResolver();
+ cr.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.WIFI_USE_STATIC_IP), false, this);
+ cr.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.WIFI_STATIC_IP), false, this);
+ cr.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.WIFI_STATIC_GATEWAY), false, this);
+ cr.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.WIFI_STATIC_NETMASK), false, this);
+ cr.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.WIFI_STATIC_DNS1), false, this);
+ cr.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.WIFI_STATIC_DNS2), false, this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+
+ boolean wasStaticIp = mUseStaticIp;
+ int oIp, oGw, oMsk, oDns1, oDns2;
+ oIp = oGw = oMsk = oDns1 = oDns2 = 0;
+ if (wasStaticIp) {
+ oIp = mDhcpInfo.ipAddress;
+ oGw = mDhcpInfo.gateway;
+ oMsk = mDhcpInfo.netmask;
+ oDns1 = mDhcpInfo.dns1;
+ oDns2 = mDhcpInfo.dns2;
+ }
+ checkUseStaticIp();
+
+ if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED) {
+ return;
+ }
+
+ boolean changed =
+ (wasStaticIp != mUseStaticIp) ||
+ (wasStaticIp && (
+ oIp != mDhcpInfo.ipAddress ||
+ oGw != mDhcpInfo.gateway ||
+ oMsk != mDhcpInfo.netmask ||
+ oDns1 != mDhcpInfo.dns1 ||
+ oDns2 != mDhcpInfo.dns2));
+
+ if (changed) {
+ sendMessage(CMD_RECONFIGURE_IP);
+ mConfigChanged = true;
+ }
+ }
+ }
+
+ /**
+ * Whether to disable coexistence mode while obtaining IP address. This
+ * logic will return true only if the current bluetooth
+ * headset/handsfree state is disconnected. This means if it is in an
+ * error state, we will NOT disable coexistence mode to err on the side
+ * of safety.
+ *
+ * @return Whether to disable coexistence mode.
+ */
+ private boolean shouldDisableCoexistenceMode() {
+ int state = mBluetoothHeadset.getState(mBluetoothHeadset.getCurrentHeadset());
+ return state == BluetoothHeadset.STATE_DISCONNECTED;
+ }
+
+ private void checkIsBluetoothPlaying() {
+ boolean isBluetoothPlaying = false;
+ Set<BluetoothDevice> connected = mBluetoothA2dp.getConnectedSinks();
+
+ for (BluetoothDevice device : connected) {
+ if (mBluetoothA2dp.getSinkState(device) == BluetoothA2dp.STATE_PLAYING) {
+ isBluetoothPlaying = true;
+ break;
+ }
+ }
+ setBluetoothScanMode(isBluetoothPlaying);
+ }
+
+ private void sendScanResultsAvailableBroadcast() {
+ if (!ActivityManagerNative.isSystemReady()) return;
+
+ mContext.sendBroadcast(new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
+ }
+
+ private void sendRssiChangeBroadcast(final int newRssi) {
+ if (!ActivityManagerNative.isSystemReady()) return;
+
+ Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
+ intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
+ mContext.sendBroadcast(intent);
+ }
+
+ private void sendNetworkStateChangeBroadcast(String bssid) {
+ Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+ | Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo);
+ intent.putExtra(WifiManager.EXTRA_NETWORK_PROPERTIES, mNetworkProperties);
+ if (bssid != null)
+ intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
+ mContext.sendStickyBroadcast(intent);
+ }
+
+ private void sendConfigChangeBroadcast() {
+ Intent intent = new Intent(WifiManager.CONFIG_CHANGED_ACTION);
+ intent.putExtra(WifiManager.EXTRA_NETWORK_PROPERTIES, mNetworkProperties);
+ mContext.sendBroadcast(intent);
+ }
+
+ private void sendSupplicantStateChangedBroadcast(StateChangeResult sc, boolean failedAuth) {
+ Intent intent = new Intent(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+ | Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra(WifiManager.EXTRA_NEW_STATE, (Parcelable)sc.state);
+ if (failedAuth) {
+ intent.putExtra(
+ WifiManager.EXTRA_SUPPLICANT_ERROR,
+ WifiManager.ERROR_AUTHENTICATING);
+ }
+ mContext.sendStickyBroadcast(intent);
+ }
+
+ private void sendSupplicantConnectionChangedBroadcast(boolean connected) {
+ if (!ActivityManagerNative.isSystemReady()) return;
+
+ Intent intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
+ intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, connected);
+ mContext.sendBroadcast(intent);
+ }
+
+ /**
+ * Record the detailed state of a network.
+ * @param state the new @{code DetailedState}
+ */
+ private void setDetailedState(NetworkInfo.DetailedState state) {
+ Log.d(TAG, "setDetailed state, old ="
+ + mNetworkInfo.getDetailedState() + " and new state=" + state);
+ if (state != mNetworkInfo.getDetailedState()) {
+ mNetworkInfo.setDetailedState(state, null, null);
+ }
+ }
+
+ private static String removeDoubleQuotes(String string) {
+ if (string.length() <= 2) return "";
+ return string.substring(1, string.length() - 1);
+ }
+
+ private static String convertToQuotedString(String string) {
+ return "\"" + string + "\"";
+ }
+
+ private static String makeString(BitSet set, String[] strings) {
+ StringBuffer buf = new StringBuffer();
+ int nextSetBit = -1;
+
+ /* Make sure all set bits are in [0, strings.length) to avoid
+ * going out of bounds on strings. (Shouldn't happen, but...) */
+ set = set.get(0, strings.length);
+
+ while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
+ buf.append(strings[nextSetBit].replace('_', '-')).append(' ');
+ }
+
+ // remove trailing space
+ if (set.cardinality() > 0) {
+ buf.setLength(buf.length() - 1);
+ }
+
+ return buf.toString();
+ }
+
+ private static int lookupString(String string, String[] strings) {
+ int size = strings.length;
+
+ string = string.replace('-', '_');
+
+ for (int i = 0; i < size; i++)
+ if (string.equals(strings[i]))
+ return i;
+
+ // if we ever get here, we should probably add the
+ // value to WifiConfiguration to reflect that it's
+ // supported by the WPA supplicant
+ Log.w(TAG, "Failed to look-up a string: " + string);
+
+ return -1;
+ }
+
+ private int addOrUpdateNetworkNative(WifiConfiguration config) {
+ /*
+ * If the supplied networkId is -1, we create a new empty
+ * network configuration. Otherwise, the networkId should
+ * refer to an existing configuration.
+ */
+ int netId = config.networkId;
+ boolean newNetwork = netId == -1;
+ // networkId of -1 means we want to create a new network
+
+ if (newNetwork) {
+ netId = WifiNative.addNetworkCommand();
+ if (netId < 0) {
+ Log.e(TAG, "Failed to add a network!");
+ return -1;
+ }
+ }
+
+ setVariables: {
+
+ if (config.SSID != null &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.ssidVarName,
+ config.SSID)) {
+ Log.d(TAG, "failed to set SSID: "+config.SSID);
+ break setVariables;
+ }
+
+ if (config.BSSID != null &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.bssidVarName,
+ config.BSSID)) {
+ Log.d(TAG, "failed to set BSSID: "+config.BSSID);
+ break setVariables;
+ }
+
+ String allowedKeyManagementString =
+ makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings);
+ if (config.allowedKeyManagement.cardinality() != 0 &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.KeyMgmt.varName,
+ allowedKeyManagementString)) {
+ Log.d(TAG, "failed to set key_mgmt: "+
+ allowedKeyManagementString);
+ break setVariables;
+ }
+
+ String allowedProtocolsString =
+ makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings);
+ if (config.allowedProtocols.cardinality() != 0 &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.Protocol.varName,
+ allowedProtocolsString)) {
+ Log.d(TAG, "failed to set proto: "+
+ allowedProtocolsString);
+ break setVariables;
+ }
+
+ String allowedAuthAlgorithmsString =
+ makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings);
+ if (config.allowedAuthAlgorithms.cardinality() != 0 &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.AuthAlgorithm.varName,
+ allowedAuthAlgorithmsString)) {
+ Log.d(TAG, "failed to set auth_alg: "+
+ allowedAuthAlgorithmsString);
+ break setVariables;
+ }
+
+ String allowedPairwiseCiphersString =
+ makeString(config.allowedPairwiseCiphers,
+ WifiConfiguration.PairwiseCipher.strings);
+ if (config.allowedPairwiseCiphers.cardinality() != 0 &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.PairwiseCipher.varName,
+ allowedPairwiseCiphersString)) {
+ Log.d(TAG, "failed to set pairwise: "+
+ allowedPairwiseCiphersString);
+ break setVariables;
+ }
+
+ String allowedGroupCiphersString =
+ makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings);
+ if (config.allowedGroupCiphers.cardinality() != 0 &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.GroupCipher.varName,
+ allowedGroupCiphersString)) {
+ Log.d(TAG, "failed to set group: "+
+ allowedGroupCiphersString);
+ break setVariables;
+ }
+
+ // Prevent client screw-up by passing in a WifiConfiguration we gave it
+ // by preventing "*" as a key.
+ if (config.preSharedKey != null && !config.preSharedKey.equals("*") &&
+ !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.pskVarName,
+ config.preSharedKey)) {
+ Log.d(TAG, "failed to set psk: "+config.preSharedKey);
+ break setVariables;
+ }
+
+ boolean hasSetKey = false;
+ if (config.wepKeys != null) {
+ for (int i = 0; i < config.wepKeys.length; i++) {
+ // Prevent client screw-up by passing in a WifiConfiguration we gave it
+ // by preventing "*" as a key.
+ if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) {
+ if (!WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.wepKeyVarNames[i],
+ config.wepKeys[i])) {
+ Log.d(TAG,
+ "failed to set wep_key"+i+": " +
+ config.wepKeys[i]);
+ break setVariables;
+ }
+ hasSetKey = true;
+ }
+ }
+ }
+
+ if (hasSetKey) {
+ if (!WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.wepTxKeyIdxVarName,
+ Integer.toString(config.wepTxKeyIndex))) {
+ Log.d(TAG,
+ "failed to set wep_tx_keyidx: "+
+ config.wepTxKeyIndex);
+ break setVariables;
+ }
+ }
+
+ if (!WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.priorityVarName,
+ Integer.toString(config.priority))) {
+ Log.d(TAG, config.SSID + ": failed to set priority: "
+ +config.priority);
+ break setVariables;
+ }
+
+ if (config.hiddenSSID && !WifiNative.setNetworkVariableCommand(
+ netId,
+ WifiConfiguration.hiddenSSIDVarName,
+ Integer.toString(config.hiddenSSID ? 1 : 0))) {
+ Log.d(TAG, config.SSID + ": failed to set hiddenSSID: "+
+ config.hiddenSSID);
+ break setVariables;
+ }
+
+ for (WifiConfiguration.EnterpriseField field
+ : config.enterpriseFields) {
+ String varName = field.varName();
+ String value = field.value();
+ if (value != null) {
+ if (field != config.eap) {
+ value = (value.length() == 0) ? "NULL" : convertToQuotedString(value);
+ }
+ if (!WifiNative.setNetworkVariableCommand(
+ netId,
+ varName,
+ value)) {
+ Log.d(TAG, config.SSID + ": failed to set " + varName +
+ ": " + value);
+ break setVariables;
+ }
+ }
+ }
+ return netId;
+ }
+
+ if (newNetwork) {
+ WifiNative.removeNetworkCommand(netId);
+ Log.d(TAG,
+ "Failed to set a network variable, removed network: "
+ + netId);
+ }
+
+ return -1;
+ }
+
+ private List<WifiConfiguration> getConfiguredNetworksNative() {
+ String listStr = WifiNative.listNetworksCommand();
+
+ List<WifiConfiguration> networks =
+ new ArrayList<WifiConfiguration>();
+ if (listStr == null)
+ return networks;
+
+ String[] lines = listStr.split("\n");
+ // Skip the first line, which is a header
+ for (int i = 1; i < lines.length; i++) {
+ String[] result = lines[i].split("\t");
+ // network-id | ssid | bssid | flags
+ WifiConfiguration config = new WifiConfiguration();
+ try {
+ config.networkId = Integer.parseInt(result[0]);
+ } catch(NumberFormatException e) {
+ continue;
+ }
+ if (result.length > 3) {
+ if (result[3].indexOf("[CURRENT]") != -1)
+ config.status = WifiConfiguration.Status.CURRENT;
+ else if (result[3].indexOf("[DISABLED]") != -1)
+ config.status = WifiConfiguration.Status.DISABLED;
+ else
+ config.status = WifiConfiguration.Status.ENABLED;
+ } else {
+ config.status = WifiConfiguration.Status.ENABLED;
+ }
+ readNetworkVariables(config);
+ networks.add(config);
+ }
+ return networks;
+ }
+
+ /**
+ * Read the variables from the supplicant daemon that are needed to
+ * fill in the WifiConfiguration object.
+ *
+ * @param config the {@link WifiConfiguration} object to be filled in.
+ */
+ private void readNetworkVariables(WifiConfiguration config) {
+
+ int netId = config.networkId;
+ if (netId < 0)
+ return;
+
+ /*
+ * TODO: maybe should have a native method that takes an array of
+ * variable names and returns an array of values. But we'd still
+ * be doing a round trip to the supplicant daemon for each variable.
+ */
+ String value;
+
+ value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.ssidVarName);
+ if (!TextUtils.isEmpty(value)) {
+ config.SSID = removeDoubleQuotes(value);
+ } else {
+ config.SSID = null;
+ }
+
+ value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.bssidVarName);
+ if (!TextUtils.isEmpty(value)) {
+ config.BSSID = value;
+ } else {
+ config.BSSID = null;
+ }
+
+ value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.priorityVarName);
+ config.priority = -1;
+ if (!TextUtils.isEmpty(value)) {
+ try {
+ config.priority = Integer.parseInt(value);
+ } catch (NumberFormatException ignore) {
+ }
+ }
+
+ value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.hiddenSSIDVarName);
+ config.hiddenSSID = false;
+ if (!TextUtils.isEmpty(value)) {
+ try {
+ config.hiddenSSID = Integer.parseInt(value) != 0;
+ } catch (NumberFormatException ignore) {
+ }
+ }
+
+ value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.wepTxKeyIdxVarName);
+ config.wepTxKeyIndex = -1;
+ if (!TextUtils.isEmpty(value)) {
+ try {
+ config.wepTxKeyIndex = Integer.parseInt(value);
+ } catch (NumberFormatException ignore) {
+ }
+ }
+
+ for (int i = 0; i < 4; i++) {
+ value = WifiNative.getNetworkVariableCommand(netId,
+ WifiConfiguration.wepKeyVarNames[i]);
+ if (!TextUtils.isEmpty(value)) {
+ config.wepKeys[i] = value;
+ } else {
+ config.wepKeys[i] = null;
+ }
+ }
+
+ value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.pskVarName);
+ if (!TextUtils.isEmpty(value)) {
+ config.preSharedKey = value;
+ } else {
+ config.preSharedKey = null;
+ }
+
+ value = WifiNative.getNetworkVariableCommand(config.networkId,
+ WifiConfiguration.Protocol.varName);
+ if (!TextUtils.isEmpty(value)) {
+ String vals[] = value.split(" ");
+ for (String val : vals) {
+ int index =
+ lookupString(val, WifiConfiguration.Protocol.strings);
+ if (0 <= index) {
+ config.allowedProtocols.set(index);
+ }
+ }
+ }
+
+ value = WifiNative.getNetworkVariableCommand(config.networkId,
+ WifiConfiguration.KeyMgmt.varName);
+ if (!TextUtils.isEmpty(value)) {
+ String vals[] = value.split(" ");
+ for (String val : vals) {
+ int index =
+ lookupString(val, WifiConfiguration.KeyMgmt.strings);
+ if (0 <= index) {
+ config.allowedKeyManagement.set(index);
+ }
+ }
+ }
+
+ value = WifiNative.getNetworkVariableCommand(config.networkId,
+ WifiConfiguration.AuthAlgorithm.varName);
+ if (!TextUtils.isEmpty(value)) {
+ String vals[] = value.split(" ");
+ for (String val : vals) {
+ int index =
+ lookupString(val, WifiConfiguration.AuthAlgorithm.strings);
+ if (0 <= index) {
+ config.allowedAuthAlgorithms.set(index);
+ }
+ }
+ }
+
+ value = WifiNative.getNetworkVariableCommand(config.networkId,
+ WifiConfiguration.PairwiseCipher.varName);
+ if (!TextUtils.isEmpty(value)) {
+ String vals[] = value.split(" ");
+ for (String val : vals) {
+ int index =
+ lookupString(val, WifiConfiguration.PairwiseCipher.strings);
+ if (0 <= index) {
+ config.allowedPairwiseCiphers.set(index);
+ }
+ }
+ }
+
+ value = WifiNative.getNetworkVariableCommand(config.networkId,
+ WifiConfiguration.GroupCipher.varName);
+ if (!TextUtils.isEmpty(value)) {
+ String vals[] = value.split(" ");
+ for (String val : vals) {
+ int index =
+ lookupString(val, WifiConfiguration.GroupCipher.strings);
+ if (0 <= index) {
+ config.allowedGroupCiphers.set(index);
+ }
+ }
+ }
+
+ for (WifiConfiguration.EnterpriseField field :
+ config.enterpriseFields) {
+ value = WifiNative.getNetworkVariableCommand(netId,
+ field.varName());
+ if (!TextUtils.isEmpty(value)) {
+ if (field != config.eap) value = removeDoubleQuotes(value);
+ field.setValue(value);
+ }
+ }
+
+ }
+
+ /**
+ * Poll for info not reported via events
+ * RSSI & Linkspeed
+ */
+ private void requestPolledInfo() {
+ int newRssi = WifiNative.getRssiCommand();
+ if (newRssi != -1 && -200 < newRssi && newRssi < 256) { // screen out invalid values
+ /* some implementations avoid negative values by adding 256
+ * so we need to adjust for that here.
+ */
+ if (newRssi > 0) newRssi -= 256;
+ mWifiInfo.setRssi(newRssi);
+ /*
+ * Rather then sending the raw RSSI out every time it
+ * changes, we precalculate the signal level that would
+ * be displayed in the status bar, and only send the
+ * broadcast if that much more coarse-grained number
+ * changes. This cuts down greatly on the number of
+ * broadcasts, at the cost of not mWifiInforming others
+ * interested in RSSI of all the changes in signal
+ * level.
+ */
+ // TODO: The second arg to the call below needs to be a symbol somewhere, but
+ // it's actually the size of an array of icons that's private
+ // to StatusBar Policy.
+ int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 4);
+ if (newSignalLevel != mLastSignalLevel) {
+ sendRssiChangeBroadcast(newRssi);
+ }
+ mLastSignalLevel = newSignalLevel;
+ } else {
+ mWifiInfo.setRssi(-200);
+ }
+ int newLinkSpeed = WifiNative.getLinkSpeedCommand();
+ if (newLinkSpeed != -1) {
+ mWifiInfo.setLinkSpeed(newLinkSpeed);
+ }
+ }
+
+ /**
+ * Resets the Wi-Fi Connections by clearing any state, resetting any sockets
+ * using the interface, stopping DHCP & disabling interface
+ */
+ private void handleNetworkDisconnect() {
+ Log.d(TAG, "Reset connections and stopping DHCP");
+
+ /*
+ * Reset connections & stop DHCP
+ */
+ NetworkUtils.resetConnections(mInterfaceName);
+
+ if (!NetworkUtils.stopDhcp(mInterfaceName)) {
+ Log.e(TAG, "Could not stop DHCP");
+ }
+
+ /* Disable interface */
+ NetworkUtils.disableInterface(mInterfaceName);
+
+ /* send event to CM & network change broadcast */
+ setDetailedState(DetailedState.DISCONNECTED);
+ sendNetworkStateChangeBroadcast(mLastBssid);
+
+ /* Reset data structures */
+ mWifiInfo.setIpAddress(0);
+ mWifiInfo.setBSSID(null);
+ mWifiInfo.setSSID(null);
+ mWifiInfo.setNetworkId(-1);
+
+ /* Clear network properties */
+ mNetworkProperties.clear();
+
+ mLastBssid= null;
+ mLastNetworkId = -1;
+
+ }
+
+
+ /*********************************************************
+ * Notifications from WifiMonitor
+ ********************************************************/
+
+ /**
+ * A structure for supplying information about a supplicant state
+ * change in the STATE_CHANGE event message that comes from the
+ * WifiMonitor
+ * thread.
+ */
+ private static class StateChangeResult {
+ StateChangeResult(int networkId, String BSSID, Object state) {
+ this.state = state;
+ this.BSSID = BSSID;
+ this.networkId = networkId;
+ }
+ int networkId;
+ String BSSID;
+ Object state;
+ }
+
+ /**
+ * Send the tracker a notification that a user-entered password key
+ * may be incorrect (i.e., caused authentication to fail).
+ */
+ void notifyPasswordKeyMayBeIncorrect() {
+ sendMessage(PASSWORD_MAY_BE_INCORRECT_EVENT);
+ }
+
+ /**
+ * Send the tracker a notification that a connection to the supplicant
+ * daemon has been established.
+ */
+ void notifySupplicantConnection() {
+ sendMessage(SUP_CONNECTION_EVENT);
+ }
+
+ /**
+ * Send the tracker a notification that a connection to the supplicant
+ * daemon has been established.
+ */
+ void notifySupplicantLost() {
+ sendMessage(SUP_DISCONNECTION_EVENT);
+ }
+
+ /**
+ * Send the tracker a notification that the state of Wifi connectivity
+ * has changed.
+ * @param networkId the configured network on which the state change occurred
+ * @param newState the new network state
+ * @param BSSID when the new state is {@link DetailedState#CONNECTED
+ * NetworkInfo.DetailedState.CONNECTED},
+ * this is the MAC address of the access point. Otherwise, it
+ * is {@code null}.
+ */
+ void notifyNetworkStateChange(DetailedState newState, String BSSID, int networkId) {
+ if (newState == NetworkInfo.DetailedState.CONNECTED) {
+ sendMessage(obtainMessage(NETWORK_CONNECTION_EVENT,
+ new StateChangeResult(networkId, BSSID, newState)));
+ } else {
+ sendMessage(obtainMessage(NETWORK_DISCONNECTION_EVENT,
+ new StateChangeResult(networkId, BSSID, newState)));
+ }
+ }
+
+ /**
+ * Send the tracker a notification that the state of the supplicant
+ * has changed.
+ * @param networkId the configured network on which the state change occurred
+ * @param newState the new {@code SupplicantState}
+ */
+ void notifySupplicantStateChange(int networkId, String BSSID, SupplicantState newState) {
+ sendMessage(obtainMessage(SUPPLICANT_STATE_CHANGE_EVENT,
+ new StateChangeResult(networkId, BSSID, newState)));
+ }
+
+ /**
+ * Send the tracker a notification that a scan has completed, and results
+ * are available.
+ */
+ void notifyScanResultsAvailable() {
+ /**
+ * Switch scan mode over to passive.
+ * Turning off scan-only mode happens only in "Connect" mode
+ */
+ setScanType(false);
+ sendMessage(SCAN_RESULTS_EVENT);
+ }
+
+ void notifyDriverStarted() {
+ sendMessage(DRIVER_START_EVENT);
+ }
+
+ void notifyDriverStopped() {
+ sendMessage(DRIVER_STOP_EVENT);
+ }
+
+ void notifyDriverHung() {
+ setWifiEnabled(false);
+ setWifiEnabled(true);
+ }
+
+
+ /********************************************************
+ * HSM states
+ *******************************************************/
+
+ class DefaultState extends HierarchicalState {
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ SyncParams syncParams;
+ switch (message.what) {
+ /* Synchronous call returns */
+ case CMD_PING_SUPPLICANT:
+ case CMD_START_SCAN:
+ case CMD_DISCONNECT:
+ case CMD_RECONNECT:
+ case CMD_REASSOCIATE:
+ case CMD_REMOVE_NETWORK:
+ case CMD_ENABLE_NETWORK:
+ case CMD_DISABLE_NETWORK:
+ case CMD_ADD_OR_UPDATE_NETWORK:
+ case CMD_GET_RSSI:
+ case CMD_GET_RSSI_APPROX:
+ case CMD_GET_LINK_SPEED:
+ case CMD_GET_MAC_ADDR:
+ case CMD_SAVE_CONFIG:
+ case CMD_CONNECTION_STATUS:
+ case CMD_GET_NETWORK_CONFIG:
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = false;
+ syncParams.mSyncReturn.intValue = -1;
+ syncParams.mSyncReturn.stringValue = null;
+ syncParams.mSyncReturn.configList = null;
+ notifyOnMsgObject(message);
+ }
+ break;
+ case CMD_ENABLE_RSSI_POLL:
+ mEnableRssiPolling = (message.arg1 == 1);
+ mSupplicantStateTracker.sendMessage(CMD_ENABLE_RSSI_POLL);
+ break;
+ /* Discard */
+ case CMD_LOAD_DRIVER:
+ case CMD_UNLOAD_DRIVER:
+ case CMD_START_SUPPLICANT:
+ case CMD_STOP_SUPPLICANT:
+ case CMD_START_DRIVER:
+ case CMD_STOP_DRIVER:
+ case CMD_START_AP:
+ case CMD_STOP_AP:
+ case CMD_RECONFIGURE_IP:
+ case SUP_CONNECTION_EVENT:
+ case SUP_DISCONNECTION_EVENT:
+ case DRIVER_START_EVENT:
+ case DRIVER_STOP_EVENT:
+ case NETWORK_CONNECTION_EVENT:
+ case NETWORK_DISCONNECTION_EVENT:
+ case SCAN_RESULTS_EVENT:
+ case SUPPLICANT_STATE_CHANGE_EVENT:
+ case PASSWORD_MAY_BE_INCORRECT_EVENT:
+ case CMD_BLACKLIST_NETWORK:
+ case CMD_CLEAR_BLACKLIST:
+ case CMD_SET_SCAN_MODE:
+ case CMD_SET_SCAN_TYPE:
+ case CMD_SET_POWER_MODE:
+ case CMD_SET_BLUETOOTH_COEXISTENCE:
+ case CMD_SET_BLUETOOTH_SCAN_MODE:
+ case CMD_SET_NUM_ALLOWED_CHANNELS:
+ case CMD_REQUEST_CM_WAKELOCK:
+ break;
+ default:
+ Log.e(TAG, "Error! unhandled message" + message);
+ break;
+ }
+ return HANDLED;
+ }
+ }
+
+ class InitialState extends HierarchicalState {
+ @Override
+ //TODO: could move logging into a common class
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ // [31-8] Reserved for future use
+ // [7 - 0] HSM state change
+ // 50021 wifi_state_changed (custom|1|5)
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+ if (WifiNative.isDriverLoaded()) {
+ transitionTo(mDriverLoadedState);
+ }
+ else {
+ transitionTo(mDriverUnloadedState);
+ }
+ }
+ }
+
+ class DriverLoadingState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+ final Message message = new Message();
+ message.copyFrom(getCurrentMessage());
+ /* TODO: add a timeout to fail when driver load is hung.
+ * Similarly for driver unload.
+ */
+ new Thread(new Runnable() {
+ public void run() {
+ sWakeLock.acquire();
+ //enabling state
+ switch(message.arg1) {
+ case WIFI_STATE_ENABLING:
+ setWifiState(WIFI_STATE_ENABLING);
+ break;
+ case WIFI_AP_STATE_ENABLING:
+ setWifiApState(WIFI_AP_STATE_ENABLING);
+ break;
+ }
+
+ if(WifiNative.loadDriver()) {
+ Log.d(TAG, "Driver load successful");
+ sendMessage(CMD_LOAD_DRIVER_SUCCESS);
+ } else {
+ Log.e(TAG, "Failed to load driver!");
+ switch(message.arg1) {
+ case WIFI_STATE_ENABLING:
+ setWifiState(WIFI_STATE_UNKNOWN);
+ break;
+ case WIFI_AP_STATE_ENABLING:
+ setWifiApState(WIFI_AP_STATE_FAILED);
+ break;
+ }
+ sendMessage(CMD_LOAD_DRIVER_FAILURE);
+ }
+ sWakeLock.release();
+ }
+ }).start();
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case CMD_LOAD_DRIVER_SUCCESS:
+ transitionTo(mDriverLoadedState);
+ break;
+ case CMD_LOAD_DRIVER_FAILURE:
+ transitionTo(mDriverFailedState);
+ break;
+ case CMD_LOAD_DRIVER:
+ case CMD_UNLOAD_DRIVER:
+ case CMD_START_SUPPLICANT:
+ case CMD_STOP_SUPPLICANT:
+ case CMD_START_AP:
+ case CMD_STOP_AP:
+ case CMD_START_DRIVER:
+ case CMD_STOP_DRIVER:
+ case CMD_SET_SCAN_MODE:
+ case CMD_SET_SCAN_TYPE:
+ case CMD_SET_POWER_MODE:
+ case CMD_SET_BLUETOOTH_COEXISTENCE:
+ case CMD_SET_BLUETOOTH_SCAN_MODE:
+ case CMD_SET_NUM_ALLOWED_CHANNELS:
+ case CMD_START_PACKET_FILTERING:
+ case CMD_STOP_PACKET_FILTERING:
+ deferMessage(message);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DriverLoadedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch(message.what) {
+ case CMD_UNLOAD_DRIVER:
+ transitionTo(mDriverUnloadingState);
+ break;
+ case CMD_START_SUPPLICANT:
+ if(WifiNative.startSupplicant()) {
+ Log.d(TAG, "Supplicant start successful");
+ mWifiMonitor.startMonitoring();
+ setWifiState(WIFI_STATE_ENABLED);
+ transitionTo(mWaitForSupState);
+ } else {
+ Log.e(TAG, "Failed to start supplicant!");
+ sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0));
+ }
+ break;
+ case CMD_START_AP:
+ try {
+ nwService.startAccessPoint((WifiConfiguration) message.obj,
+ mInterfaceName,
+ SOFTAP_IFACE);
+ } catch(Exception e) {
+ Log.e(TAG, "Exception in startAccessPoint()");
+ sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0));
+ break;
+ }
+ Log.d(TAG, "Soft AP start successful");
+ setWifiApState(WIFI_AP_STATE_ENABLED);
+ transitionTo(mSoftApStartedState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DriverUnloadingState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+ final Message message = new Message();
+ message.copyFrom(getCurrentMessage());
+ new Thread(new Runnable() {
+ public void run() {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ sWakeLock.acquire();
+ if(WifiNative.unloadDriver()) {
+ Log.d(TAG, "Driver unload successful");
+ sendMessage(CMD_UNLOAD_DRIVER_SUCCESS);
+
+ switch(message.arg1) {
+ case WIFI_STATE_DISABLED:
+ case WIFI_STATE_UNKNOWN:
+ setWifiState(message.arg1);
+ break;
+ case WIFI_AP_STATE_DISABLED:
+ case WIFI_AP_STATE_FAILED:
+ setWifiApState(message.arg1);
+ break;
+ }
+ } else {
+ Log.e(TAG, "Failed to unload driver!");
+ sendMessage(CMD_UNLOAD_DRIVER_FAILURE);
+
+ switch(message.arg1) {
+ case WIFI_STATE_DISABLED:
+ case WIFI_STATE_UNKNOWN:
+ setWifiState(WIFI_STATE_UNKNOWN);
+ break;
+ case WIFI_AP_STATE_DISABLED:
+ case WIFI_AP_STATE_FAILED:
+ setWifiApState(WIFI_AP_STATE_FAILED);
+ break;
+ }
+ }
+ sWakeLock.release();
+ }
+ }).start();
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case CMD_UNLOAD_DRIVER_SUCCESS:
+ transitionTo(mDriverUnloadedState);
+ break;
+ case CMD_UNLOAD_DRIVER_FAILURE:
+ transitionTo(mDriverFailedState);
+ break;
+ case CMD_LOAD_DRIVER:
+ case CMD_UNLOAD_DRIVER:
+ case CMD_START_SUPPLICANT:
+ case CMD_STOP_SUPPLICANT:
+ case CMD_START_AP:
+ case CMD_STOP_AP:
+ case CMD_START_DRIVER:
+ case CMD_STOP_DRIVER:
+ case CMD_SET_SCAN_MODE:
+ case CMD_SET_SCAN_TYPE:
+ case CMD_SET_POWER_MODE:
+ case CMD_SET_BLUETOOTH_COEXISTENCE:
+ case CMD_SET_BLUETOOTH_SCAN_MODE:
+ case CMD_SET_NUM_ALLOWED_CHANNELS:
+ case CMD_START_PACKET_FILTERING:
+ case CMD_STOP_PACKET_FILTERING:
+ deferMessage(message);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DriverUnloadedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case CMD_LOAD_DRIVER:
+ transitionTo(mDriverLoadingState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DriverFailedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ Log.e(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ return NOT_HANDLED;
+ }
+ }
+
+
+ class WaitForSupState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch(message.what) {
+ case SUP_CONNECTION_EVENT:
+ Log.d(TAG, "Supplicant connection established");
+ mSupplicantStateTracker.resetSupplicantState();
+ /* Initialize data structures */
+ mLastBssid = null;
+ mLastNetworkId = -1;
+ mLastSignalLevel = -1;
+
+ mWifiInfo.setMacAddress(WifiNative.getMacAddressCommand());
+
+ //TODO: initialize and fix multicast filtering
+ //mWM.initializeMulticastFiltering();
+
+ if (mBluetoothA2dp == null) {
+ mBluetoothA2dp = new BluetoothA2dp(mContext);
+ }
+ checkIsBluetoothPlaying();
+
+ checkUseStaticIp();
+ sendSupplicantConnectionChangedBroadcast(true);
+ transitionTo(mDriverSupReadyState);
+ break;
+ case CMD_STOP_SUPPLICANT:
+ Log.d(TAG, "Stop supplicant received");
+ WifiNative.stopSupplicant();
+ transitionTo(mDriverLoadedState);
+ break;
+ /* Fail soft ap when waiting for supplicant start */
+ case CMD_START_AP:
+ Log.d(TAG, "Failed to start soft AP with a running supplicant");
+ setWifiApState(WIFI_AP_STATE_FAILED);
+ break;
+ case CMD_START_DRIVER:
+ case CMD_STOP_DRIVER:
+ case CMD_SET_SCAN_MODE:
+ case CMD_SET_SCAN_TYPE:
+ case CMD_SET_POWER_MODE:
+ case CMD_SET_BLUETOOTH_COEXISTENCE:
+ case CMD_SET_BLUETOOTH_SCAN_MODE:
+ case CMD_SET_NUM_ALLOWED_CHANNELS:
+ case CMD_START_PACKET_FILTERING:
+ case CMD_STOP_PACKET_FILTERING:
+ deferMessage(message);
+ break;
+ case CMD_STOP_AP:
+ case CMD_START_SUPPLICANT:
+ case CMD_UNLOAD_DRIVER:
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DriverSupReadyState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ /* Initialize for connect mode operation at start */
+ mIsScanMode = false;
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ SyncParams syncParams;
+ switch(message.what) {
+ case CMD_STOP_SUPPLICANT: /* Supplicant stopped by user */
+ Log.d(TAG, "Stop supplicant received");
+ WifiNative.stopSupplicant();
+ //$FALL-THROUGH$
+ case SUP_DISCONNECTION_EVENT: /* Supplicant died */
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ WifiNative.closeSupplicantConnection();
+ handleNetworkDisconnect();
+ sendSupplicantConnectionChangedBroadcast(false);
+ mSupplicantStateTracker.resetSupplicantState();
+ transitionTo(mDriverLoadedState);
+
+ /* When supplicant dies, unload driver and enter failed state */
+ //TODO: consider bringing up supplicant again
+ if (message.what == SUP_DISCONNECTION_EVENT) {
+ Log.d(TAG, "Supplicant died, unloading driver");
+ sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0));
+ }
+ break;
+ case CMD_START_DRIVER:
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ WifiNative.startDriverCommand();
+ transitionTo(mDriverStartingState);
+ break;
+ case SCAN_RESULTS_EVENT:
+ setScanResults(WifiNative.scanResultsCommand());
+ sendScanResultsAvailableBroadcast();
+ break;
+ case CMD_PING_SUPPLICANT:
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiNative.pingCommand();
+ notifyOnMsgObject(message);
+ break;
+ case CMD_ADD_OR_UPDATE_NETWORK:
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ syncParams = (SyncParams) message.obj;
+ WifiConfiguration config = (WifiConfiguration) syncParams.mParameter;
+ syncParams.mSyncReturn.intValue = addOrUpdateNetworkNative(config);
+ notifyOnMsgObject(message);
+ break;
+ case CMD_REMOVE_NETWORK:
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiNative.removeNetworkCommand(
+ message.arg1);
+ notifyOnMsgObject(message);
+ } else {
+ /* asynchronous handling */
+ WifiNative.removeNetworkCommand(message.arg1);
+ }
+ break;
+ case CMD_ENABLE_NETWORK:
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ syncParams = (SyncParams) message.obj;
+ EnableNetParams enableNetParams = (EnableNetParams) syncParams.mParameter;
+ syncParams.mSyncReturn.boolValue = WifiNative.enableNetworkCommand(
+ enableNetParams.netId, enableNetParams.disableOthers);
+ notifyOnMsgObject(message);
+ } else {
+ /* asynchronous handling */
+ WifiNative.enableNetworkCommand(message.arg1, message.arg2 == 1);
+ }
+ break;
+ case CMD_DISABLE_NETWORK:
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiNative.disableNetworkCommand(
+ message.arg1);
+ notifyOnMsgObject(message);
+ } else {
+ /* asynchronous handling */
+ WifiNative.disableNetworkCommand(message.arg1);
+ }
+ break;
+ case CMD_BLACKLIST_NETWORK:
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ WifiNative.addToBlacklistCommand((String)message.obj);
+ break;
+ case CMD_CLEAR_BLACKLIST:
+ WifiNative.clearBlacklistCommand();
+ break;
+ case CMD_GET_NETWORK_CONFIG:
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.configList = getConfiguredNetworksNative();
+ notifyOnMsgObject(message);
+ break;
+ case CMD_SAVE_CONFIG:
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiNative.saveConfigCommand();
+ notifyOnMsgObject(message);
+ } else {
+ /* asynchronous handling */
+ WifiNative.saveConfigCommand();
+ }
+ // Inform the backup manager about a data change
+ IBackupManager ibm = IBackupManager.Stub.asInterface(
+ ServiceManager.getService(Context.BACKUP_SERVICE));
+ if (ibm != null) {
+ try {
+ ibm.dataChanged("com.android.providers.settings");
+ } catch (Exception e) {
+ // Try again later
+ }
+ }
+ break;
+ case CMD_CONNECTION_STATUS:
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.stringValue = WifiNative.statusCommand();
+ notifyOnMsgObject(message);
+ break;
+ case CMD_GET_MAC_ADDR:
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.stringValue = WifiNative.getMacAddressCommand();
+ notifyOnMsgObject(message);
+ break;
+ /* Cannot start soft AP while in client mode */
+ case CMD_START_AP:
+ Log.d(TAG, "Failed to start soft AP with a running supplicant");
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ setWifiApState(WIFI_AP_STATE_FAILED);
+ break;
+ case CMD_SET_SCAN_MODE:
+ mIsScanMode = (message.arg1 == SCAN_ONLY_MODE);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ class DriverStartingState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch(message.what) {
+ case DRIVER_START_EVENT:
+ transitionTo(mDriverStartedState);
+ break;
+ /* Queue driver commands & connection events */
+ case CMD_START_DRIVER:
+ case CMD_STOP_DRIVER:
+ case SUPPLICANT_STATE_CHANGE_EVENT:
+ case NETWORK_CONNECTION_EVENT:
+ case NETWORK_DISCONNECTION_EVENT:
+ case PASSWORD_MAY_BE_INCORRECT_EVENT:
+ case CMD_SET_SCAN_TYPE:
+ case CMD_SET_POWER_MODE:
+ case CMD_SET_BLUETOOTH_COEXISTENCE:
+ case CMD_SET_BLUETOOTH_SCAN_MODE:
+ case CMD_SET_NUM_ALLOWED_CHANNELS:
+ case CMD_START_PACKET_FILTERING:
+ case CMD_STOP_PACKET_FILTERING:
+ deferMessage(message);
+ break;
+ /* Queue the asynchronous version of these commands */
+ case CMD_START_SCAN:
+ case CMD_DISCONNECT:
+ case CMD_REASSOCIATE:
+ case CMD_RECONNECT:
+ if (message.arg2 != SYNCHRONOUS_CALL) {
+ deferMessage(message);
+ }
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DriverStartedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+ try {
+ mBatteryStats.noteWifiRunning();
+ } catch (RemoteException ignore) {}
+
+ /* Initialize channel count */
+ setNumAllowedChannels();
+
+ if (mIsScanMode) {
+ WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);
+ WifiNative.disconnectCommand();
+ transitionTo(mScanModeState);
+ } else {
+ WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
+ /* If supplicant has already connected, before we could finish establishing
+ * the control channel connection, we miss all the supplicant events.
+ * Disconnect and reconnect when driver has started to ensure we receive
+ * all supplicant events.
+ *
+ * TODO: This is a bit unclean, ideally the supplicant should never
+ * connect until told to do so by the framework
+ */
+ WifiNative.disconnectCommand();
+ WifiNative.reconnectCommand();
+ transitionTo(mConnectModeState);
+ }
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ SyncParams syncParams;
+ switch(message.what) {
+ case CMD_SET_SCAN_TYPE:
+ if (message.arg1 == SCAN_ACTIVE) {
+ WifiNative.setScanModeCommand(true);
+ } else {
+ WifiNative.setScanModeCommand(false);
+ }
+ break;
+ case CMD_SET_POWER_MODE:
+ WifiNative.setPowerModeCommand(message.arg1);
+ break;
+ case CMD_SET_BLUETOOTH_COEXISTENCE:
+ WifiNative.setBluetoothCoexistenceModeCommand(message.arg1);
+ break;
+ case CMD_SET_BLUETOOTH_SCAN_MODE:
+ WifiNative.setBluetoothCoexistenceScanModeCommand(message.arg1 == 1);
+ break;
+ case CMD_SET_NUM_ALLOWED_CHANNELS:
+ mNumAllowedChannels = message.arg1;
+ WifiNative.setNumAllowedChannelsCommand(message.arg1);
+ break;
+ case CMD_START_DRIVER:
+ /* Ignore another driver start */
+ break;
+ case CMD_STOP_DRIVER:
+ WifiNative.stopDriverCommand();
+ transitionTo(mDriverStoppingState);
+ break;
+ case CMD_REQUEST_CM_WAKELOCK:
+ if (mCm == null) {
+ mCm = (ConnectivityManager)mContext.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ }
+ mCm.requestNetworkTransitionWakelock(TAG);
+ break;
+ case CMD_START_PACKET_FILTERING:
+ WifiNative.startPacketFiltering();
+ break;
+ case CMD_STOP_PACKET_FILTERING:
+ WifiNative.stopPacketFiltering();
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ @Override
+ public void exit() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ try {
+ mBatteryStats.noteWifiStopped();
+ } catch (RemoteException ignore) { }
+ }
+ }
+
+ class DriverStoppingState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch(message.what) {
+ case DRIVER_STOP_EVENT:
+ transitionTo(mDriverStoppedState);
+ break;
+ /* Queue driver commands */
+ case CMD_START_DRIVER:
+ case CMD_STOP_DRIVER:
+ case CMD_SET_SCAN_TYPE:
+ case CMD_SET_POWER_MODE:
+ case CMD_SET_BLUETOOTH_COEXISTENCE:
+ case CMD_SET_BLUETOOTH_SCAN_MODE:
+ case CMD_SET_NUM_ALLOWED_CHANNELS:
+ case CMD_START_PACKET_FILTERING:
+ case CMD_STOP_PACKET_FILTERING:
+ deferMessage(message);
+ break;
+ /* Queue the asynchronous version of these commands */
+ case CMD_START_SCAN:
+ case CMD_DISCONNECT:
+ case CMD_REASSOCIATE:
+ case CMD_RECONNECT:
+ if (message.arg2 != SYNCHRONOUS_CALL) {
+ deferMessage(message);
+ }
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DriverStoppedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ return NOT_HANDLED;
+ }
+ }
+
+ class ScanModeState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ SyncParams syncParams;
+ switch(message.what) {
+ case CMD_SET_SCAN_MODE:
+ if (message.arg1 == SCAN_ONLY_MODE) {
+ /* Ignore */
+ return HANDLED;
+ } else {
+ WifiNative.setScanResultHandlingCommand(message.arg1);
+ WifiNative.reconnectCommand();
+ mIsScanMode = false;
+ transitionTo(mDisconnectedState);
+ }
+ break;
+ case CMD_START_SCAN:
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiNative.scanCommand(
+ message.arg1 == SCAN_ACTIVE);
+ notifyOnMsgObject(message);
+ } else {
+ /* asynchronous handling */
+ WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
+ }
+ break;
+ /* Ignore */
+ case CMD_DISCONNECT:
+ case CMD_RECONNECT:
+ case CMD_REASSOCIATE:
+ case SUPPLICANT_STATE_CHANGE_EVENT:
+ case NETWORK_CONNECTION_EVENT:
+ case NETWORK_DISCONNECTION_EVENT:
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class ConnectModeState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ SyncParams syncParams;
+ StateChangeResult stateChangeResult;
+ switch(message.what) {
+ case PASSWORD_MAY_BE_INCORRECT_EVENT:
+ mPasswordKeyMayBeIncorrect = true;
+ break;
+ case SUPPLICANT_STATE_CHANGE_EVENT:
+ stateChangeResult = (StateChangeResult) message.obj;
+ mSupplicantStateTracker.handleEvent(stateChangeResult);
+ break;
+ case CMD_START_SCAN:
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = true;
+ notifyOnMsgObject(message);
+ }
+ /* We need to set scan type in completed state */
+ Message newMsg = obtainMessage();
+ newMsg.copyFrom(message);
+ mSupplicantStateTracker.sendMessage(newMsg);
+ break;
+ /* Do a redundant disconnect without transition */
+ case CMD_DISCONNECT:
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand();
+ notifyOnMsgObject(message);
+ } else {
+ /* asynchronous handling */
+ WifiNative.disconnectCommand();
+ }
+ break;
+ case CMD_RECONNECT:
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiNative.reconnectCommand();
+ notifyOnMsgObject(message);
+ } else {
+ /* asynchronous handling */
+ WifiNative.reconnectCommand();
+ }
+ break;
+ case CMD_REASSOCIATE:
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiNative.reassociateCommand();
+ notifyOnMsgObject(message);
+ } else {
+ /* asynchronous handling */
+ WifiNative.reassociateCommand();
+ }
+ break;
+ case SCAN_RESULTS_EVENT:
+ /* Set the scan setting back to "connect" mode */
+ WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
+ /* Handle scan results */
+ return NOT_HANDLED;
+ case NETWORK_CONNECTION_EVENT:
+ Log.d(TAG,"Network connection established");
+ stateChangeResult = (StateChangeResult) message.obj;
+
+ mWifiInfo.setBSSID(mLastBssid = stateChangeResult.BSSID);
+ mWifiInfo.setNetworkId(stateChangeResult.networkId);
+ mLastNetworkId = stateChangeResult.networkId;
+
+ /* send event to CM & network change broadcast */
+ setDetailedState(DetailedState.OBTAINING_IPADDR);
+ sendNetworkStateChangeBroadcast(mLastBssid);
+
+ transitionTo(mConnectingState);
+ break;
+ case NETWORK_DISCONNECTION_EVENT:
+ Log.d(TAG,"Network connection lost");
+ handleNetworkDisconnect();
+ transitionTo(mDisconnectedState);
+ break;
+ case CMD_GET_RSSI:
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.intValue = WifiNative.getRssiCommand();
+ notifyOnMsgObject(message);
+ break;
+ case CMD_GET_RSSI_APPROX:
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.intValue = WifiNative.getRssiApproxCommand();
+ notifyOnMsgObject(message);
+ break;
+ case CMD_GET_LINK_SPEED:
+ syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.intValue = WifiNative.getLinkSpeedCommand();
+ notifyOnMsgObject(message);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class ConnectingState extends HierarchicalState {
+ boolean modifiedBluetoothCoexistenceMode;
+ int powerMode;
+ Thread mDhcpThread;
+
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+
+ if (!mUseStaticIp) {
+
+ mDhcpThread = null;
+ modifiedBluetoothCoexistenceMode = false;
+ powerMode = DRIVER_POWER_MODE_AUTO;
+
+ if (shouldDisableCoexistenceMode()) {
+ /*
+ * There are problems setting the Wi-Fi driver's power
+ * mode to active when bluetooth coexistence mode is
+ * enabled or sense.
+ * <p>
+ * We set Wi-Fi to active mode when
+ * obtaining an IP address because we've found
+ * compatibility issues with some routers with low power
+ * mode.
+ * <p>
+ * In order for this active power mode to properly be set,
+ * we disable coexistence mode until we're done with
+ * obtaining an IP address. One exception is if we
+ * are currently connected to a headset, since disabling
+ * coexistence would interrupt that connection.
+ */
+ modifiedBluetoothCoexistenceMode = true;
+
+ // Disable the coexistence mode
+ WifiNative.setBluetoothCoexistenceModeCommand(
+ WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
+ }
+
+ powerMode = WifiNative.getPowerModeCommand();
+ if (powerMode < 0) {
+ // Handle the case where supplicant driver does not support
+ // getPowerModeCommand.
+ powerMode = DRIVER_POWER_MODE_AUTO;
+ }
+ if (powerMode != DRIVER_POWER_MODE_ACTIVE) {
+ WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_ACTIVE);
+ }
+
+ Log.d(TAG, "DHCP request started");
+ mDhcpThread = new Thread(new Runnable() {
+ public void run() {
+ if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) {
+ Log.d(TAG, "DHCP request succeeded");
+ sendMessage(CMD_IP_CONFIG_SUCCESS);
+ } else {
+ Log.d(TAG, "DHCP request failed: " +
+ NetworkUtils.getDhcpError());
+ sendMessage(CMD_IP_CONFIG_FAILURE);
+ }
+ }
+ });
+ mDhcpThread.start();
+ } else {
+ if (NetworkUtils.configureInterface(mInterfaceName, mDhcpInfo)) {
+ Log.v(TAG, "Static IP configuration succeeded");
+ sendMessage(CMD_IP_CONFIG_SUCCESS);
+ } else {
+ Log.v(TAG, "Static IP configuration failed");
+ sendMessage(CMD_IP_CONFIG_FAILURE);
+ }
+ }
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+
+ switch(message.what) {
+ case CMD_IP_CONFIG_SUCCESS:
+ mReconnectCount = 0;
+ mLastSignalLevel = -1; // force update of signal strength
+ mWifiInfo.setIpAddress(mDhcpInfo.ipAddress);
+ Log.d(TAG, "IP configuration: " + mDhcpInfo);
+ configureNetworkProperties();
+ setDetailedState(DetailedState.CONNECTED);
+ sendNetworkStateChangeBroadcast(mLastBssid);
+ //TODO: we could also detect an IP config change
+ // from a DHCP renewal and send out a config change
+ // broadcast
+ if (mConfigChanged) {
+ sendConfigChangeBroadcast();
+ mConfigChanged = false;
+ }
+ transitionTo(mConnectedState);
+ break;
+ case CMD_IP_CONFIG_FAILURE:
+ mWifiInfo.setIpAddress(0);
+
+ Log.e(TAG, "IP configuration failed");
+ /**
+ * If we've exceeded the maximum number of retries for DHCP
+ * to a given network, disable the network
+ */
+ if (++mReconnectCount > getMaxDhcpRetries()) {
+ Log.e(TAG, "Failed " +
+ mReconnectCount + " times, Disabling " + mLastNetworkId);
+ WifiNative.disableNetworkCommand(mLastNetworkId);
+ }
+
+ /* DHCP times out after about 30 seconds, we do a
+ * disconnect and an immediate reconnect to try again
+ */
+ WifiNative.disconnectCommand();
+ WifiNative.reconnectCommand();
+ transitionTo(mDisconnectingState);
+ break;
+ case CMD_DISCONNECT:
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ SyncParams syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand();
+ notifyOnMsgObject(message);
+ } else {
+ /* asynchronous handling */
+ WifiNative.disconnectCommand();
+ }
+ transitionTo(mDisconnectingState);
+ break;
+ /* Ignore */
+ case NETWORK_CONNECTION_EVENT:
+ break;
+ case CMD_STOP_DRIVER:
+ sendMessage(CMD_DISCONNECT);
+ deferMessage(message);
+ break;
+ case CMD_SET_SCAN_MODE:
+ if (message.arg1 == SCAN_ONLY_MODE) {
+ sendMessage(CMD_DISCONNECT);
+ deferMessage(message);
+ }
+ break;
+ case CMD_RECONFIGURE_IP:
+ deferMessage(message);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+
+ @Override
+ public void exit() {
+ /* reset power state & bluetooth coexistence if on DHCP */
+ if (!mUseStaticIp) {
+ if (powerMode != DRIVER_POWER_MODE_ACTIVE) {
+ WifiNative.setPowerModeCommand(powerMode);
+ }
+
+ if (modifiedBluetoothCoexistenceMode) {
+ // Set the coexistence mode back to its default value
+ WifiNative.setBluetoothCoexistenceModeCommand(
+ WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
+ }
+ }
+
+ }
+ }
+
+ class ConnectedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case CMD_DISCONNECT:
+ if (message.arg2 == SYNCHRONOUS_CALL) {
+ SyncParams syncParams = (SyncParams) message.obj;
+ syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand();
+ notifyOnMsgObject(message);
+ } else {
+ /* asynchronous handling */
+ WifiNative.disconnectCommand();
+ }
+ transitionTo(mDisconnectingState);
+ break;
+ case CMD_RECONFIGURE_IP:
+ Log.d(TAG,"Reconfiguring IP on connection");
+ NetworkUtils.resetConnections(mInterfaceName);
+ transitionTo(mConnectingState);
+ break;
+ case CMD_STOP_DRIVER:
+ sendMessage(CMD_DISCONNECT);
+ deferMessage(message);
+ break;
+ case CMD_SET_SCAN_MODE:
+ if (message.arg1 == SCAN_ONLY_MODE) {
+ sendMessage(CMD_DISCONNECT);
+ deferMessage(message);
+ }
+ break;
+ /* Ignore */
+ case NETWORK_CONNECTION_EVENT:
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DisconnectingState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case CMD_STOP_DRIVER: /* Stop driver only after disconnect handled */
+ deferMessage(message);
+ break;
+ case CMD_SET_SCAN_MODE:
+ if (message.arg1 == SCAN_ONLY_MODE) {
+ deferMessage(message);
+ }
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class DisconnectedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case CMD_SET_SCAN_MODE:
+ if (message.arg1 == SCAN_ONLY_MODE) {
+ WifiNative.setScanResultHandlingCommand(message.arg1);
+ //Supplicant disconnect to prevent further connects
+ WifiNative.disconnectCommand();
+ mIsScanMode = true;
+ transitionTo(mScanModeState);
+ }
+ break;
+ /* Ignore network disconnect */
+ case NETWORK_DISCONNECTION_EVENT:
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+ class SoftApStartedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch(message.what) {
+ case CMD_STOP_AP:
+ Log.d(TAG,"Stopping Soft AP");
+ setWifiApState(WIFI_AP_STATE_DISABLING);
+ try {
+ nwService.stopAccessPoint();
+ } catch(Exception e) {
+ Log.e(TAG, "Exception in stopAccessPoint()");
+ }
+ transitionTo(mDriverLoadedState);
+ break;
+ case CMD_START_AP:
+ Log.d(TAG,"SoftAP set on a running access point");
+ try {
+ nwService.setAccessPoint((WifiConfiguration) message.obj,
+ mInterfaceName,
+ SOFTAP_IFACE);
+ } catch(Exception e) {
+ Log.e(TAG, "Exception in nwService during soft AP set");
+ try {
+ nwService.stopAccessPoint();
+ } catch (Exception ee) {
+ Slog.e(TAG, "Could not stop AP, :" + ee);
+ }
+ sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0));
+ }
+ break;
+ /* Fail client mode operation when soft AP is enabled */
+ case CMD_START_SUPPLICANT:
+ Log.e(TAG,"Cannot start supplicant with a running soft AP");
+ setWifiState(WIFI_STATE_UNKNOWN);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
+ return HANDLED;
+ }
+ }
+
+
+ class SupplicantStateTracker extends HierarchicalStateMachine {
+
+ private int mRssiPollToken = 0;
+
+ /**
+ * The max number of the WPA supplicant loop iterations before we
+ * decide that the loop should be terminated:
+ */
+ private static final int MAX_SUPPLICANT_LOOP_ITERATIONS = 4;
+ private int mLoopDetectIndex = 0;
+ private int mLoopDetectCount = 0;
+
+ /**
+ * Supplicant state change commands follow
+ * the ordinal values defined in SupplicantState.java
+ */
+ private static final int DISCONNECTED = 0;
+ private static final int INACTIVE = 1;
+ private static final int SCANNING = 2;
+ private static final int ASSOCIATING = 3;
+ private static final int ASSOCIATED = 4;
+ private static final int FOUR_WAY_HANDSHAKE = 5;
+ private static final int GROUP_HANDSHAKE = 6;
+ private static final int COMPLETED = 7;
+ private static final int DORMANT = 8;
+ private static final int UNINITIALIZED = 9;
+ private static final int INVALID = 10;
+
+ private HierarchicalState mUninitializedState = new UninitializedState();
+ private HierarchicalState mInitializedState = new InitializedState();;
+ private HierarchicalState mInactiveState = new InactiveState();
+ private HierarchicalState mDisconnectState = new DisconnectedState();
+ private HierarchicalState mScanState = new ScanState();
+ private HierarchicalState mConnectState = new ConnectState();
+ private HierarchicalState mHandshakeState = new HandshakeState();
+ private HierarchicalState mCompletedState = new CompletedState();
+ private HierarchicalState mDormantState = new DormantState();
+
+ public SupplicantStateTracker(Context context, Handler target) {
+ super(TAG, target.getLooper());
+
+ addState(mUninitializedState);
+ addState(mInitializedState);
+ addState(mInactiveState, mInitializedState);
+ addState(mDisconnectState, mInitializedState);
+ addState(mScanState, mInitializedState);
+ addState(mConnectState, mInitializedState);
+ addState(mHandshakeState, mConnectState);
+ addState(mCompletedState, mConnectState);
+ addState(mDormantState, mInitializedState);
+
+ setInitialState(mUninitializedState);
+
+ //start the state machine
+ start();
+ }
+
+ public void handleEvent(StateChangeResult stateChangeResult) {
+ SupplicantState newState = (SupplicantState) stateChangeResult.state;
+
+ // Supplicant state change
+ // [31-13] Reserved for future use
+ // [8 - 0] Supplicant state (as defined in SupplicantState.java)
+ // 50023 supplicant_state_changed (custom|1|5)
+ EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, newState.ordinal());
+
+ sendMessage(obtainMessage(newState.ordinal(), stateChangeResult));
+ }
+
+ public void resetSupplicantState() {
+ transitionTo(mUninitializedState);
+ }
+
+ private void resetLoopDetection() {
+ mLoopDetectCount = 0;
+ mLoopDetectIndex = 0;
+ }
+
+ private boolean handleTransition(Message msg) {
+ if (DBG) Log.d(TAG, getName() + msg.toString() + "\n");
+ switch (msg.what) {
+ case DISCONNECTED:
+ transitionTo(mDisconnectState);
+ break;
+ case SCANNING:
+ transitionTo(mScanState);
+ break;
+ case ASSOCIATING:
+ StateChangeResult stateChangeResult = (StateChangeResult) msg.obj;
+ /* BSSID is valid only in ASSOCIATING state */
+ mWifiInfo.setBSSID(stateChangeResult.BSSID);
+ //$FALL-THROUGH$
+ case ASSOCIATED:
+ case FOUR_WAY_HANDSHAKE:
+ case GROUP_HANDSHAKE:
+ transitionTo(mHandshakeState);
+ break;
+ case COMPLETED:
+ transitionTo(mCompletedState);
+ break;
+ case DORMANT:
+ transitionTo(mDormantState);
+ break;
+ case INACTIVE:
+ transitionTo(mInactiveState);
+ break;
+ case UNINITIALIZED:
+ case INVALID:
+ transitionTo(mUninitializedState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ StateChangeResult stateChangeResult = (StateChangeResult) msg.obj;
+ SupplicantState supState = (SupplicantState) stateChangeResult.state;
+ setDetailedState(WifiInfo.getDetailedStateOf(supState));
+ mWifiInfo.setSupplicantState(supState);
+ mWifiInfo.setNetworkId(stateChangeResult.networkId);
+ //TODO: Modify WifiMonitor to report SSID on events
+ //mWifiInfo.setSSID()
+ return HANDLED;
+ }
+
+ /********************************************************
+ * HSM states
+ *******************************************************/
+
+ class InitializedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+ switch (message.what) {
+ case CMD_START_SCAN:
+ WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
+ break;
+ default:
+ if (DBG) Log.w(TAG, "Ignoring " + message);
+ break;
+ }
+ return HANDLED;
+ }
+ }
+
+ class UninitializedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ mNetworkInfo.setIsAvailable(false);
+ resetLoopDetection();
+ mPasswordKeyMayBeIncorrect = false;
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ switch(message.what) {
+ default:
+ if (!handleTransition(message)) {
+ if (DBG) Log.w(TAG, "Ignoring " + message);
+ }
+ break;
+ }
+ return HANDLED;
+ }
+ @Override
+ public void exit() {
+ mNetworkInfo.setIsAvailable(true);
+ }
+ }
+
+ class InactiveState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ Message message = getCurrentMessage();
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+ mNetworkInfo.setIsAvailable(false);
+ resetLoopDetection();
+ mPasswordKeyMayBeIncorrect = false;
+
+ sendSupplicantStateChangedBroadcast(stateChangeResult, false);
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ return handleTransition(message);
+ }
+ @Override
+ public void exit() {
+ mNetworkInfo.setIsAvailable(true);
+ }
+ }
+
+
+ class DisconnectedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ Message message = getCurrentMessage();
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+ resetLoopDetection();
+
+ /* If a disconnect event happens after a password key failure
+ * event, disable the network
+ */
+ if (mPasswordKeyMayBeIncorrect) {
+ Log.d(TAG, "Failed to authenticate, disabling network " +
+ mWifiInfo.getNetworkId());
+ WifiNative.disableNetworkCommand(mWifiInfo.getNetworkId());
+ mPasswordKeyMayBeIncorrect = false;
+ sendSupplicantStateChangedBroadcast(stateChangeResult, true);
+ }
+ else {
+ sendSupplicantStateChangedBroadcast(stateChangeResult, false);
+ }
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ return handleTransition(message);
+ }
+ }
+
+ class ScanState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ Message message = getCurrentMessage();
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+ mPasswordKeyMayBeIncorrect = false;
+ resetLoopDetection();
+ sendSupplicantStateChangedBroadcast(stateChangeResult, false);
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ return handleTransition(message);
+ }
+ }
+
+ class ConnectState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ switch (message.what) {
+ case CMD_START_SCAN:
+ WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);
+ WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ class HandshakeState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ final Message message = getCurrentMessage();
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+ if (mLoopDetectIndex > message.what) {
+ mLoopDetectCount++;
+ }
+ if (mLoopDetectCount > MAX_SUPPLICANT_LOOP_ITERATIONS) {
+ WifiNative.disableNetworkCommand(stateChangeResult.networkId);
+ mLoopDetectCount = 0;
+ }
+
+ mLoopDetectIndex = message.what;
+
+ mPasswordKeyMayBeIncorrect = false;
+ sendSupplicantStateChangedBroadcast(stateChangeResult, false);
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ return handleTransition(message);
+ }
+ }
+
+ class CompletedState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ Message message = getCurrentMessage();
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+ mRssiPollToken++;
+ if (mEnableRssiPolling) {
+ sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
+ POLL_RSSI_INTERVAL_MSECS);
+ }
+
+ resetLoopDetection();
+
+ mPasswordKeyMayBeIncorrect = false;
+ sendSupplicantStateChangedBroadcast(stateChangeResult, false);
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ switch(message.what) {
+ case ASSOCIATING:
+ case ASSOCIATED:
+ case FOUR_WAY_HANDSHAKE:
+ case GROUP_HANDSHAKE:
+ case COMPLETED:
+ break;
+ case CMD_RSSI_POLL:
+ if (message.arg1 == mRssiPollToken) {
+ // Get Info and continue polling
+ requestPolledInfo();
+ sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
+ POLL_RSSI_INTERVAL_MSECS);
+ } else {
+ // Polling has completed
+ }
+ break;
+ case CMD_ENABLE_RSSI_POLL:
+ mRssiPollToken++;
+ if (mEnableRssiPolling) {
+ // first poll
+ requestPolledInfo();
+ sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
+ POLL_RSSI_INTERVAL_MSECS);
+ }
+ break;
+ default:
+ return handleTransition(message);
+ }
+ return HANDLED;
+ }
+ }
+
+ class DormantState extends HierarchicalState {
+ @Override
+ public void enter() {
+ if (DBG) Log.d(TAG, getName() + "\n");
+ Message message = getCurrentMessage();
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+
+ resetLoopDetection();
+ mPasswordKeyMayBeIncorrect = false;
+
+ sendSupplicantStateChangedBroadcast(stateChangeResult, false);
+
+ /* TODO: reconnect is now being handled at DHCP failure handling
+ * If we run into issues with staying in Dormant state, might
+ * need a reconnect here
+ */
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ return handleTransition(message);
+ }
+ }
+ }
+}
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index 388beead9022..8e1b23697d14 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -16,524 +16,59 @@
package android.net.wifi;
-import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
-import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING;
-import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
-import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
-import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
-/**
- * TODO: Add soft AP states as part of WIFI_STATE_XXX
- * Retain WIFI_STATE_ENABLING that indicates driver is loading
- * Add WIFI_STATE_AP_ENABLED to indicate soft AP has started
- * and WIFI_STATE_FAILED for failure
- * Deprecate WIFI_STATE_UNKNOWN
- *
- * Doing this will simplify the logic for sending broadcasts
- */
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
-import android.app.ActivityManagerNative;
-import android.net.NetworkInfo;
-import android.net.NetworkStateTracker;
-import android.net.DhcpInfo;
-import android.net.NetworkUtils;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.net.ConnectivityManager;
-import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkInfo.State;
+import android.net.NetworkInfo;
import android.net.NetworkProperties;
-import android.os.Binder;
-import android.os.Message;
-import android.os.Parcelable;
+import android.net.NetworkStateTracker;
import android.os.Handler;
-import android.os.IBinder;
-import android.os.INetworkManagementService;
-import android.os.PowerManager;
-import android.os.SystemProperties;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.Process;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.EventLog;
-import android.util.Log;
-import android.util.Slog;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.backup.IBackupManager;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothHeadset;
-import android.bluetooth.BluetoothA2dp;
-import android.content.ContentResolver;
-import android.content.Intent;
-import android.content.Context;
-import android.database.ContentObserver;
-import com.android.internal.app.IBatteryStats;
-import com.android.internal.util.HierarchicalState;
-import com.android.internal.util.HierarchicalStateMachine;
+import android.os.Message;
-import java.net.InetAddress;
-import java.net.NetworkInterface;
-import java.net.SocketException;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.BitSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.regex.Pattern;
/**
- * Track the state of Wifi connectivity. All event handling is done here,
- * and all changes in connectivity state are initiated here.
+ * Track the state of wifi for connectivity service.
*
* @hide
*/
-//TODO: we still need frequent scanning for the case when
-// we issue disconnect but need scan results for open network notification
-public class WifiStateTracker extends HierarchicalStateMachine implements NetworkStateTracker {
+public class WifiStateTracker implements NetworkStateTracker {
- private static final String TAG = "WifiStateTracker";
private static final String NETWORKTYPE = "WIFI";
- private static final boolean DBG = false;
-
- /* TODO: fetch a configurable interface */
- private static final String SOFTAP_IFACE = "wl0.1";
-
- private WifiMonitor mWifiMonitor;
- private INetworkManagementService nwService;
- private ConnectivityManager mCm;
-
- /* Scan results handling */
- private List<ScanResult> mScanResults;
- private static final Pattern scanResultPattern = Pattern.compile("\t+");
- private static final int SCAN_RESULT_CACHE_SIZE = 80;
- private final LinkedHashMap<String, ScanResult> mScanResultCache;
+ private static final String TAG = "WifiStateTracker";
- private String mInterfaceName;
private AtomicBoolean mTeardownRequested = new AtomicBoolean(false);
private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false);
private AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0);
private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false);
- private int mNumAllowedChannels = 0;
- private int mLastSignalLevel = -1;
- private String mLastBssid;
- private int mLastNetworkId;
- private boolean mEnableRssiPolling = false;
- private boolean mPasswordKeyMayBeIncorrect = false;
- private boolean mUseStaticIp = false;
- private int mReconnectCount = 0;
- private boolean mIsScanMode = false;
-
- /**
- * Instance of the bluetooth headset helper. This needs to be created
- * early because there is a delay before it actually 'connects', as
- * noted by its javadoc. If we check before it is connected, it will be
- * in an error state and we will not disable coexistence.
- */
- private BluetoothHeadset mBluetoothHeadset;
-
- private BluetoothA2dp mBluetoothA2dp;
-
- /**
- * Observes the static IP address settings.
- */
- private SettingsObserver mSettingsObserver;
private NetworkProperties mNetworkProperties;
-
-
- // Variables relating to the 'available networks' notification
- /**
- * The icon to show in the 'available networks' notification. This will also
- * be the ID of the Notification given to the NotificationManager.
- */
- private static final int ICON_NETWORKS_AVAILABLE =
- com.android.internal.R.drawable.stat_notify_wifi_in_range;
- /**
- * When a notification is shown, we wait this amount before possibly showing it again.
- */
- private final long NOTIFICATION_REPEAT_DELAY_MS;
- /**
- * Whether the user has set the setting to show the 'available networks' notification.
- */
- private boolean mNotificationEnabled;
- /**
- * Observes the user setting to keep {@link #mNotificationEnabled} in sync.
- */
- private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver;
- /**
- * The {@link System#currentTimeMillis()} must be at least this value for us
- * to show the notification again.
- */
- private long mNotificationRepeatTime;
- /**
- * The Notification object given to the NotificationManager.
- */
- private Notification mNotification;
- /**
- * Whether the notification is being shown, as set by us. That is, if the
- * user cancels the notification, we will not receive the callback so this
- * will still be true. We only guarantee if this is false, then the
- * notification is not showing.
- */
- private boolean mNotificationShown;
- /**
- * The number of continuous scans that must occur before consider the
- * supplicant in a scanning state. This allows supplicant to associate with
- * remembered networks that are in the scan results.
- */
- private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3;
- /**
- * The number of scans since the last network state change. When this
- * exceeds {@link #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the
- * supplicant to actually be scanning. When the network state changes to
- * something other than scanning, we reset this to 0.
- */
- private int mNumScansSinceNetworkStateChange;
-
- // Held during driver load and unload
- private static PowerManager.WakeLock sWakeLock;
+ private NetworkInfo mNetworkInfo;
/* For sending events to connectivity service handler */
private Handler mCsHandler;
private Context mContext;
-
- private DhcpInfo mDhcpInfo;
- private WifiInfo mWifiInfo;
- private NetworkInfo mNetworkInfo;
- private SupplicantStateTracker mSupplicantStateTracker;
-
- // Event log tags (must be in sync with event-log-tags)
- private static final int EVENTLOG_WIFI_STATE_CHANGED = 50021;
- private static final int EVENTLOG_WIFI_EVENT_HANDLED = 50022;
- private static final int EVENTLOG_SUPPLICANT_STATE_CHANGED = 50023;
-
- /* Load the driver */
- private static final int CMD_LOAD_DRIVER = 1;
- /* Unload the driver */
- private static final int CMD_UNLOAD_DRIVER = 2;
- /* Indicates driver load succeeded */
- private static final int CMD_LOAD_DRIVER_SUCCESS = 3;
- /* Indicates driver load failed */
- private static final int CMD_LOAD_DRIVER_FAILURE = 4;
- /* Indicates driver unload succeeded */
- private static final int CMD_UNLOAD_DRIVER_SUCCESS = 5;
- /* Indicates driver unload failed */
- private static final int CMD_UNLOAD_DRIVER_FAILURE = 6;
-
- /* Start the supplicant */
- private static final int CMD_START_SUPPLICANT = 11;
- /* Stop the supplicant */
- private static final int CMD_STOP_SUPPLICANT = 12;
- /* Start the driver */
- private static final int CMD_START_DRIVER = 13;
- /* Start the driver */
- private static final int CMD_STOP_DRIVER = 14;
- /* Indicates DHCP succeded */
- private static final int CMD_IP_CONFIG_SUCCESS = 15;
- /* Indicates DHCP failed */
- private static final int CMD_IP_CONFIG_FAILURE = 16;
- /* Re-configure interface */
- private static final int CMD_RECONFIGURE_IP = 17;
-
-
- /* Start the soft access point */
- private static final int CMD_START_AP = 21;
- /* Stop the soft access point */
- private static final int CMD_STOP_AP = 22;
-
-
- /* Supplicant events */
- /* Connection to supplicant established */
- private static final int SUP_CONNECTION_EVENT = 31;
- /* Connection to supplicant lost */
- private static final int SUP_DISCONNECTION_EVENT = 32;
- /* Driver start completed */
- private static final int DRIVER_START_EVENT = 33;
- /* Driver stop completed */
- private static final int DRIVER_STOP_EVENT = 34;
- /* Network connection completed */
- private static final int NETWORK_CONNECTION_EVENT = 36;
- /* Network disconnection completed */
- private static final int NETWORK_DISCONNECTION_EVENT = 37;
- /* Scan results are available */
- private static final int SCAN_RESULTS_EVENT = 38;
- /* Supplicate state changed */
- private static final int SUPPLICANT_STATE_CHANGE_EVENT = 39;
- /* Password may be incorrect */
- private static final int PASSWORD_MAY_BE_INCORRECT_EVENT = 40;
-
- /* Supplicant commands */
- /* Is supplicant alive ? */
- private static final int CMD_PING_SUPPLICANT = 51;
- /* Add/update a network configuration */
- private static final int CMD_ADD_OR_UPDATE_NETWORK = 52;
- /* Delete a network */
- private static final int CMD_REMOVE_NETWORK = 53;
- /* Enable a network. The device will attempt a connection to the given network. */
- private static final int CMD_ENABLE_NETWORK = 54;
- /* Disable a network. The device does not attempt a connection to the given network. */
- private static final int CMD_DISABLE_NETWORK = 55;
- /* Blacklist network. De-prioritizes the given BSSID for connection. */
- private static final int CMD_BLACKLIST_NETWORK = 56;
- /* Clear the blacklist network list */
- private static final int CMD_CLEAR_BLACKLIST = 57;
- /* Get the configured networks */
- private static final int CMD_GET_NETWORK_CONFIG = 58;
- /* Save configuration */
- private static final int CMD_SAVE_CONFIG = 59;
- /* Connection status */
- private static final int CMD_CONNECTION_STATUS = 60;
-
- /* Supplicant commands after driver start*/
- /* Initiate a scan */
- private static final int CMD_START_SCAN = 71;
- /* Set scan mode. CONNECT_MODE or SCAN_ONLY_MODE */
- private static final int CMD_SET_SCAN_MODE = 72;
- /* Set scan type. SCAN_ACTIVE or SCAN_PASSIVE */
- private static final int CMD_SET_SCAN_TYPE = 73;
- /* Disconnect from a network */
- private static final int CMD_DISCONNECT = 74;
- /* Reconnect to a network */
- private static final int CMD_RECONNECT = 75;
- /* Reassociate to a network */
- private static final int CMD_REASSOCIATE = 76;
- /* Set power mode
- * POWER_MODE_ACTIVE
- * POWER_MODE_AUTO
- */
- private static final int CMD_SET_POWER_MODE = 77;
- /* Set bluetooth co-existence
- * BLUETOOTH_COEXISTENCE_MODE_ENABLED
- * BLUETOOTH_COEXISTENCE_MODE_DISABLED
- * BLUETOOTH_COEXISTENCE_MODE_SENSE
- */
- private static final int CMD_SET_BLUETOOTH_COEXISTENCE = 78;
- /* Enable/disable bluetooth scan mode
- * true(1)
- * false(0)
- */
- private static final int CMD_SET_BLUETOOTH_SCAN_MODE = 79;
- /* Set number of allowed channels */
- private static final int CMD_SET_NUM_ALLOWED_CHANNELS = 80;
- /* Request connectivity manager wake lock before driver stop */
- private static final int CMD_REQUEST_CM_WAKELOCK = 81;
- /* Enables RSSI poll */
- private static final int CMD_ENABLE_RSSI_POLL = 82;
- /* RSSI poll */
- private static final int CMD_RSSI_POLL = 83;
- /* Get current RSSI */
- private static final int CMD_GET_RSSI = 84;
- /* Get approx current RSSI */
- private static final int CMD_GET_RSSI_APPROX = 85;
- /* Get link speed on connection */
- private static final int CMD_GET_LINK_SPEED = 86;
- /* Radio mac address */
- private static final int CMD_GET_MAC_ADDR = 87;
- /* Set up packet filtering */
- private static final int CMD_START_PACKET_FILTERING = 88;
- /* Clear packet filter */
- private static final int CMD_STOP_PACKET_FILTERING = 89;
-
- /* Connectivity service commands */
- /* Bring down wifi connection */
- private static final int CM_CMD_TEARDOWN = 110;
- /* Reconnect to wifi */
- private static final int CM_CMD_RECONNECT = 111;
-
- /**
- * Interval in milliseconds between polling for connection
- * status items that are not sent via asynchronous events.
- * An example is RSSI (signal strength).
- */
- private static final int POLL_RSSI_INTERVAL_MSECS = 3000;
-
- private static final int CONNECT_MODE = 1;
- private static final int SCAN_ONLY_MODE = 2;
-
- private static final int SCAN_ACTIVE = 1;
- private static final int SCAN_PASSIVE = 2;
-
- /**
- * The maximum number of times we will retry a connection to an access point
- * for which we have failed in acquiring an IP address from DHCP. A value of
- * N means that we will make N+1 connection attempts in all.
- * <p>
- * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
- * value if a Settings value is not present.
- */
- private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
-
- private static final int DRIVER_POWER_MODE_ACTIVE = 1;
- private static final int DRIVER_POWER_MODE_AUTO = 0;
-
- /* Default parent state */
- private HierarchicalState mDefaultState = new DefaultState();
- /* Temporary initial state */
- private HierarchicalState mInitialState = new InitialState();
- /* Unloading the driver */
- private HierarchicalState mDriverUnloadingState = new DriverUnloadingState();
- /* Loading the driver */
- private HierarchicalState mDriverUnloadedState = new DriverUnloadedState();
- /* Driver load/unload failed */
- private HierarchicalState mDriverFailedState = new DriverFailedState();
- /* Driver loading */
- private HierarchicalState mDriverLoadingState = new DriverLoadingState();
- /* Driver loaded */
- private HierarchicalState mDriverLoadedState = new DriverLoadedState();
- /* Driver loaded, waiting for supplicant to start */
- private HierarchicalState mWaitForSupState = new WaitForSupState();
-
- /* Driver loaded and supplicant ready */
- private HierarchicalState mDriverSupReadyState = new DriverSupReadyState();
- /* Driver start issued, waiting for completed event */
- private HierarchicalState mDriverStartingState = new DriverStartingState();
- /* Driver started */
- private HierarchicalState mDriverStartedState = new DriverStartedState();
- /* Driver stopping */
- private HierarchicalState mDriverStoppingState = new DriverStoppingState();
- /* Driver stopped */
- private HierarchicalState mDriverStoppedState = new DriverStoppedState();
- /* Scan for networks, no connection will be established */
- private HierarchicalState mScanModeState = new ScanModeState();
- /* Connecting to an access point */
- private HierarchicalState mConnectModeState = new ConnectModeState();
- /* Fetching IP after network connection (assoc+auth complete) */
- private HierarchicalState mConnectingState = new ConnectingState();
- /* Connected with IP addr */
- private HierarchicalState mConnectedState = new ConnectedState();
- /* disconnect issued, waiting for network disconnect confirmation */
- private HierarchicalState mDisconnectingState = new DisconnectingState();
- /* Network is not connected, supplicant assoc+auth is not complete */
- private HierarchicalState mDisconnectedState = new DisconnectedState();
-
- /* Soft Ap is running */
- private HierarchicalState mSoftApStartedState = new SoftApStartedState();
-
- /* Argument for Message object to indicate a synchronous call */
- private static final int SYNCHRONOUS_CALL = 1;
- private static final int ASYNCHRONOUS_CALL = 0;
-
-
- /**
- * One of {@link WifiManager#WIFI_STATE_DISABLED},
- * {@link WifiManager#WIFI_STATE_DISABLING},
- * {@link WifiManager#WIFI_STATE_ENABLED},
- * {@link WifiManager#WIFI_STATE_ENABLING},
- * {@link WifiManager#WIFI_STATE_UNKNOWN}
- *
- */
- private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_DISABLED);
-
- /**
- * One of {@link WifiManager#WIFI_AP_STATE_DISABLED},
- * {@link WifiManager#WIFI_AP_STATE_DISABLING},
- * {@link WifiManager#WIFI_AP_STATE_ENABLED},
- * {@link WifiManager#WIFI_AP_STATE_ENABLING},
- * {@link WifiManager#WIFI_AP_STATE_FAILED}
- *
- */
- private final AtomicInteger mWifiApState = new AtomicInteger(WIFI_AP_STATE_DISABLED);
-
- private final AtomicInteger mLastEnableUid = new AtomicInteger(Process.myUid());
- private final AtomicInteger mLastApEnableUid = new AtomicInteger(Process.myUid());
-
- private final IBatteryStats mBatteryStats;
+ private BroadcastReceiver mWifiStateReceiver;
+ private WifiManager mWifiManager;
public WifiStateTracker(Context context, Handler target) {
- super(NETWORKTYPE);
-
mCsHandler = target;
mContext = context;
mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
- mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
-
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- nwService = INetworkManagementService.Stub.asInterface(b);
-
- mWifiMonitor = new WifiMonitor(this);
- mDhcpInfo = new DhcpInfo();
- mWifiInfo = new WifiInfo();
- mInterfaceName = SystemProperties.get("wifi.interface", "tiwlan0");
- mSupplicantStateTracker = new SupplicantStateTracker(context, getHandler());
-
- mBluetoothHeadset = new BluetoothHeadset(mContext, null);
mNetworkProperties = new NetworkProperties();
mNetworkInfo.setIsAvailable(false);
mNetworkProperties.clear();
- resetNotificationTimer();
setTeardownRequested(false);
- mLastBssid = null;
- mLastNetworkId = -1;
- mLastSignalLevel = -1;
-
- mScanResultCache = new LinkedHashMap<String, ScanResult>(
- SCAN_RESULT_CACHE_SIZE, 0.75f, true) {
- /*
- * Limit the cache size by SCAN_RESULT_CACHE_SIZE
- * elements
- */
- @Override
- public boolean removeEldestEntry(Map.Entry eldest) {
- return SCAN_RESULT_CACHE_SIZE < this.size();
- }
- };
-
- // Setting is in seconds
- NOTIFICATION_REPEAT_DELAY_MS = Settings.Secure.getInt(context.getContentResolver(),
- Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l;
- mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(target);
- mNotificationEnabledSettingObserver.register();
-
- mSettingsObserver = new SettingsObserver(new Handler());
-
- PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
- sWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
-
- addState(mDefaultState);
- addState(mInitialState, mDefaultState);
- addState(mDriverUnloadingState, mDefaultState);
- addState(mDriverUnloadedState, mDefaultState);
- addState(mDriverFailedState, mDriverUnloadedState);
- addState(mDriverLoadingState, mDefaultState);
- addState(mDriverLoadedState, mDefaultState);
- addState(mWaitForSupState, mDriverLoadedState);
- addState(mDriverSupReadyState, mDefaultState);
- addState(mDriverStartingState, mDriverSupReadyState);
- addState(mDriverStartedState, mDriverSupReadyState);
- addState(mScanModeState, mDriverStartedState);
- addState(mConnectModeState, mDriverStartedState);
- addState(mConnectingState, mConnectModeState);
- addState(mConnectedState, mConnectModeState);
- addState(mDisconnectingState, mConnectModeState);
- addState(mDisconnectedState, mConnectModeState);
- addState(mDriverStoppingState, mDriverSupReadyState);
- addState(mDriverStoppedState, mDriverSupReadyState);
- addState(mSoftApStartedState, mDefaultState);
-
- setInitialState(mInitialState);
-
- if (DBG) setDbg(true);
-
- //start the state machine
- start();
}
- /*********************************************************
- * NetworkStateTracker interface implementation
- ********************************************************/
-
public void setTeardownRequested(boolean isRequested) {
mTeardownRequested.set(isRequested);
}
@@ -544,11 +79,17 @@ public class WifiStateTracker extends HierarchicalStateMachine implements Networ
/**
* Begin monitoring wifi connectivity
- * @deprecated
- *
- * TODO: remove this from callers
*/
public void startMonitoring() {
+ mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.CONFIG_CHANGED_ACTION);
+ filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+
+ mWifiStateReceiver = new WifiStateReceiver();
+ mContext.registerReceiver(mWifiStateReceiver, filter);
}
/**
@@ -556,7 +97,8 @@ public class WifiStateTracker extends HierarchicalStateMachine implements Networ
* TODO: do away with return value after making MobileDataStateTracker async
*/
public boolean teardown() {
- sendMessage(CM_CMD_TEARDOWN);
+ mTeardownRequested.set(true);
+ mWifiManager.stopWifi();
return true;
}
@@ -565,7 +107,8 @@ public class WifiStateTracker extends HierarchicalStateMachine implements Networ
* TODO: do away with return value after making MobileDataStateTracker async
*/
public boolean reconnect() {
- sendMessage(CM_CMD_RECONNECT);
+ mTeardownRequested.set(false);
+ mWifiManager.startWifi();
return true;
}
@@ -575,7 +118,7 @@ public class WifiStateTracker extends HierarchicalStateMachine implements Networ
* TODO: do away with return value after making MobileDataStateTracker async
*/
public boolean setRadio(boolean turnOn) {
- setWifiEnabled(turnOn);
+ mWifiManager.setWifiEnabled(turnOn);
return true;
}
@@ -626,21 +169,6 @@ public class WifiStateTracker extends HierarchicalStateMachine implements Networ
return -1;
}
- /* TODO: will go away.
- * Notifications are directly handled in WifiStateTracker at checkAndSetNotification()
- */
- public void interpretScanResultsAvailable() {
-
- }
-
- /**
- * Return the name of our WLAN network interface.
- * @return the name of our interface.
- */
- public String getInterfaceName() {
- return mInterfaceName;
- }
-
/**
* Check if private DNS route is set for the network
*/
@@ -698,3279 +226,23 @@ public class WifiStateTracker extends HierarchicalStateMachine implements Networ
return "net.tcp.buffersize.wifi";
}
-
- /*********************************************************
- * Methods exposed for public use
- ********************************************************/
-
- /**
- * TODO: doc
- */
- public boolean pingSupplicant() {
- return sendSyncMessage(CMD_PING_SUPPLICANT).boolValue;
- }
-
- /**
- * TODO: doc
- */
- public boolean startScan(boolean forceActive) {
- return sendSyncMessage(obtainMessage(CMD_START_SCAN, forceActive ?
- SCAN_ACTIVE : SCAN_PASSIVE, 0)).boolValue;
- }
-
- /**
- * TODO: doc
- */
- public void setWifiEnabled(boolean enable) {
- mLastEnableUid.set(Binder.getCallingUid());
- if (enable) {
- /* Argument is the state that is entered prior to load */
- sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_STATE_ENABLING, 0));
- sendMessage(CMD_START_SUPPLICANT);
- } else {
- sendMessage(CMD_STOP_SUPPLICANT);
- /* Argument is the state that is entered upon success */
- sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_DISABLED, 0));
- }
- }
-
- /**
- * TODO: doc
- */
- public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enable) {
- mLastApEnableUid.set(Binder.getCallingUid());
- if (enable) {
- /* Argument is the state that is entered prior to load */
- sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_AP_STATE_ENABLING, 0));
- sendMessage(obtainMessage(CMD_START_AP, wifiConfig));
- } else {
- sendMessage(CMD_STOP_AP);
- /* Argument is the state that is entered upon success */
- sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_DISABLED, 0));
- }
- }
-
- /**
- * TODO: doc
- */
- public int getWifiState() {
- return mWifiState.get();
- }
-
- /**
- * TODO: doc
- */
- public String getWifiStateByName() {
- switch (mWifiState.get()) {
- case WIFI_STATE_DISABLING:
- return "disabling";
- case WIFI_STATE_DISABLED:
- return "disabled";
- case WIFI_STATE_ENABLING:
- return "enabling";
- case WIFI_STATE_ENABLED:
- return "enabled";
- case WIFI_STATE_UNKNOWN:
- return "unknown state";
- default:
- return "[invalid state]";
- }
- }
-
- /**
- * TODO: doc
- */
- public int getWifiApState() {
- return mWifiApState.get();
- }
-
- /**
- * TODO: doc
- */
- public String getWifiApStateByName() {
- switch (mWifiApState.get()) {
- case WIFI_AP_STATE_DISABLING:
- return "disabling";
- case WIFI_AP_STATE_DISABLED:
- return "disabled";
- case WIFI_AP_STATE_ENABLING:
- return "enabling";
- case WIFI_AP_STATE_ENABLED:
- return "enabled";
- case WIFI_AP_STATE_FAILED:
- return "failed";
- default:
- return "[invalid state]";
- }
- }
-
- /**
- * Get status information for the current connection, if any.
- * @return a {@link WifiInfo} object containing information about the current connection
- *
- */
- public WifiInfo requestConnectionInfo() {
- return mWifiInfo;
- }
-
- public DhcpInfo getDhcpInfo() {
- return mDhcpInfo;
- }
-
- /**
- * TODO: doc
- */
- public void startWifi(boolean enable) {
- if (enable) {
- sendMessage(CMD_START_DRIVER);
- } else {
- sendMessage(CMD_STOP_DRIVER);
- }
- }
-
- /**
- * TODO: doc
- */
- public void disconnectAndStop() {
- sendMessage(CMD_DISCONNECT);
- sendMessage(CMD_STOP_DRIVER);
- }
-
- /**
- * TODO: doc
- */
- public void setScanOnlyMode(boolean enable) {
- if (enable) {
- sendMessage(obtainMessage(CMD_SET_SCAN_MODE, SCAN_ONLY_MODE, 0));
- } else {
- sendMessage(obtainMessage(CMD_SET_SCAN_MODE, CONNECT_MODE, 0));
- }
- }
-
- /**
- * TODO: doc
- */
- public void setScanType(boolean active) {
- if (active) {
- sendMessage(obtainMessage(CMD_SET_SCAN_TYPE, SCAN_ACTIVE, 0));
- } else {
- sendMessage(obtainMessage(CMD_SET_SCAN_TYPE, SCAN_PASSIVE, 0));
- }
- }
-
- /**
- * TODO: doc
- */
- public List<ScanResult> getScanResultsList() {
- return mScanResults;
- }
-
- /**
- * Disconnect from Access Point
- */
- public boolean disconnectCommand() {
- return sendSyncMessage(CMD_DISCONNECT).boolValue;
- }
-
- /**
- * Initiate a reconnection to AP
- */
- public boolean reconnectCommand() {
- return sendSyncMessage(CMD_RECONNECT).boolValue;
- }
-
- /**
- * Initiate a re-association to AP
- */
- public boolean reassociateCommand() {
- return sendSyncMessage(CMD_REASSOCIATE).boolValue;
- }
-
- /**
- * Add a network synchronously
- *
- * @return network id of the new network
- */
- public int addOrUpdateNetwork(WifiConfiguration config) {
- return sendSyncMessage(CMD_ADD_OR_UPDATE_NETWORK, config).intValue;
- }
-
- public List<WifiConfiguration> getConfiguredNetworks() {
- return sendSyncMessage(CMD_GET_NETWORK_CONFIG).configList;
- }
-
- /**
- * Delete a network
- *
- * @param networkId id of the network to be removed
- */
- public boolean removeNetwork(int networkId) {
- return sendSyncMessage(obtainMessage(CMD_REMOVE_NETWORK, networkId, 0)).boolValue;
- }
-
- private class EnableNetParams {
- private int netId;
- private boolean disableOthers;
- EnableNetParams(int n, boolean b) {
- netId = n;
- disableOthers = b;
- }
- }
- /**
- * Enable a network
- *
- * @param netId network id of the network
- * @param disableOthers true, if all other networks have to be disabled
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public boolean enableNetwork(int netId, boolean disableOthers) {
- return sendSyncMessage(CMD_ENABLE_NETWORK,
- new EnableNetParams(netId, disableOthers)).boolValue;
- }
-
- /**
- * Disable a network
- *
- * @param netId network id of the network
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public boolean disableNetwork(int netId) {
- return sendSyncMessage(obtainMessage(CMD_DISABLE_NETWORK, netId, 0)).boolValue;
- }
-
- /**
- * Blacklist a BSSID. This will avoid the AP if there are
- * alternate APs to connect
- *
- * @param bssid BSSID of the network
- */
- public void addToBlacklist(String bssid) {
- sendMessage(obtainMessage(CMD_BLACKLIST_NETWORK, bssid));
- }
-
- /**
- * Clear the blacklist list
- *
- */
- public void clearBlacklist() {
- sendMessage(obtainMessage(CMD_CLEAR_BLACKLIST));
- }
-
- /**
- * Get detailed status of the connection
- *
- * @return Example status result
- * bssid=aa:bb:cc:dd:ee:ff
- * ssid=TestNet
- * id=3
- * pairwise_cipher=NONE
- * group_cipher=NONE
- * key_mgmt=NONE
- * wpa_state=COMPLETED
- * ip_address=X.X.X.X
- */
- public String status() {
- return sendSyncMessage(CMD_CONNECTION_STATUS).stringValue;
- }
-
- public void enableRssiPolling(boolean enabled) {
- sendMessage(obtainMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0));
- }
- /**
- * Get RSSI to currently connected network
- *
- * @return RSSI value, -1 on failure
- */
- public int getRssi() {
- return sendSyncMessage(CMD_GET_RSSI).intValue;
- }
-
- /**
- * Get approx RSSI to currently connected network
- *
- * @return RSSI value, -1 on failure
- */
- public int getRssiApprox() {
- return sendSyncMessage(CMD_GET_RSSI_APPROX).intValue;
- }
-
- /**
- * Get link speed to currently connected network
- *
- * @return link speed, -1 on failure
- */
- public int getLinkSpeed() {
- return sendSyncMessage(CMD_GET_LINK_SPEED).intValue;
- }
-
- /**
- * Get MAC address of radio
- *
- * @return MAC address, null on failure
- */
- public String getMacAddress() {
- return sendSyncMessage(CMD_GET_MAC_ADDR).stringValue;
- }
-
- /**
- * Start packet filtering
- */
- public void startPacketFiltering() {
- sendMessage(CMD_START_PACKET_FILTERING);
- }
-
- /**
- * Stop packet filtering
- */
- public void stopPacketFiltering() {
- sendMessage(CMD_STOP_PACKET_FILTERING);
- }
-
- /**
- * Set power mode
- * @param mode
- * DRIVER_POWER_MODE_AUTO
- * DRIVER_POWER_MODE_ACTIVE
- */
- public void setPowerMode(int mode) {
- sendMessage(obtainMessage(CMD_SET_POWER_MODE, mode, 0));
- }
-
- /**
- * Set the number of allowed radio frequency channels from the system
- * setting value, if any.
- */
- public void setNumAllowedChannels() {
- try {
- setNumAllowedChannels(
- Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS));
- } catch (Settings.SettingNotFoundException e) {
- if (mNumAllowedChannels != 0) {
- setNumAllowedChannels(mNumAllowedChannels);
- }
- // otherwise, use the driver default
- }
- }
-
- /**
- * Set the number of radio frequency channels that are allowed to be used
- * in the current regulatory domain.
- * @param numChannels the number of allowed channels. Must be greater than 0
- * and less than or equal to 16.
- */
- public void setNumAllowedChannels(int numChannels) {
- sendMessage(obtainMessage(CMD_SET_NUM_ALLOWED_CHANNELS, numChannels, 0));
- }
-
- /**
- * Get number of allowed channels
- *
- * @return channel count, -1 on failure
- *
- * TODO: this is not a public API and needs to be removed in favor
- * of asynchronous reporting. unused for now.
- */
- public int getNumAllowedChannels() {
- return -1;
- }
-
- /**
- * Set bluetooth coex mode:
- *
- * @param mode
- * BLUETOOTH_COEXISTENCE_MODE_ENABLED
- * BLUETOOTH_COEXISTENCE_MODE_DISABLED
- * BLUETOOTH_COEXISTENCE_MODE_SENSE
- */
- public void setBluetoothCoexistenceMode(int mode) {
- sendMessage(obtainMessage(CMD_SET_BLUETOOTH_COEXISTENCE, mode, 0));
- }
-
- /**
- * Enable or disable Bluetooth coexistence scan mode. When this mode is on,
- * some of the low-level scan parameters used by the driver are changed to
- * reduce interference with A2DP streaming.
- *
- * @param isBluetoothPlaying whether to enable or disable this mode
- */
- public void setBluetoothScanMode(boolean isBluetoothPlaying) {
- sendMessage(obtainMessage(CMD_SET_BLUETOOTH_SCAN_MODE, isBluetoothPlaying ? 1 : 0, 0));
- }
-
- /**
- * Save configuration on supplicant
- *
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- *
- * TODO: deprecate this
- */
- public boolean saveConfig() {
- return sendSyncMessage(CMD_SAVE_CONFIG).boolValue;
- }
-
- /**
- * TODO: doc
- */
- public void requestCmWakeLock() {
- sendMessage(CMD_REQUEST_CM_WAKELOCK);
- }
-
- /*********************************************************
- * Internal private functions
- ********************************************************/
-
- class SyncReturn {
- boolean boolValue;
- int intValue;
- String stringValue;
- Object objValue;
- List<WifiConfiguration> configList;
- }
-
- class SyncParams {
- Object mParameter;
- SyncReturn mSyncReturn;
- SyncParams() {
- mSyncReturn = new SyncReturn();
- }
- SyncParams(Object p) {
- mParameter = p;
- mSyncReturn = new SyncReturn();
- }
- }
-
- /**
- * message.arg2 is reserved to indicate synchronized
- * message.obj is used to store SyncParams
- */
- private SyncReturn syncedSend(Message msg) {
- SyncParams syncParams = (SyncParams) msg.obj;
- msg.arg2 = SYNCHRONOUS_CALL;
- synchronized(syncParams) {
- if (DBG) Log.d(TAG, "syncedSend " + msg);
- sendMessage(msg);
- try {
- syncParams.wait();
- } catch (InterruptedException e) {
- Log.e(TAG, "sendSyncMessage: unexpected interruption of wait()");
- return null;
- }
- }
- return syncParams.mSyncReturn;
- }
-
- private SyncReturn sendSyncMessage(Message msg) {
- SyncParams syncParams = new SyncParams();
- msg.obj = syncParams;
- return syncedSend(msg);
- }
-
- private SyncReturn sendSyncMessage(int what, Object param) {
- SyncParams syncParams = new SyncParams(param);
- Message msg = obtainMessage(what, syncParams);
- return syncedSend(msg);
- }
-
-
- private SyncReturn sendSyncMessage(int what) {
- return sendSyncMessage(obtainMessage(what));
- }
-
- private void notifyOnMsgObject(Message msg) {
- SyncParams syncParams = (SyncParams) msg.obj;
- if (syncParams != null) {
- synchronized(syncParams) {
- if (DBG) Log.d(TAG, "notifyOnMsgObject " + msg);
- syncParams.notify();
- }
- }
- else {
- Log.e(TAG, "Error! syncParams in notifyOnMsgObject is null");
- }
- }
-
- private void setWifiState(int wifiState) {
- final int previousWifiState = mWifiState.get();
-
- try {
- if (wifiState == WIFI_STATE_ENABLED) {
- mBatteryStats.noteWifiOn(mLastEnableUid.get());
- } else if (wifiState == WIFI_STATE_DISABLED) {
- mBatteryStats.noteWifiOff(mLastEnableUid.get());
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to note battery stats in wifi");
- }
-
- mWifiState.set(wifiState);
-
- if (DBG) Log.d(TAG, "setWifiState: " + getWifiStateByName());
-
- final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState);
- intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState);
- mContext.sendStickyBroadcast(intent);
- }
-
- private void setWifiApState(int wifiApState) {
- final int previousWifiApState = mWifiApState.get();
-
- try {
- if (wifiApState == WIFI_AP_STATE_ENABLED) {
- mBatteryStats.noteWifiOn(mLastApEnableUid.get());
- } else if (wifiApState == WIFI_AP_STATE_DISABLED) {
- mBatteryStats.noteWifiOff(mLastApEnableUid.get());
- }
- } catch (RemoteException e) {
- Log.d(TAG, "Failed to note battery stats in wifi");
- }
-
- // Update state
- mWifiApState.set(wifiApState);
-
- if (DBG) Log.d(TAG, "setWifiApState: " + getWifiApStateByName());
-
- // Broadcast
- final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState);
- intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState);
- mContext.sendStickyBroadcast(intent);
- }
-
- /**
- * Parse the scan result line passed to us by wpa_supplicant (helper).
- * @param line the line to parse
- * @return the {@link ScanResult} object
- */
- private ScanResult parseScanResult(String line) {
- ScanResult scanResult = null;
- if (line != null) {
- /*
- * Cache implementation (LinkedHashMap) is not synchronized, thus,
- * must synchronized here!
- */
- synchronized (mScanResultCache) {
- String[] result = scanResultPattern.split(line);
- if (3 <= result.length && result.length <= 5) {
- String bssid = result[0];
- // bssid | frequency | level | flags | ssid
- int frequency;
- int level;
- try {
- frequency = Integer.parseInt(result[1]);
- level = Integer.parseInt(result[2]);
- /* some implementations avoid negative values by adding 256
- * so we need to adjust for that here.
- */
- if (level > 0) level -= 256;
- } catch (NumberFormatException e) {
- frequency = 0;
- level = 0;
- }
-
- /*
- * The formatting of the results returned by
- * wpa_supplicant is intended to make the fields
- * line up nicely when printed,
- * not to make them easy to parse. So we have to
- * apply some heuristics to figure out which field
- * is the SSID and which field is the flags.
- */
- String ssid;
- String flags;
- if (result.length == 4) {
- if (result[3].charAt(0) == '[') {
- flags = result[3];
- ssid = "";
- } else {
- flags = "";
- ssid = result[3];
- }
- } else if (result.length == 5) {
- flags = result[3];
- ssid = result[4];
- } else {
- // Here, we must have 3 fields: no flags and ssid
- // set
- flags = "";
- ssid = "";
- }
-
- // bssid + ssid is the hash key
- String key = bssid + ssid;
- scanResult = mScanResultCache.get(key);
- if (scanResult != null) {
- scanResult.level = level;
- scanResult.SSID = ssid;
- scanResult.capabilities = flags;
- scanResult.frequency = frequency;
- } else {
- // Do not add scan results that have no SSID set
- if (0 < ssid.trim().length()) {
- scanResult =
- new ScanResult(
- ssid, bssid, flags, level, frequency);
- mScanResultCache.put(key, scanResult);
- }
- }
- } else {
- Log.w(TAG, "Misformatted scan result text with " +
- result.length + " fields: " + line);
- }
- }
- }
-
- return scanResult;
- }
-
- /**
- * scanResults input format
- * 00:bb:cc:dd:cc:ee 2427 166 [WPA-EAP-TKIP][WPA2-EAP-CCMP] Net1
- * 00:bb:cc:dd:cc:ff 2412 165 [WPA-EAP-TKIP][WPA2-EAP-CCMP] Net2
- */
- private void setScanResults(String scanResults) {
- if (scanResults == null) {
- return;
- }
-
- List<ScanResult> scanList = new ArrayList<ScanResult>();
-
- int lineCount = 0;
-
- int scanResultsLen = scanResults.length();
- // Parse the result string, keeping in mind that the last line does
- // not end with a newline.
- for (int lineBeg = 0, lineEnd = 0; lineEnd <= scanResultsLen; ++lineEnd) {
- if (lineEnd == scanResultsLen || scanResults.charAt(lineEnd) == '\n') {
- ++lineCount;
-
- if (lineCount == 1) {
- lineBeg = lineEnd + 1;
- continue;
- }
- if (lineEnd > lineBeg) {
- String line = scanResults.substring(lineBeg, lineEnd);
- ScanResult scanResult = parseScanResult(line);
- if (scanResult != null) {
- scanList.add(scanResult);
- } else {
- Log.w(TAG, "misformatted scan result for: " + line);
- }
- }
- lineBeg = lineEnd + 1;
- }
- }
-
- mScanResults = scanList;
- }
-
- private void checkAndSetNotification() {
- // If we shouldn't place a notification on available networks, then
- // don't bother doing any of the following
- if (!mNotificationEnabled) return;
-
- State state = mNetworkInfo.getState();
- if ((state == NetworkInfo.State.DISCONNECTED)
- || (state == NetworkInfo.State.UNKNOWN)) {
- // Look for an open network
- List<ScanResult> scanResults = mScanResults;
- if (scanResults != null) {
- int numOpenNetworks = 0;
- for (int i = scanResults.size() - 1; i >= 0; i--) {
- ScanResult scanResult = scanResults.get(i);
-
- if (TextUtils.isEmpty(scanResult.capabilities)) {
- numOpenNetworks++;
- }
- }
-
- if (numOpenNetworks > 0) {
- if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) {
- /*
- * We've scanned continuously at least
- * NUM_SCANS_BEFORE_NOTIFICATION times. The user
- * probably does not have a remembered network in range,
- * since otherwise supplicant would have tried to
- * associate and thus resetting this counter.
- */
- setNotificationVisible(true, numOpenNetworks, false, 0);
- }
- return;
- }
- }
- }
-
- // No open networks in range, remove the notification
- setNotificationVisible(false, 0, false, 0);
- }
-
- /**
- * Display or don't display a notification that there are open Wi-Fi networks.
- * @param visible {@code true} if notification should be visible, {@code false} otherwise
- * @param numNetworks the number networks seen
- * @param force {@code true} to force notification to be shown/not-shown,
- * even if it is already shown/not-shown.
- * @param delay time in milliseconds after which the notification should be made
- * visible or invisible.
- */
- private void setNotificationVisible(boolean visible, int numNetworks, boolean force,
- int delay) {
-
- // Since we use auto cancel on the notification, when the
- // mNetworksAvailableNotificationShown is true, the notification may
- // have actually been canceled. However, when it is false we know
- // for sure that it is not being shown (it will not be shown any other
- // place than here)
-
- // If it should be hidden and it is already hidden, then noop
- if (!visible && !mNotificationShown && !force) {
- return;
- }
-
- Message message;
- if (visible) {
-
- // Not enough time has passed to show the notification again
- if (System.currentTimeMillis() < mNotificationRepeatTime) {
- return;
- }
-
- if (mNotification == null) {
- // Cache the Notification mainly so we can remove the
- // EVENT_NOTIFICATION_CHANGED message with this Notification from
- // the queue later
- mNotification = new Notification();
- mNotification.when = 0;
- mNotification.icon = ICON_NETWORKS_AVAILABLE;
- mNotification.flags = Notification.FLAG_AUTO_CANCEL;
- mNotification.contentIntent = PendingIntent.getActivity(mContext, 0,
- new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK), 0);
- }
-
- CharSequence title = mContext.getResources().getQuantityText(
- com.android.internal.R.plurals.wifi_available, numNetworks);
- CharSequence details = mContext.getResources().getQuantityText(
- com.android.internal.R.plurals.wifi_available_detailed, numNetworks);
- mNotification.tickerText = title;
- mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent);
-
- mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS;
-
- message = mCsHandler.obtainMessage(EVENT_NOTIFICATION_CHANGED, 1,
- ICON_NETWORKS_AVAILABLE, mNotification);
-
- } else {
-
- // Remove any pending messages to show the notification
- mCsHandler.removeMessages(EVENT_NOTIFICATION_CHANGED, mNotification);
-
- message = mCsHandler.obtainMessage(EVENT_NOTIFICATION_CHANGED, 0,
- ICON_NETWORKS_AVAILABLE);
- }
-
- mCsHandler.sendMessageDelayed(message, delay);
-
- mNotificationShown = visible;
- }
-
- private void configureNetworkProperties() {
- try {
- mNetworkProperties.setInterface(NetworkInterface.getByName(mInterfaceName));
- } catch (SocketException e) {
- Log.e(TAG, "SocketException creating NetworkInterface from " + mInterfaceName +
- ". e=" + e);
- return;
- } catch (NullPointerException e) {
- Log.e(TAG, "NPE creating NetworkInterface. e=" + e);
- return;
- }
- // TODO - fix this for v6
- try {
- mNetworkProperties.addAddress(InetAddress.getByAddress(
- NetworkUtils.v4IntToArray(mDhcpInfo.ipAddress)));
- } catch (UnknownHostException e) {
- Log.e(TAG, "Exception setting IpAddress using " + mDhcpInfo + ", e=" + e);
- }
-
- try {
- mNetworkProperties.setGateway(InetAddress.getByAddress(NetworkUtils.v4IntToArray(
- mDhcpInfo.gateway)));
- } catch (UnknownHostException e) {
- Log.e(TAG, "Exception setting Gateway using " + mDhcpInfo + ", e=" + e);
- }
-
- try {
- mNetworkProperties.addDns(InetAddress.getByAddress(
- NetworkUtils.v4IntToArray(mDhcpInfo.dns1)));
- } catch (UnknownHostException e) {
- Log.e(TAG, "Exception setting Dns1 using " + mDhcpInfo + ", e=" + e);
- }
- try {
- mNetworkProperties.addDns(InetAddress.getByAddress(
- NetworkUtils.v4IntToArray(mDhcpInfo.dns2)));
-
- } catch (UnknownHostException e) {
- Log.e(TAG, "Exception setting Dns2 using " + mDhcpInfo + ", e=" + e);
- }
- // TODO - add proxy info
- }
-
-
- private void checkUseStaticIp() {
- mUseStaticIp = false;
- final ContentResolver cr = mContext.getContentResolver();
- try {
- if (Settings.System.getInt(cr, Settings.System.WIFI_USE_STATIC_IP) == 0) {
- return;
- }
- } catch (Settings.SettingNotFoundException e) {
- return;
- }
-
- try {
- String addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_IP);
- if (addr != null) {
- mDhcpInfo.ipAddress = stringToIpAddr(addr);
- } else {
- return;
- }
- addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_GATEWAY);
- if (addr != null) {
- mDhcpInfo.gateway = stringToIpAddr(addr);
- } else {
- return;
- }
- addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_NETMASK);
- if (addr != null) {
- mDhcpInfo.netmask = stringToIpAddr(addr);
- } else {
- return;
- }
- addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS1);
- if (addr != null) {
- mDhcpInfo.dns1 = stringToIpAddr(addr);
- } else {
- return;
- }
- addr = Settings.System.getString(cr, Settings.System.WIFI_STATIC_DNS2);
- if (addr != null) {
- mDhcpInfo.dns2 = stringToIpAddr(addr);
- } else {
- mDhcpInfo.dns2 = 0;
- }
- } catch (UnknownHostException e) {
- return;
- }
- mUseStaticIp = true;
- }
-
- private static int stringToIpAddr(String addrString) throws UnknownHostException {
- try {
- String[] parts = addrString.split("\\.");
- if (parts.length != 4) {
- throw new UnknownHostException(addrString);
- }
-
- int a = Integer.parseInt(parts[0]) ;
- int b = Integer.parseInt(parts[1]) << 8;
- int c = Integer.parseInt(parts[2]) << 16;
- int d = Integer.parseInt(parts[3]) << 24;
-
- return a | b | c | d;
- } catch (NumberFormatException ex) {
- throw new UnknownHostException(addrString);
- }
- }
-
- private int getMaxDhcpRetries() {
- return Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT,
- DEFAULT_MAX_DHCP_RETRIES);
- }
-
- private class SettingsObserver extends ContentObserver {
- public SettingsObserver(Handler handler) {
- super(handler);
- ContentResolver cr = mContext.getContentResolver();
- cr.registerContentObserver(Settings.System.getUriFor(
- Settings.System.WIFI_USE_STATIC_IP), false, this);
- cr.registerContentObserver(Settings.System.getUriFor(
- Settings.System.WIFI_STATIC_IP), false, this);
- cr.registerContentObserver(Settings.System.getUriFor(
- Settings.System.WIFI_STATIC_GATEWAY), false, this);
- cr.registerContentObserver(Settings.System.getUriFor(
- Settings.System.WIFI_STATIC_NETMASK), false, this);
- cr.registerContentObserver(Settings.System.getUriFor(
- Settings.System.WIFI_STATIC_DNS1), false, this);
- cr.registerContentObserver(Settings.System.getUriFor(
- Settings.System.WIFI_STATIC_DNS2), false, this);
- }
-
+ private class WifiStateReceiver extends BroadcastReceiver {
@Override
- public void onChange(boolean selfChange) {
- super.onChange(selfChange);
-
- boolean wasStaticIp = mUseStaticIp;
- int oIp, oGw, oMsk, oDns1, oDns2;
- oIp = oGw = oMsk = oDns1 = oDns2 = 0;
- if (wasStaticIp) {
- oIp = mDhcpInfo.ipAddress;
- oGw = mDhcpInfo.gateway;
- oMsk = mDhcpInfo.netmask;
- oDns1 = mDhcpInfo.dns1;
- oDns2 = mDhcpInfo.dns2;
- }
- checkUseStaticIp();
-
- if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED) {
- return;
- }
-
- boolean changed =
- (wasStaticIp != mUseStaticIp) ||
- (wasStaticIp && (
- oIp != mDhcpInfo.ipAddress ||
- oGw != mDhcpInfo.gateway ||
- oMsk != mDhcpInfo.netmask ||
- oDns1 != mDhcpInfo.dns1 ||
- oDns2 != mDhcpInfo.dns2));
-
- if (changed) {
- sendMessage(CMD_RECONFIGURE_IP);
- if (mUseStaticIp) {
- mCsHandler.sendEmptyMessage(EVENT_CONFIGURATION_CHANGED);
- }
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+ mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
+ WifiManager.EXTRA_NETWORK_INFO);
+ mNetworkProperties = (NetworkProperties) intent.getParcelableExtra(
+ WifiManager.EXTRA_NETWORK_PROPERTIES);
+ Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
+ msg.sendToTarget();
+ } else if (intent.getAction().equals(WifiManager.CONFIG_CHANGED_ACTION)) {
+ mNetworkProperties = (NetworkProperties) intent.getParcelableExtra(
+ WifiManager.EXTRA_NETWORK_PROPERTIES);
+ Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
+ msg.sendToTarget();
}
}
}
-
- /**
- * Clears variables related to tracking whether a notification has been
- * shown recently.
- * <p>
- * After calling this method, the timer that prevents notifications from
- * being shown too often will be cleared.
- */
- private void resetNotificationTimer() {
- mNotificationRepeatTime = 0;
- mNumScansSinceNetworkStateChange = 0;
- }
-
-
- /**
- * Whether to disable coexistence mode while obtaining IP address. This
- * logic will return true only if the current bluetooth
- * headset/handsfree state is disconnected. This means if it is in an
- * error state, we will NOT disable coexistence mode to err on the side
- * of safety.
- *
- * @return Whether to disable coexistence mode.
- */
- private boolean shouldDisableCoexistenceMode() {
- int state = mBluetoothHeadset.getState(mBluetoothHeadset.getCurrentHeadset());
- return state == BluetoothHeadset.STATE_DISCONNECTED;
- }
-
- private void checkIsBluetoothPlaying() {
- boolean isBluetoothPlaying = false;
- Set<BluetoothDevice> connected = mBluetoothA2dp.getConnectedSinks();
-
- for (BluetoothDevice device : connected) {
- if (mBluetoothA2dp.getSinkState(device) == BluetoothA2dp.STATE_PLAYING) {
- isBluetoothPlaying = true;
- break;
- }
- }
- setBluetoothScanMode(isBluetoothPlaying);
- }
-
- private void sendScanResultsAvailableBroadcast() {
- if (!ActivityManagerNative.isSystemReady()) return;
-
- mContext.sendBroadcast(new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
- }
-
- private void sendRssiChangeBroadcast(final int newRssi) {
- if (!ActivityManagerNative.isSystemReady()) return;
-
- Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION);
- intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi);
- mContext.sendBroadcast(intent);
- }
-
- private void sendNetworkStateChangeBroadcast(String bssid) {
- Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
- | Intent.FLAG_RECEIVER_REPLACE_PENDING);
- intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo);
- if (bssid != null)
- intent.putExtra(WifiManager.EXTRA_BSSID, bssid);
- mContext.sendStickyBroadcast(intent);
- }
-
- private void sendSupplicantStateChangedBroadcast(StateChangeResult sc, boolean failedAuth) {
- Intent intent = new Intent(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
- | Intent.FLAG_RECEIVER_REPLACE_PENDING);
- intent.putExtra(WifiManager.EXTRA_NEW_STATE, (Parcelable)sc.state);
- if (failedAuth) {
- intent.putExtra(
- WifiManager.EXTRA_SUPPLICANT_ERROR,
- WifiManager.ERROR_AUTHENTICATING);
- }
- mContext.sendStickyBroadcast(intent);
- }
-
- private void sendSupplicantConnectionChangedBroadcast(boolean connected) {
- if (!ActivityManagerNative.isSystemReady()) return;
-
- Intent intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
- intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, connected);
- mContext.sendBroadcast(intent);
- }
-
- /**
- * Record the detailed state of a network, and if it is a
- * change from the previous state, send a notification to
- * any listeners.
- * @param state the new @{code DetailedState}
- */
- private void setDetailedState(NetworkInfo.DetailedState state) {
- Log.d(TAG, "setDetailed state, old ="
- + mNetworkInfo.getDetailedState() + " and new state=" + state);
- if (state != mNetworkInfo.getDetailedState()) {
- mNetworkInfo.setDetailedState(state, null, null);
- Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
- msg.sendToTarget();
- }
- }
-
- private static String removeDoubleQuotes(String string) {
- if (string.length() <= 2) return "";
- return string.substring(1, string.length() - 1);
- }
-
- private static String convertToQuotedString(String string) {
- return "\"" + string + "\"";
- }
-
- private static String makeString(BitSet set, String[] strings) {
- StringBuffer buf = new StringBuffer();
- int nextSetBit = -1;
-
- /* Make sure all set bits are in [0, strings.length) to avoid
- * going out of bounds on strings. (Shouldn't happen, but...) */
- set = set.get(0, strings.length);
-
- while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
- buf.append(strings[nextSetBit].replace('_', '-')).append(' ');
- }
-
- // remove trailing space
- if (set.cardinality() > 0) {
- buf.setLength(buf.length() - 1);
- }
-
- return buf.toString();
- }
-
- private static int lookupString(String string, String[] strings) {
- int size = strings.length;
-
- string = string.replace('-', '_');
-
- for (int i = 0; i < size; i++)
- if (string.equals(strings[i]))
- return i;
-
- // if we ever get here, we should probably add the
- // value to WifiConfiguration to reflect that it's
- // supported by the WPA supplicant
- Log.w(TAG, "Failed to look-up a string: " + string);
-
- return -1;
- }
-
- private int addOrUpdateNetworkNative(WifiConfiguration config) {
- /*
- * If the supplied networkId is -1, we create a new empty
- * network configuration. Otherwise, the networkId should
- * refer to an existing configuration.
- */
- int netId = config.networkId;
- boolean newNetwork = netId == -1;
- // networkId of -1 means we want to create a new network
-
- if (newNetwork) {
- netId = WifiNative.addNetworkCommand();
- if (netId < 0) {
- Log.e(TAG, "Failed to add a network!");
- return -1;
- }
- }
-
- setVariables: {
-
- if (config.SSID != null &&
- !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.ssidVarName,
- config.SSID)) {
- Log.d(TAG, "failed to set SSID: "+config.SSID);
- break setVariables;
- }
-
- if (config.BSSID != null &&
- !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.bssidVarName,
- config.BSSID)) {
- Log.d(TAG, "failed to set BSSID: "+config.BSSID);
- break setVariables;
- }
-
- String allowedKeyManagementString =
- makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings);
- if (config.allowedKeyManagement.cardinality() != 0 &&
- !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.KeyMgmt.varName,
- allowedKeyManagementString)) {
- Log.d(TAG, "failed to set key_mgmt: "+
- allowedKeyManagementString);
- break setVariables;
- }
-
- String allowedProtocolsString =
- makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings);
- if (config.allowedProtocols.cardinality() != 0 &&
- !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.Protocol.varName,
- allowedProtocolsString)) {
- Log.d(TAG, "failed to set proto: "+
- allowedProtocolsString);
- break setVariables;
- }
-
- String allowedAuthAlgorithmsString =
- makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings);
- if (config.allowedAuthAlgorithms.cardinality() != 0 &&
- !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.AuthAlgorithm.varName,
- allowedAuthAlgorithmsString)) {
- Log.d(TAG, "failed to set auth_alg: "+
- allowedAuthAlgorithmsString);
- break setVariables;
- }
-
- String allowedPairwiseCiphersString =
- makeString(config.allowedPairwiseCiphers,
- WifiConfiguration.PairwiseCipher.strings);
- if (config.allowedPairwiseCiphers.cardinality() != 0 &&
- !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.PairwiseCipher.varName,
- allowedPairwiseCiphersString)) {
- Log.d(TAG, "failed to set pairwise: "+
- allowedPairwiseCiphersString);
- break setVariables;
- }
-
- String allowedGroupCiphersString =
- makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings);
- if (config.allowedGroupCiphers.cardinality() != 0 &&
- !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.GroupCipher.varName,
- allowedGroupCiphersString)) {
- Log.d(TAG, "failed to set group: "+
- allowedGroupCiphersString);
- break setVariables;
- }
-
- // Prevent client screw-up by passing in a WifiConfiguration we gave it
- // by preventing "*" as a key.
- if (config.preSharedKey != null && !config.preSharedKey.equals("*") &&
- !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.pskVarName,
- config.preSharedKey)) {
- Log.d(TAG, "failed to set psk: "+config.preSharedKey);
- break setVariables;
- }
-
- boolean hasSetKey = false;
- if (config.wepKeys != null) {
- for (int i = 0; i < config.wepKeys.length; i++) {
- // Prevent client screw-up by passing in a WifiConfiguration we gave it
- // by preventing "*" as a key.
- if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) {
- if (!WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.wepKeyVarNames[i],
- config.wepKeys[i])) {
- Log.d(TAG,
- "failed to set wep_key"+i+": " +
- config.wepKeys[i]);
- break setVariables;
- }
- hasSetKey = true;
- }
- }
- }
-
- if (hasSetKey) {
- if (!WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.wepTxKeyIdxVarName,
- Integer.toString(config.wepTxKeyIndex))) {
- Log.d(TAG,
- "failed to set wep_tx_keyidx: "+
- config.wepTxKeyIndex);
- break setVariables;
- }
- }
-
- if (!WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.priorityVarName,
- Integer.toString(config.priority))) {
- Log.d(TAG, config.SSID + ": failed to set priority: "
- +config.priority);
- break setVariables;
- }
-
- if (config.hiddenSSID && !WifiNative.setNetworkVariableCommand(
- netId,
- WifiConfiguration.hiddenSSIDVarName,
- Integer.toString(config.hiddenSSID ? 1 : 0))) {
- Log.d(TAG, config.SSID + ": failed to set hiddenSSID: "+
- config.hiddenSSID);
- break setVariables;
- }
-
- for (WifiConfiguration.EnterpriseField field
- : config.enterpriseFields) {
- String varName = field.varName();
- String value = field.value();
- if (value != null) {
- if (field != config.eap) {
- value = (value.length() == 0) ? "NULL" : convertToQuotedString(value);
- }
- if (!WifiNative.setNetworkVariableCommand(
- netId,
- varName,
- value)) {
- Log.d(TAG, config.SSID + ": failed to set " + varName +
- ": " + value);
- break setVariables;
- }
- }
- }
- return netId;
- }
-
- if (newNetwork) {
- WifiNative.removeNetworkCommand(netId);
- Log.d(TAG,
- "Failed to set a network variable, removed network: "
- + netId);
- }
-
- return -1;
- }
-
- private List<WifiConfiguration> getConfiguredNetworksNative() {
- String listStr = WifiNative.listNetworksCommand();
-
- List<WifiConfiguration> networks =
- new ArrayList<WifiConfiguration>();
- if (listStr == null)
- return networks;
-
- String[] lines = listStr.split("\n");
- // Skip the first line, which is a header
- for (int i = 1; i < lines.length; i++) {
- String[] result = lines[i].split("\t");
- // network-id | ssid | bssid | flags
- WifiConfiguration config = new WifiConfiguration();
- try {
- config.networkId = Integer.parseInt(result[0]);
- } catch(NumberFormatException e) {
- continue;
- }
- if (result.length > 3) {
- if (result[3].indexOf("[CURRENT]") != -1)
- config.status = WifiConfiguration.Status.CURRENT;
- else if (result[3].indexOf("[DISABLED]") != -1)
- config.status = WifiConfiguration.Status.DISABLED;
- else
- config.status = WifiConfiguration.Status.ENABLED;
- } else {
- config.status = WifiConfiguration.Status.ENABLED;
- }
- readNetworkVariables(config);
- networks.add(config);
- }
- return networks;
- }
-
- /**
- * Read the variables from the supplicant daemon that are needed to
- * fill in the WifiConfiguration object.
- *
- * @param config the {@link WifiConfiguration} object to be filled in.
- */
- private void readNetworkVariables(WifiConfiguration config) {
-
- int netId = config.networkId;
- if (netId < 0)
- return;
-
- /*
- * TODO: maybe should have a native method that takes an array of
- * variable names and returns an array of values. But we'd still
- * be doing a round trip to the supplicant daemon for each variable.
- */
- String value;
-
- value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.ssidVarName);
- if (!TextUtils.isEmpty(value)) {
- config.SSID = removeDoubleQuotes(value);
- } else {
- config.SSID = null;
- }
-
- value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.bssidVarName);
- if (!TextUtils.isEmpty(value)) {
- config.BSSID = value;
- } else {
- config.BSSID = null;
- }
-
- value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.priorityVarName);
- config.priority = -1;
- if (!TextUtils.isEmpty(value)) {
- try {
- config.priority = Integer.parseInt(value);
- } catch (NumberFormatException ignore) {
- }
- }
-
- value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.hiddenSSIDVarName);
- config.hiddenSSID = false;
- if (!TextUtils.isEmpty(value)) {
- try {
- config.hiddenSSID = Integer.parseInt(value) != 0;
- } catch (NumberFormatException ignore) {
- }
- }
-
- value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.wepTxKeyIdxVarName);
- config.wepTxKeyIndex = -1;
- if (!TextUtils.isEmpty(value)) {
- try {
- config.wepTxKeyIndex = Integer.parseInt(value);
- } catch (NumberFormatException ignore) {
- }
- }
-
- for (int i = 0; i < 4; i++) {
- value = WifiNative.getNetworkVariableCommand(netId,
- WifiConfiguration.wepKeyVarNames[i]);
- if (!TextUtils.isEmpty(value)) {
- config.wepKeys[i] = value;
- } else {
- config.wepKeys[i] = null;
- }
- }
-
- value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.pskVarName);
- if (!TextUtils.isEmpty(value)) {
- config.preSharedKey = value;
- } else {
- config.preSharedKey = null;
- }
-
- value = WifiNative.getNetworkVariableCommand(config.networkId,
- WifiConfiguration.Protocol.varName);
- if (!TextUtils.isEmpty(value)) {
- String vals[] = value.split(" ");
- for (String val : vals) {
- int index =
- lookupString(val, WifiConfiguration.Protocol.strings);
- if (0 <= index) {
- config.allowedProtocols.set(index);
- }
- }
- }
-
- value = WifiNative.getNetworkVariableCommand(config.networkId,
- WifiConfiguration.KeyMgmt.varName);
- if (!TextUtils.isEmpty(value)) {
- String vals[] = value.split(" ");
- for (String val : vals) {
- int index =
- lookupString(val, WifiConfiguration.KeyMgmt.strings);
- if (0 <= index) {
- config.allowedKeyManagement.set(index);
- }
- }
- }
-
- value = WifiNative.getNetworkVariableCommand(config.networkId,
- WifiConfiguration.AuthAlgorithm.varName);
- if (!TextUtils.isEmpty(value)) {
- String vals[] = value.split(" ");
- for (String val : vals) {
- int index =
- lookupString(val, WifiConfiguration.AuthAlgorithm.strings);
- if (0 <= index) {
- config.allowedAuthAlgorithms.set(index);
- }
- }
- }
-
- value = WifiNative.getNetworkVariableCommand(config.networkId,
- WifiConfiguration.PairwiseCipher.varName);
- if (!TextUtils.isEmpty(value)) {
- String vals[] = value.split(" ");
- for (String val : vals) {
- int index =
- lookupString(val, WifiConfiguration.PairwiseCipher.strings);
- if (0 <= index) {
- config.allowedPairwiseCiphers.set(index);
- }
- }
- }
-
- value = WifiNative.getNetworkVariableCommand(config.networkId,
- WifiConfiguration.GroupCipher.varName);
- if (!TextUtils.isEmpty(value)) {
- String vals[] = value.split(" ");
- for (String val : vals) {
- int index =
- lookupString(val, WifiConfiguration.GroupCipher.strings);
- if (0 <= index) {
- config.allowedGroupCiphers.set(index);
- }
- }
- }
-
- for (WifiConfiguration.EnterpriseField field :
- config.enterpriseFields) {
- value = WifiNative.getNetworkVariableCommand(netId,
- field.varName());
- if (!TextUtils.isEmpty(value)) {
- if (field != config.eap) value = removeDoubleQuotes(value);
- field.setValue(value);
- }
- }
-
- }
-
- /**
- * Poll for info not reported via events
- * RSSI & Linkspeed
- */
- private void requestPolledInfo() {
- int newRssi = WifiNative.getRssiCommand();
- if (newRssi != -1 && -200 < newRssi && newRssi < 256) { // screen out invalid values
- /* some implementations avoid negative values by adding 256
- * so we need to adjust for that here.
- */
- if (newRssi > 0) newRssi -= 256;
- mWifiInfo.setRssi(newRssi);
- /*
- * Rather then sending the raw RSSI out every time it
- * changes, we precalculate the signal level that would
- * be displayed in the status bar, and only send the
- * broadcast if that much more coarse-grained number
- * changes. This cuts down greatly on the number of
- * broadcasts, at the cost of not mWifiInforming others
- * interested in RSSI of all the changes in signal
- * level.
- */
- // TODO: The second arg to the call below needs to be a symbol somewhere, but
- // it's actually the size of an array of icons that's private
- // to StatusBar Policy.
- int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 4);
- if (newSignalLevel != mLastSignalLevel) {
- sendRssiChangeBroadcast(newRssi);
- }
- mLastSignalLevel = newSignalLevel;
- } else {
- mWifiInfo.setRssi(-200);
- }
- int newLinkSpeed = WifiNative.getLinkSpeedCommand();
- if (newLinkSpeed != -1) {
- mWifiInfo.setLinkSpeed(newLinkSpeed);
- }
- }
-
- /**
- * Resets the Wi-Fi Connections by clearing any state, resetting any sockets
- * using the interface, stopping DHCP & disabling interface
- */
- private void handleNetworkDisconnect() {
- Log.d(TAG, "Reset connections and stopping DHCP");
-
- /*
- * Reset connections & stop DHCP
- */
- NetworkUtils.resetConnections(mInterfaceName);
-
- if (!NetworkUtils.stopDhcp(mInterfaceName)) {
- Log.e(TAG, "Could not stop DHCP");
- }
-
- /* Disable interface */
- NetworkUtils.disableInterface(mInterfaceName);
-
- /* send event to CM & network change broadcast */
- setDetailedState(DetailedState.DISCONNECTED);
- sendNetworkStateChangeBroadcast(mLastBssid);
-
- /* Reset data structures */
- mWifiInfo.setIpAddress(0);
- mWifiInfo.setBSSID(null);
- mWifiInfo.setSSID(null);
- mWifiInfo.setNetworkId(-1);
-
- /* Clear network properties */
- mNetworkProperties.clear();
-
- mLastBssid= null;
- mLastNetworkId = -1;
-
- }
-
-
- /*********************************************************
- * Notifications from WifiMonitor
- ********************************************************/
-
- /**
- * A structure for supplying information about a supplicant state
- * change in the STATE_CHANGE event message that comes from the
- * WifiMonitor
- * thread.
- */
- private static class StateChangeResult {
- StateChangeResult(int networkId, String BSSID, Object state) {
- this.state = state;
- this.BSSID = BSSID;
- this.networkId = networkId;
- }
- int networkId;
- String BSSID;
- Object state;
- }
-
- /**
- * Send the tracker a notification that a user-entered password key
- * may be incorrect (i.e., caused authentication to fail).
- */
- void notifyPasswordKeyMayBeIncorrect() {
- sendMessage(PASSWORD_MAY_BE_INCORRECT_EVENT);
- }
-
- /**
- * Send the tracker a notification that a connection to the supplicant
- * daemon has been established.
- */
- void notifySupplicantConnection() {
- sendMessage(SUP_CONNECTION_EVENT);
- }
-
- /**
- * Send the tracker a notification that a connection to the supplicant
- * daemon has been established.
- */
- void notifySupplicantLost() {
- sendMessage(SUP_DISCONNECTION_EVENT);
- }
-
- /**
- * Send the tracker a notification that the state of Wifi connectivity
- * has changed.
- * @param networkId the configured network on which the state change occurred
- * @param newState the new network state
- * @param BSSID when the new state is {@link DetailedState#CONNECTED
- * NetworkInfo.DetailedState.CONNECTED},
- * this is the MAC address of the access point. Otherwise, it
- * is {@code null}.
- */
- void notifyNetworkStateChange(DetailedState newState, String BSSID, int networkId) {
- if (newState == NetworkInfo.DetailedState.CONNECTED) {
- sendMessage(obtainMessage(NETWORK_CONNECTION_EVENT,
- new StateChangeResult(networkId, BSSID, newState)));
- } else {
- sendMessage(obtainMessage(NETWORK_DISCONNECTION_EVENT,
- new StateChangeResult(networkId, BSSID, newState)));
- }
- }
-
- /**
- * Send the tracker a notification that the state of the supplicant
- * has changed.
- * @param networkId the configured network on which the state change occurred
- * @param newState the new {@code SupplicantState}
- */
- void notifySupplicantStateChange(int networkId, String BSSID, SupplicantState newState) {
- sendMessage(obtainMessage(SUPPLICANT_STATE_CHANGE_EVENT,
- new StateChangeResult(networkId, BSSID, newState)));
- }
-
- /**
- * Send the tracker a notification that a scan has completed, and results
- * are available.
- */
- void notifyScanResultsAvailable() {
- /**
- * Switch scan mode over to passive.
- * Turning off scan-only mode happens only in "Connect" mode
- */
- setScanType(false);
- sendMessage(SCAN_RESULTS_EVENT);
- }
-
- void notifyDriverStarted() {
- sendMessage(DRIVER_START_EVENT);
- }
-
- void notifyDriverStopped() {
- sendMessage(DRIVER_STOP_EVENT);
- }
-
- void notifyDriverHung() {
- setWifiEnabled(false);
- setWifiEnabled(true);
- }
-
-
- /********************************************************
- * HSM states
- *******************************************************/
-
- class DefaultState extends HierarchicalState {
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- SyncParams syncParams;
- switch (message.what) {
- /* Synchronous call returns */
- case CMD_PING_SUPPLICANT:
- case CMD_START_SCAN:
- case CMD_DISCONNECT:
- case CMD_RECONNECT:
- case CMD_REASSOCIATE:
- case CMD_REMOVE_NETWORK:
- case CMD_ENABLE_NETWORK:
- case CMD_DISABLE_NETWORK:
- case CMD_ADD_OR_UPDATE_NETWORK:
- case CMD_GET_RSSI:
- case CMD_GET_RSSI_APPROX:
- case CMD_GET_LINK_SPEED:
- case CMD_GET_MAC_ADDR:
- case CMD_SAVE_CONFIG:
- case CMD_CONNECTION_STATUS:
- case CMD_GET_NETWORK_CONFIG:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = false;
- syncParams.mSyncReturn.intValue = -1;
- syncParams.mSyncReturn.stringValue = null;
- syncParams.mSyncReturn.configList = null;
- notifyOnMsgObject(message);
- }
- break;
- case CM_CMD_TEARDOWN:
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- mTeardownRequested.set(true);
- sendMessage(CMD_DISCONNECT);
- sendMessage(CMD_STOP_DRIVER);
- /* Mark wifi available when CM tears down */
- mNetworkInfo.setIsAvailable(true);
- break;
- case CM_CMD_RECONNECT:
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- mTeardownRequested.set(false);
- sendMessage(CMD_START_DRIVER);
- sendMessage(CMD_RECONNECT);
- break;
- case CMD_ENABLE_RSSI_POLL:
- mEnableRssiPolling = (message.arg1 == 1);
- mSupplicantStateTracker.sendMessage(CMD_ENABLE_RSSI_POLL);
- break;
- default:
- if (DBG) Log.w(TAG, "Unhandled " + message);
- break;
- }
- return HANDLED;
- }
- }
-
- class InitialState extends HierarchicalState {
- @Override
- //TODO: could move logging into a common class
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- // [31-8] Reserved for future use
- // [7 - 0] HSM state change
- // 50021 wifi_state_changed (custom|1|5)
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
-
- if (WifiNative.isDriverLoaded()) {
- transitionTo(mDriverLoadedState);
- }
- else {
- transitionTo(mDriverUnloadedState);
- }
- }
- }
-
- class DriverLoadingState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
-
- final Message message = new Message();
- message.copyFrom(getCurrentMessage());
- new Thread(new Runnable() {
- public void run() {
- sWakeLock.acquire();
- //enabling state
- switch(message.arg1) {
- case WIFI_STATE_ENABLING:
- setWifiState(WIFI_STATE_ENABLING);
- break;
- case WIFI_AP_STATE_ENABLING:
- setWifiApState(WIFI_AP_STATE_ENABLING);
- break;
- }
-
- if(WifiNative.loadDriver()) {
- Log.d(TAG, "Driver load successful");
- sendMessage(CMD_LOAD_DRIVER_SUCCESS);
- } else {
- Log.e(TAG, "Failed to load driver!");
- switch(message.arg1) {
- case WIFI_STATE_ENABLING:
- setWifiState(WIFI_STATE_UNKNOWN);
- break;
- case WIFI_AP_STATE_ENABLING:
- setWifiApState(WIFI_AP_STATE_FAILED);
- break;
- }
- sendMessage(CMD_LOAD_DRIVER_FAILURE);
- }
- sWakeLock.release();
- }
- }).start();
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch (message.what) {
- case CMD_LOAD_DRIVER_SUCCESS:
- transitionTo(mDriverLoadedState);
- break;
- case CMD_LOAD_DRIVER_FAILURE:
- transitionTo(mDriverFailedState);
- break;
- case CMD_LOAD_DRIVER:
- case CMD_UNLOAD_DRIVER:
- case CMD_START_SUPPLICANT:
- case CMD_STOP_SUPPLICANT:
- case CMD_START_AP:
- case CMD_STOP_AP:
- case CMD_START_DRIVER:
- case CMD_STOP_DRIVER:
- case CMD_SET_SCAN_MODE:
- case CMD_SET_SCAN_TYPE:
- case CMD_SET_POWER_MODE:
- case CMD_SET_BLUETOOTH_COEXISTENCE:
- case CMD_SET_BLUETOOTH_SCAN_MODE:
- case CMD_SET_NUM_ALLOWED_CHANNELS:
- case CMD_START_PACKET_FILTERING:
- case CMD_STOP_PACKET_FILTERING:
- deferMessage(message);
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class DriverLoadedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch(message.what) {
- case CMD_UNLOAD_DRIVER:
- transitionTo(mDriverUnloadingState);
- break;
- case CMD_START_SUPPLICANT:
- if(WifiNative.startSupplicant()) {
- Log.d(TAG, "Supplicant start successful");
- mWifiMonitor.startMonitoring();
- setWifiState(WIFI_STATE_ENABLED);
- transitionTo(mWaitForSupState);
- } else {
- Log.e(TAG, "Failed to start supplicant!");
- sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0));
- }
- break;
- case CMD_START_AP:
- try {
- nwService.startAccessPoint((WifiConfiguration) message.obj,
- mInterfaceName,
- SOFTAP_IFACE);
- } catch(Exception e) {
- Log.e(TAG, "Exception in startAccessPoint()");
- sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0));
- break;
- }
- Log.d(TAG, "Soft AP start successful");
- setWifiApState(WIFI_AP_STATE_ENABLED);
- transitionTo(mSoftApStartedState);
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class DriverUnloadingState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
-
- final Message message = new Message();
- message.copyFrom(getCurrentMessage());
- new Thread(new Runnable() {
- public void run() {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- sWakeLock.acquire();
- if(WifiNative.unloadDriver()) {
- Log.d(TAG, "Driver unload successful");
- sendMessage(CMD_UNLOAD_DRIVER_SUCCESS);
-
- switch(message.arg1) {
- case WIFI_STATE_DISABLED:
- case WIFI_STATE_UNKNOWN:
- setWifiState(message.arg1);
- break;
- case WIFI_AP_STATE_DISABLED:
- case WIFI_AP_STATE_FAILED:
- setWifiApState(message.arg1);
- break;
- }
- } else {
- Log.e(TAG, "Failed to unload driver!");
- sendMessage(CMD_UNLOAD_DRIVER_FAILURE);
-
- switch(message.arg1) {
- case WIFI_STATE_DISABLED:
- case WIFI_STATE_UNKNOWN:
- setWifiState(WIFI_STATE_UNKNOWN);
- break;
- case WIFI_AP_STATE_DISABLED:
- case WIFI_AP_STATE_FAILED:
- setWifiApState(WIFI_AP_STATE_FAILED);
- break;
- }
- }
- sWakeLock.release();
- }
- }).start();
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch (message.what) {
- case CMD_UNLOAD_DRIVER_SUCCESS:
- transitionTo(mDriverUnloadedState);
- break;
- case CMD_UNLOAD_DRIVER_FAILURE:
- transitionTo(mDriverFailedState);
- break;
- case CMD_LOAD_DRIVER:
- case CMD_UNLOAD_DRIVER:
- case CMD_START_SUPPLICANT:
- case CMD_STOP_SUPPLICANT:
- case CMD_START_AP:
- case CMD_STOP_AP:
- case CMD_START_DRIVER:
- case CMD_STOP_DRIVER:
- case CMD_SET_SCAN_MODE:
- case CMD_SET_SCAN_TYPE:
- case CMD_SET_POWER_MODE:
- case CMD_SET_BLUETOOTH_COEXISTENCE:
- case CMD_SET_BLUETOOTH_SCAN_MODE:
- case CMD_SET_NUM_ALLOWED_CHANNELS:
- case CMD_START_PACKET_FILTERING:
- case CMD_STOP_PACKET_FILTERING:
- deferMessage(message);
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class DriverUnloadedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch (message.what) {
- case CMD_LOAD_DRIVER:
- transitionTo(mDriverLoadingState);
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class DriverFailedState extends HierarchicalState {
- @Override
- public void enter() {
- Log.e(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- return NOT_HANDLED;
- }
- }
-
-
- class WaitForSupState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch(message.what) {
- case SUP_CONNECTION_EVENT:
- Log.d(TAG, "Supplicant connection established");
- mSupplicantStateTracker.resetSupplicantState();
- /* Initialize data structures */
- resetNotificationTimer();
- setTeardownRequested(false);
- mLastBssid = null;
- mLastNetworkId = -1;
- mLastSignalLevel = -1;
-
- mWifiInfo.setMacAddress(WifiNative.getMacAddressCommand());
-
- //TODO: initialize and fix multicast filtering
- //mWM.initializeMulticastFiltering();
-
- if (mBluetoothA2dp == null) {
- mBluetoothA2dp = new BluetoothA2dp(mContext);
- }
- checkIsBluetoothPlaying();
-
- checkUseStaticIp();
- sendSupplicantConnectionChangedBroadcast(true);
- transitionTo(mDriverSupReadyState);
- break;
- case CMD_STOP_SUPPLICANT:
- Log.d(TAG, "Stop supplicant received");
- WifiNative.stopSupplicant();
- transitionTo(mDriverLoadedState);
- break;
- /* Fail soft ap when waiting for supplicant start */
- case CMD_START_AP:
- Log.d(TAG, "Failed to start soft AP with a running supplicant");
- setWifiApState(WIFI_AP_STATE_FAILED);
- break;
- case CMD_START_DRIVER:
- case CMD_STOP_DRIVER:
- case CMD_SET_SCAN_MODE:
- case CMD_SET_SCAN_TYPE:
- case CMD_SET_POWER_MODE:
- case CMD_SET_BLUETOOTH_COEXISTENCE:
- case CMD_SET_BLUETOOTH_SCAN_MODE:
- case CMD_SET_NUM_ALLOWED_CHANNELS:
- case CMD_START_PACKET_FILTERING:
- case CMD_STOP_PACKET_FILTERING:
- deferMessage(message);
- break;
- case CMD_STOP_AP:
- case CMD_START_SUPPLICANT:
- case CMD_UNLOAD_DRIVER:
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class DriverSupReadyState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- /* Initialize for connect mode operation at start */
- mIsScanMode = false;
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- SyncParams syncParams;
- switch(message.what) {
- case CMD_STOP_SUPPLICANT: /* Supplicant stopped by user */
- Log.d(TAG, "Stop supplicant received");
- WifiNative.stopSupplicant();
- //$FALL-THROUGH$
- case SUP_DISCONNECTION_EVENT: /* Supplicant died */
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- //Remove any notifications on disconnection
- setNotificationVisible(false, 0, false, 0);
- WifiNative.closeSupplicantConnection();
- handleNetworkDisconnect();
- sendSupplicantConnectionChangedBroadcast(false);
- mSupplicantStateTracker.resetSupplicantState();
- transitionTo(mDriverLoadedState);
-
- /* When supplicant dies, unload driver and enter failed state */
- //TODO: consider bringing up supplicant again
- if (message.what == SUP_DISCONNECTION_EVENT) {
- Log.d(TAG, "Supplicant died, unloading driver");
- sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0));
- }
- break;
- case CMD_START_DRIVER:
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- WifiNative.startDriverCommand();
- transitionTo(mDriverStartingState);
- break;
- case SCAN_RESULTS_EVENT:
- setScanResults(WifiNative.scanResultsCommand());
- sendScanResultsAvailableBroadcast();
- checkAndSetNotification();
- break;
- case CMD_PING_SUPPLICANT:
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.pingCommand();
- notifyOnMsgObject(message);
- break;
- case CMD_ADD_OR_UPDATE_NETWORK:
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- syncParams = (SyncParams) message.obj;
- WifiConfiguration config = (WifiConfiguration) syncParams.mParameter;
- syncParams.mSyncReturn.intValue = addOrUpdateNetworkNative(config);
- notifyOnMsgObject(message);
- break;
- case CMD_REMOVE_NETWORK:
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.removeNetworkCommand(
- message.arg1);
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.removeNetworkCommand(message.arg1);
- }
- break;
- case CMD_ENABLE_NETWORK:
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- EnableNetParams enableNetParams = (EnableNetParams) syncParams.mParameter;
- syncParams.mSyncReturn.boolValue = WifiNative.enableNetworkCommand(
- enableNetParams.netId, enableNetParams.disableOthers);
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.enableNetworkCommand(message.arg1, message.arg2 == 1);
- }
- break;
- case CMD_DISABLE_NETWORK:
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.disableNetworkCommand(
- message.arg1);
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.disableNetworkCommand(message.arg1);
- }
- break;
- case CMD_BLACKLIST_NETWORK:
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- WifiNative.addToBlacklistCommand((String)message.obj);
- break;
- case CMD_CLEAR_BLACKLIST:
- WifiNative.clearBlacklistCommand();
- break;
- case CMD_GET_NETWORK_CONFIG:
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.configList = getConfiguredNetworksNative();
- notifyOnMsgObject(message);
- break;
- case CMD_SAVE_CONFIG:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.saveConfigCommand();
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.saveConfigCommand();
- }
- // Inform the backup manager about a data change
- IBackupManager ibm = IBackupManager.Stub.asInterface(
- ServiceManager.getService(Context.BACKUP_SERVICE));
- if (ibm != null) {
- try {
- ibm.dataChanged("com.android.providers.settings");
- } catch (Exception e) {
- // Try again later
- }
- }
- break;
- case CMD_CONNECTION_STATUS:
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.stringValue = WifiNative.statusCommand();
- notifyOnMsgObject(message);
- break;
- case CMD_GET_MAC_ADDR:
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.stringValue = WifiNative.getMacAddressCommand();
- notifyOnMsgObject(message);
- break;
- /* Cannot start soft AP while in client mode */
- case CMD_START_AP:
- Log.d(TAG, "Failed to start soft AP with a running supplicant");
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- setWifiApState(WIFI_AP_STATE_FAILED);
- break;
- case CMD_SET_SCAN_MODE:
- mIsScanMode = (message.arg1 == SCAN_ONLY_MODE);
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
- class DriverStartingState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch(message.what) {
- case DRIVER_START_EVENT:
- transitionTo(mDriverStartedState);
- break;
- /* Queue driver commands & connection events */
- case CMD_START_DRIVER:
- case CMD_STOP_DRIVER:
- case SUPPLICANT_STATE_CHANGE_EVENT:
- case NETWORK_CONNECTION_EVENT:
- case NETWORK_DISCONNECTION_EVENT:
- case PASSWORD_MAY_BE_INCORRECT_EVENT:
- case CMD_SET_SCAN_TYPE:
- case CMD_SET_POWER_MODE:
- case CMD_SET_BLUETOOTH_COEXISTENCE:
- case CMD_SET_BLUETOOTH_SCAN_MODE:
- case CMD_SET_NUM_ALLOWED_CHANNELS:
- case CMD_START_PACKET_FILTERING:
- case CMD_STOP_PACKET_FILTERING:
- deferMessage(message);
- break;
- /* Queue the asynchronous version of these commands */
- case CMD_START_SCAN:
- case CMD_DISCONNECT:
- case CMD_REASSOCIATE:
- case CMD_RECONNECT:
- if (message.arg2 != SYNCHRONOUS_CALL) {
- deferMessage(message);
- }
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class DriverStartedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
-
- try {
- mBatteryStats.noteWifiRunning();
- } catch (RemoteException ignore) {}
-
- /* Initialize channel count */
- setNumAllowedChannels();
-
- if (mIsScanMode) {
- WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);
- WifiNative.disconnectCommand();
- transitionTo(mScanModeState);
- } else {
- WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
- /* If supplicant has already connected, before we could finish establishing
- * the control channel connection, we miss all the supplicant events.
- * Disconnect and reconnect when driver has started to ensure we receive
- * all supplicant events.
- *
- * TODO: This is a bit unclean, ideally the supplicant should never
- * connect until told to do so by the framework
- */
- WifiNative.disconnectCommand();
- WifiNative.reconnectCommand();
- transitionTo(mConnectModeState);
- }
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- SyncParams syncParams;
- switch(message.what) {
- case CMD_SET_SCAN_TYPE:
- if (message.arg1 == SCAN_ACTIVE) {
- WifiNative.setScanModeCommand(true);
- } else {
- WifiNative.setScanModeCommand(false);
- }
- break;
- case CMD_SET_POWER_MODE:
- WifiNative.setPowerModeCommand(message.arg1);
- break;
- case CMD_SET_BLUETOOTH_COEXISTENCE:
- WifiNative.setBluetoothCoexistenceModeCommand(message.arg1);
- break;
- case CMD_SET_BLUETOOTH_SCAN_MODE:
- WifiNative.setBluetoothCoexistenceScanModeCommand(message.arg1 == 1);
- break;
- case CMD_SET_NUM_ALLOWED_CHANNELS:
- mNumAllowedChannels = message.arg1;
- WifiNative.setNumAllowedChannelsCommand(message.arg1);
- break;
- case CMD_START_DRIVER:
- /* Ignore another driver start */
- break;
- case CMD_STOP_DRIVER:
- WifiNative.stopDriverCommand();
- transitionTo(mDriverStoppingState);
- break;
- case CMD_REQUEST_CM_WAKELOCK:
- if (mCm == null) {
- mCm = (ConnectivityManager)mContext.getSystemService(
- Context.CONNECTIVITY_SERVICE);
- }
- mCm.requestNetworkTransitionWakelock(TAG);
- break;
- case CMD_START_PACKET_FILTERING:
- WifiNative.startPacketFiltering();
- break;
- case CMD_STOP_PACKET_FILTERING:
- WifiNative.stopPacketFiltering();
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- @Override
- public void exit() {
- if (DBG) Log.d(TAG, getName() + "\n");
- try {
- mBatteryStats.noteWifiStopped();
- } catch (RemoteException ignore) { }
- }
- }
-
- class DriverStoppingState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch(message.what) {
- case DRIVER_STOP_EVENT:
- transitionTo(mDriverStoppedState);
- break;
- /* Queue driver commands */
- case CMD_START_DRIVER:
- case CMD_STOP_DRIVER:
- case CMD_SET_SCAN_TYPE:
- case CMD_SET_POWER_MODE:
- case CMD_SET_BLUETOOTH_COEXISTENCE:
- case CMD_SET_BLUETOOTH_SCAN_MODE:
- case CMD_SET_NUM_ALLOWED_CHANNELS:
- case CMD_START_PACKET_FILTERING:
- case CMD_STOP_PACKET_FILTERING:
- deferMessage(message);
- break;
- /* Queue the asynchronous version of these commands */
- case CMD_START_SCAN:
- case CMD_DISCONNECT:
- case CMD_REASSOCIATE:
- case CMD_RECONNECT:
- if (message.arg2 != SYNCHRONOUS_CALL) {
- deferMessage(message);
- }
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class DriverStoppedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
-
- // Take down any open network notifications on driver stop
- setNotificationVisible(false, 0, false, 0);
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- return NOT_HANDLED;
- }
- }
-
- class ScanModeState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- SyncParams syncParams;
- switch(message.what) {
- case CMD_SET_SCAN_MODE:
- if (message.arg1 == SCAN_ONLY_MODE) {
- /* Ignore */
- return HANDLED;
- } else {
- WifiNative.setScanResultHandlingCommand(message.arg1);
- WifiNative.reconnectCommand();
- mIsScanMode = false;
- transitionTo(mDisconnectedState);
- }
- break;
- case CMD_START_SCAN:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.scanCommand(
- message.arg1 == SCAN_ACTIVE);
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
- }
- break;
- /* Ignore */
- case CMD_DISCONNECT:
- case CMD_RECONNECT:
- case CMD_REASSOCIATE:
- case SUPPLICANT_STATE_CHANGE_EVENT:
- case NETWORK_CONNECTION_EVENT:
- case NETWORK_DISCONNECTION_EVENT:
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class ConnectModeState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- SyncParams syncParams;
- StateChangeResult stateChangeResult;
- switch(message.what) {
- case PASSWORD_MAY_BE_INCORRECT_EVENT:
- mPasswordKeyMayBeIncorrect = true;
- break;
- case SUPPLICANT_STATE_CHANGE_EVENT:
- stateChangeResult = (StateChangeResult) message.obj;
- mSupplicantStateTracker.handleEvent(stateChangeResult);
- break;
- case CMD_START_SCAN:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = true;
- notifyOnMsgObject(message);
- }
- /* We need to set scan type in completed state */
- Message newMsg = obtainMessage();
- newMsg.copyFrom(message);
- mSupplicantStateTracker.sendMessage(newMsg);
- break;
- /* Do a redundant disconnect without transition */
- case CMD_DISCONNECT:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand();
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.disconnectCommand();
- }
- break;
- case CMD_RECONNECT:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.reconnectCommand();
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.reconnectCommand();
- }
- break;
- case CMD_REASSOCIATE:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.reassociateCommand();
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.reassociateCommand();
- }
- break;
- case SCAN_RESULTS_EVENT:
- /* Set the scan setting back to "connect" mode */
- WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
- /* Handle scan results */
- return NOT_HANDLED;
- case NETWORK_CONNECTION_EVENT:
- Log.d(TAG,"Network connection established");
- stateChangeResult = (StateChangeResult) message.obj;
-
- /* Remove any notifications */
- setNotificationVisible(false, 0, false, 0);
- resetNotificationTimer();
-
- mWifiInfo.setBSSID(mLastBssid = stateChangeResult.BSSID);
- mWifiInfo.setNetworkId(stateChangeResult.networkId);
- mLastNetworkId = stateChangeResult.networkId;
-
- /* send event to CM & network change broadcast */
- setDetailedState(DetailedState.OBTAINING_IPADDR);
- sendNetworkStateChangeBroadcast(mLastBssid);
-
- transitionTo(mConnectingState);
- break;
- case NETWORK_DISCONNECTION_EVENT:
- Log.d(TAG,"Network connection lost");
- handleNetworkDisconnect();
- transitionTo(mDisconnectedState);
- break;
- case CMD_GET_RSSI:
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.intValue = WifiNative.getRssiCommand();
- notifyOnMsgObject(message);
- break;
- case CMD_GET_RSSI_APPROX:
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.intValue = WifiNative.getRssiApproxCommand();
- notifyOnMsgObject(message);
- break;
- case CMD_GET_LINK_SPEED:
- syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.intValue = WifiNative.getLinkSpeedCommand();
- notifyOnMsgObject(message);
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class ConnectingState extends HierarchicalState {
- boolean modifiedBluetoothCoexistenceMode;
- int powerMode;
- Thread mDhcpThread;
-
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
-
- if (!mUseStaticIp) {
-
- mDhcpThread = null;
- modifiedBluetoothCoexistenceMode = false;
- powerMode = DRIVER_POWER_MODE_AUTO;
-
- if (shouldDisableCoexistenceMode()) {
- /*
- * There are problems setting the Wi-Fi driver's power
- * mode to active when bluetooth coexistence mode is
- * enabled or sense.
- * <p>
- * We set Wi-Fi to active mode when
- * obtaining an IP address because we've found
- * compatibility issues with some routers with low power
- * mode.
- * <p>
- * In order for this active power mode to properly be set,
- * we disable coexistence mode until we're done with
- * obtaining an IP address. One exception is if we
- * are currently connected to a headset, since disabling
- * coexistence would interrupt that connection.
- */
- modifiedBluetoothCoexistenceMode = true;
-
- // Disable the coexistence mode
- WifiNative.setBluetoothCoexistenceModeCommand(
- WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
- }
-
- powerMode = WifiNative.getPowerModeCommand();
- if (powerMode < 0) {
- // Handle the case where supplicant driver does not support
- // getPowerModeCommand.
- powerMode = DRIVER_POWER_MODE_AUTO;
- }
- if (powerMode != DRIVER_POWER_MODE_ACTIVE) {
- WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_ACTIVE);
- }
-
- Log.d(TAG, "DHCP request started");
- mDhcpThread = new Thread(new Runnable() {
- public void run() {
- if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) {
- Log.d(TAG, "DHCP request succeeded");
- sendMessage(CMD_IP_CONFIG_SUCCESS);
- } else {
- Log.d(TAG, "DHCP request failed: " +
- NetworkUtils.getDhcpError());
- sendMessage(CMD_IP_CONFIG_FAILURE);
- }
- }
- });
- mDhcpThread.start();
- } else {
- if (NetworkUtils.configureInterface(mInterfaceName, mDhcpInfo)) {
- Log.v(TAG, "Static IP configuration succeeded");
- sendMessage(CMD_IP_CONFIG_SUCCESS);
- } else {
- Log.v(TAG, "Static IP configuration failed");
- sendMessage(CMD_IP_CONFIG_FAILURE);
- }
- }
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
-
- switch(message.what) {
- case CMD_IP_CONFIG_SUCCESS:
- mReconnectCount = 0;
- mLastSignalLevel = -1; // force update of signal strength
- mWifiInfo.setIpAddress(mDhcpInfo.ipAddress);
- Log.d(TAG, "IP configuration: " + mDhcpInfo);
- configureNetworkProperties();
- setDetailedState(DetailedState.CONNECTED);
- sendNetworkStateChangeBroadcast(mLastBssid);
- transitionTo(mConnectedState);
- break;
- case CMD_IP_CONFIG_FAILURE:
- mWifiInfo.setIpAddress(0);
-
- Log.e(TAG, "IP configuration failed");
- /**
- * If we've exceeded the maximum number of retries for DHCP
- * to a given network, disable the network
- */
- if (++mReconnectCount > getMaxDhcpRetries()) {
- Log.e(TAG, "Failed " +
- mReconnectCount + " times, Disabling " + mLastNetworkId);
- WifiNative.disableNetworkCommand(mLastNetworkId);
- }
-
- /* DHCP times out after about 30 seconds, we do a
- * disconnect and an immediate reconnect to try again
- */
- WifiNative.disconnectCommand();
- WifiNative.reconnectCommand();
- transitionTo(mDisconnectingState);
- break;
- case CMD_DISCONNECT:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- SyncParams syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand();
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.disconnectCommand();
- }
- transitionTo(mDisconnectingState);
- break;
- /* Ignore */
- case NETWORK_CONNECTION_EVENT:
- break;
- case CMD_STOP_DRIVER:
- sendMessage(CMD_DISCONNECT);
- deferMessage(message);
- break;
- case CMD_SET_SCAN_MODE:
- if (message.arg1 == SCAN_ONLY_MODE) {
- sendMessage(CMD_DISCONNECT);
- deferMessage(message);
- }
- break;
- case CMD_RECONFIGURE_IP:
- deferMessage(message);
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
-
- @Override
- public void exit() {
- /* reset power state & bluetooth coexistence if on DHCP */
- if (!mUseStaticIp) {
- if (powerMode != DRIVER_POWER_MODE_ACTIVE) {
- WifiNative.setPowerModeCommand(powerMode);
- }
-
- if (modifiedBluetoothCoexistenceMode) {
- // Set the coexistence mode back to its default value
- WifiNative.setBluetoothCoexistenceModeCommand(
- WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
- }
- }
-
- }
- }
-
- class ConnectedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch (message.what) {
- case CMD_DISCONNECT:
- if (message.arg2 == SYNCHRONOUS_CALL) {
- SyncParams syncParams = (SyncParams) message.obj;
- syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand();
- notifyOnMsgObject(message);
- } else {
- /* asynchronous handling */
- WifiNative.disconnectCommand();
- }
- transitionTo(mDisconnectingState);
- break;
- case CMD_RECONFIGURE_IP:
- Log.d(TAG,"Reconfiguring IP on connection");
- NetworkUtils.resetConnections(mInterfaceName);
- transitionTo(mConnectingState);
- break;
- case CMD_STOP_DRIVER:
- sendMessage(CMD_DISCONNECT);
- deferMessage(message);
- break;
- case CMD_SET_SCAN_MODE:
- if (message.arg1 == SCAN_ONLY_MODE) {
- sendMessage(CMD_DISCONNECT);
- deferMessage(message);
- }
- break;
- /* Ignore */
- case NETWORK_CONNECTION_EVENT:
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class DisconnectingState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch (message.what) {
- case CMD_STOP_DRIVER: /* Stop driver only after disconnect handled */
- deferMessage(message);
- break;
- case CMD_SET_SCAN_MODE:
- if (message.arg1 == SCAN_ONLY_MODE) {
- deferMessage(message);
- }
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class DisconnectedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch (message.what) {
- case CMD_SET_SCAN_MODE:
- if (message.arg1 == SCAN_ONLY_MODE) {
- WifiNative.setScanResultHandlingCommand(message.arg1);
- //Supplicant disconnect to prevent further connects
- WifiNative.disconnectCommand();
- mIsScanMode = true;
- transitionTo(mScanModeState);
- }
- break;
- /* Ignore network disconnect */
- case NETWORK_DISCONNECTION_EVENT:
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
- class SoftApStartedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch(message.what) {
- case CMD_STOP_AP:
- Log.d(TAG,"Stopping Soft AP");
- setWifiApState(WIFI_AP_STATE_DISABLING);
- try {
- nwService.stopAccessPoint();
- } catch(Exception e) {
- Log.e(TAG, "Exception in stopAccessPoint()");
- }
- transitionTo(mDriverLoadedState);
- break;
- case CMD_START_AP:
- Log.d(TAG,"SoftAP set on a running access point");
- try {
- nwService.setAccessPoint((WifiConfiguration) message.obj,
- mInterfaceName,
- SOFTAP_IFACE);
- } catch(Exception e) {
- Log.e(TAG, "Exception in nwService during soft AP set");
- try {
- nwService.stopAccessPoint();
- } catch (Exception ee) {
- Slog.e(TAG, "Could not stop AP, :" + ee);
- }
- sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0));
- }
- break;
- /* Fail client mode operation when soft AP is enabled */
- case CMD_START_SUPPLICANT:
- Log.e(TAG,"Cannot start supplicant with a running soft AP");
- setWifiState(WIFI_STATE_UNKNOWN);
- break;
- default:
- return NOT_HANDLED;
- }
- EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
- return HANDLED;
- }
- }
-
-
- class SupplicantStateTracker extends HierarchicalStateMachine {
-
- private int mRssiPollToken = 0;
-
- /**
- * The max number of the WPA supplicant loop iterations before we
- * decide that the loop should be terminated:
- */
- private static final int MAX_SUPPLICANT_LOOP_ITERATIONS = 4;
- private int mLoopDetectIndex = 0;
- private int mLoopDetectCount = 0;
-
- /**
- * Supplicant state change commands follow
- * the ordinal values defined in SupplicantState.java
- */
- private static final int DISCONNECTED = 0;
- private static final int INACTIVE = 1;
- private static final int SCANNING = 2;
- private static final int ASSOCIATING = 3;
- private static final int ASSOCIATED = 4;
- private static final int FOUR_WAY_HANDSHAKE = 5;
- private static final int GROUP_HANDSHAKE = 6;
- private static final int COMPLETED = 7;
- private static final int DORMANT = 8;
- private static final int UNINITIALIZED = 9;
- private static final int INVALID = 10;
-
- private HierarchicalState mUninitializedState;
- private HierarchicalState mInitializedState;
- private HierarchicalState mInactiveState;
- private HierarchicalState mDisconnectState;
- private HierarchicalState mScanState;
- private HierarchicalState mConnectState;
- private HierarchicalState mHandshakeState;
- private HierarchicalState mCompletedState;
- private HierarchicalState mDormantState;
-
-
- public SupplicantStateTracker(Context context, Handler target) {
- super(TAG, target.getLooper());
-
- mUninitializedState = new UninitializedState();
- mInitializedState = new InitializedState();
- mInactiveState = new InactiveState();
- mDisconnectState = new DisconnectedState();
- mScanState = new ScanState();
- mConnectState = new ConnectState();
- mHandshakeState = new HandshakeState();
- mCompletedState = new CompletedState();
- mDormantState = new DormantState();
-
-
- addState(mUninitializedState);
- addState(mInitializedState);
- addState(mInactiveState, mInitializedState);
- addState(mDisconnectState, mInitializedState);
- addState(mScanState, mInitializedState);
- addState(mConnectState, mInitializedState);
- addState(mHandshakeState, mConnectState);
- addState(mCompletedState, mConnectState);
- addState(mDormantState, mInitializedState);
-
- setInitialState(mUninitializedState);
-
- //start the state machine
- start();
- }
-
- public void handleEvent(StateChangeResult stateChangeResult) {
- SupplicantState newState = (SupplicantState) stateChangeResult.state;
-
- // Supplicant state change
- // [31-13] Reserved for future use
- // [8 - 0] Supplicant state (as defined in SupplicantState.java)
- // 50023 supplicant_state_changed (custom|1|5)
- EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, newState.ordinal());
-
- sendMessage(obtainMessage(newState.ordinal(), stateChangeResult));
- }
-
- public void resetSupplicantState() {
- transitionTo(mUninitializedState);
- }
-
- private void resetLoopDetection() {
- mLoopDetectCount = 0;
- mLoopDetectIndex = 0;
- }
-
- private boolean handleTransition(Message msg) {
- if (DBG) Log.d(TAG, getName() + msg.toString() + "\n");
- switch (msg.what) {
- case DISCONNECTED:
- transitionTo(mDisconnectState);
- break;
- case SCANNING:
- transitionTo(mScanState);
- break;
- case ASSOCIATING:
- StateChangeResult stateChangeResult = (StateChangeResult) msg.obj;
- /* BSSID is valid only in ASSOCIATING state */
- mWifiInfo.setBSSID(stateChangeResult.BSSID);
- //$FALL-THROUGH$
- case ASSOCIATED:
- case FOUR_WAY_HANDSHAKE:
- case GROUP_HANDSHAKE:
- transitionTo(mHandshakeState);
- break;
- case COMPLETED:
- transitionTo(mCompletedState);
- break;
- case DORMANT:
- transitionTo(mDormantState);
- break;
- case INACTIVE:
- transitionTo(mInactiveState);
- break;
- case UNINITIALIZED:
- case INVALID:
- transitionTo(mUninitializedState);
- break;
- default:
- return NOT_HANDLED;
- }
- StateChangeResult stateChangeResult = (StateChangeResult) msg.obj;
- SupplicantState supState = (SupplicantState) stateChangeResult.state;
- setDetailedState(WifiInfo.getDetailedStateOf(supState));
- mWifiInfo.setSupplicantState(supState);
- mWifiInfo.setNetworkId(stateChangeResult.networkId);
- //TODO: Modify WifiMonitor to report SSID on events
- //mWifiInfo.setSSID()
- return HANDLED;
- }
-
- /********************************************************
- * HSM states
- *******************************************************/
-
- class InitializedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- }
- @Override
- public boolean processMessage(Message message) {
- if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
- switch (message.what) {
- case CMD_START_SCAN:
- WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
- break;
- default:
- if (DBG) Log.w(TAG, "Ignoring " + message);
- break;
- }
- return HANDLED;
- }
- }
-
- class UninitializedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- mNetworkInfo.setIsAvailable(false);
- resetLoopDetection();
- mPasswordKeyMayBeIncorrect = false;
- }
- @Override
- public boolean processMessage(Message message) {
- switch(message.what) {
- default:
- if (!handleTransition(message)) {
- if (DBG) Log.w(TAG, "Ignoring " + message);
- }
- break;
- }
- return HANDLED;
- }
- }
-
- class InactiveState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- Message message = getCurrentMessage();
- StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
-
- mNetworkInfo.setIsAvailable(false);
- resetLoopDetection();
- mPasswordKeyMayBeIncorrect = false;
-
- sendSupplicantStateChangedBroadcast(stateChangeResult, false);
- }
- @Override
- public boolean processMessage(Message message) {
- return handleTransition(message);
- }
- }
-
-
- class DisconnectedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- Message message = getCurrentMessage();
- StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
-
- mNetworkInfo.setIsAvailable(true);
- resetLoopDetection();
-
- /* If a disconnect event happens after a password key failure
- * event, disable the network
- */
- if (mPasswordKeyMayBeIncorrect) {
- Log.d(TAG, "Failed to authenticate, disabling network " +
- mWifiInfo.getNetworkId());
- WifiNative.disableNetworkCommand(mWifiInfo.getNetworkId());
- mPasswordKeyMayBeIncorrect = false;
- sendSupplicantStateChangedBroadcast(stateChangeResult, true);
- }
- else {
- sendSupplicantStateChangedBroadcast(stateChangeResult, false);
- }
- }
- @Override
- public boolean processMessage(Message message) {
- return handleTransition(message);
- }
- }
-
- class ScanState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- Message message = getCurrentMessage();
- StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
-
- mNetworkInfo.setIsAvailable(true);
- mPasswordKeyMayBeIncorrect = false;
- resetLoopDetection();
- sendSupplicantStateChangedBroadcast(stateChangeResult, false);
- }
- @Override
- public boolean processMessage(Message message) {
- return handleTransition(message);
- }
- }
-
- class ConnectState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- }
- @Override
- public boolean processMessage(Message message) {
- switch (message.what) {
- case CMD_START_SCAN:
- WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE);
- WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
- class HandshakeState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- final Message message = getCurrentMessage();
- StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
-
- mNetworkInfo.setIsAvailable(true);
-
- if (mLoopDetectIndex > message.what) {
- mLoopDetectCount++;
- }
- if (mLoopDetectCount > MAX_SUPPLICANT_LOOP_ITERATIONS) {
- WifiNative.disableNetworkCommand(stateChangeResult.networkId);
- mLoopDetectCount = 0;
- }
-
- mLoopDetectIndex = message.what;
-
- mPasswordKeyMayBeIncorrect = false;
- sendSupplicantStateChangedBroadcast(stateChangeResult, false);
- }
- @Override
- public boolean processMessage(Message message) {
- return handleTransition(message);
- }
- }
-
- class CompletedState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- Message message = getCurrentMessage();
- StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
-
- mNetworkInfo.setIsAvailable(true);
-
- mRssiPollToken++;
- if (mEnableRssiPolling) {
- sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
- POLL_RSSI_INTERVAL_MSECS);
- }
-
- resetLoopDetection();
-
- mPasswordKeyMayBeIncorrect = false;
- sendSupplicantStateChangedBroadcast(stateChangeResult, false);
- }
- @Override
- public boolean processMessage(Message message) {
- switch(message.what) {
- case ASSOCIATING:
- case ASSOCIATED:
- case FOUR_WAY_HANDSHAKE:
- case GROUP_HANDSHAKE:
- case COMPLETED:
- break;
- case CMD_RSSI_POLL:
- if (message.arg1 == mRssiPollToken) {
- // Get Info and continue polling
- requestPolledInfo();
- sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
- POLL_RSSI_INTERVAL_MSECS);
- } else {
- // Polling has completed
- }
- break;
- case CMD_ENABLE_RSSI_POLL:
- mRssiPollToken++;
- if (mEnableRssiPolling) {
- // first poll
- requestPolledInfo();
- sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0),
- POLL_RSSI_INTERVAL_MSECS);
- }
- break;
- default:
- return handleTransition(message);
- }
- return HANDLED;
- }
- }
-
- class DormantState extends HierarchicalState {
- @Override
- public void enter() {
- if (DBG) Log.d(TAG, getName() + "\n");
- Message message = getCurrentMessage();
- StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
-
- mNetworkInfo.setIsAvailable(true);
- resetLoopDetection();
- mPasswordKeyMayBeIncorrect = false;
-
- sendSupplicantStateChangedBroadcast(stateChangeResult, false);
-
- /* TODO: reconnect is now being handled at DHCP failure handling
- * If we run into issues with staying in Dormant state, might
- * need a reconnect here
- */
- }
- @Override
- public boolean processMessage(Message message) {
- return handleTransition(message);
- }
- }
- }
-
- private class NotificationEnabledSettingObserver extends ContentObserver {
-
- public NotificationEnabledSettingObserver(Handler handler) {
- super(handler);
- }
-
- public void register() {
- ContentResolver cr = mContext.getContentResolver();
- cr.registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this);
- mNotificationEnabled = getValue();
- }
-
- @Override
- public void onChange(boolean selfChange) {
- super.onChange(selfChange);
-
- mNotificationEnabled = getValue();
- if (!mNotificationEnabled) {
- // Remove any notification that may be showing
- setNotificationVisible(false, 0, true, 0);
- }
-
- resetNotificationTimer();
- }
-
- private boolean getValue() {
- return Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1;
- }
- }
-}
+} \ No newline at end of file