diff options
222 files changed, 11996 insertions, 1082 deletions
@@ -100,6 +100,7 @@ LOCAL_SRC_FILES += \ core/java/android/bluetooth/IBluetoothA2dp.aidl \ core/java/android/bluetooth/IBluetoothCallback.aidl \ core/java/android/bluetooth/IBluetoothHeadset.aidl \ + core/java/android/bluetooth/IBluetoothHid.aidl \ core/java/android/bluetooth/IBluetoothPbap.aidl \ core/java/android/content/IContentService.aidl \ core/java/android/content/IIntentReceiver.aidl \ diff --git a/api/current.xml b/api/current.xml index 9d259f3..4a8ecb7 100644 --- a/api/current.xml +++ b/api/current.xml @@ -29016,6 +29016,50 @@ visibility="public" > </field> +<field name="PERIPHERAL_COMBO_KEYBORD_POINTING" + type="int" + transient="false" + volatile="false" + value="1472" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="PERIPHERAL_KEYBORD" + type="int" + transient="false" + volatile="false" + value="1344" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="PERIPHERAL_POINTING_DEVICE" + type="int" + transient="false" + volatile="false" + value="1408" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="PERIPHERAL_UNCATEGORIZED" + type="int" + transient="false" + volatile="false" + value="1280" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="PHONE_CELLULAR" type="int" transient="false" @@ -59431,7 +59475,7 @@ <method name="drawText" return="void" abstract="false" - native="true" + native="false" synchronized="false" static="false" final="false" @@ -71548,6 +71592,19 @@ visibility="public" > </method> +<method name="open" + return="android.hardware.Camera" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="cameraNode" type="java.lang.String"> +</parameter> +</method> <method name="reconnect" return="void" abstract="false" @@ -71908,6 +71965,28 @@ visibility="public" > </method> +<method name="getAutoExposure" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getBrightness" + return="float" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getColorEffect" return="java.lang.String" abstract="false" @@ -71919,6 +71998,61 @@ visibility="public" > </method> +<method name="getContrast" + return="float" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getDefaultBrightness" + return="float" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getDefaultContrast" + return="float" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getDefaultSaturation" + return="float" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getDefaultSharpness" + return="float" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getExposureCompensation" return="int" abstract="false" @@ -71985,6 +72119,17 @@ visibility="public" > </method> +<method name="getISOValue" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getInt" return="int" abstract="false" @@ -72031,6 +72176,39 @@ visibility="public" > </method> +<method name="getLensShade" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getMaxBrightness" + return="float" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getMaxContrast" + return="float" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getMaxExposureCompensation" return="int" abstract="false" @@ -72042,6 +72220,28 @@ visibility="public" > </method> +<method name="getMaxSaturation" + return="float" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getMaxSharpness" + return="float" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getMaxZoom" return="int" abstract="false" @@ -72119,6 +72319,17 @@ visibility="public" > </method> +<method name="getSaturation" + return="float" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getSceneMode" return="java.lang.String" abstract="false" @@ -72130,6 +72341,17 @@ visibility="public" > </method> +<method name="getSharpness" + return="float" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getSupportedAntibanding" return="java.util.List<java.lang.String>" abstract="false" @@ -72141,6 +72363,17 @@ visibility="public" > </method> +<method name="getSupportedAutoexposure" + return="java.util.List<java.lang.String>" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getSupportedColorEffects" return="java.util.List<java.lang.String>" abstract="false" @@ -72174,6 +72407,17 @@ visibility="public" > </method> +<method name="getSupportedIsoValues" + return="java.util.List<java.lang.String>" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getSupportedJpegThumbnailSizes" return="java.util.List<android.hardware.Camera.Size>" abstract="false" @@ -72185,6 +72429,17 @@ visibility="public" > </method> +<method name="getSupportedLensShadeModes" + return="java.util.List<java.lang.String>" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getSupportedPictureFormats" return="java.util.List<java.lang.Integer>" abstract="false" @@ -72306,6 +72561,17 @@ visibility="public" > </method> +<method name="isSmartContrastEnabled" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="isSmoothZoomSupported" return="boolean" abstract="false" @@ -72395,6 +72661,32 @@ <parameter name="antibanding" type="java.lang.String"> </parameter> </method> +<method name="setAutoExposure" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="value" type="java.lang.String"> +</parameter> +</method> +<method name="setBrightness" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="brightness" type="float"> +</parameter> +</method> <method name="setColorEffect" return="void" abstract="false" @@ -72408,6 +72700,19 @@ <parameter name="value" type="java.lang.String"> </parameter> </method> +<method name="setContrast" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="contrast" type="float"> +</parameter> +</method> <method name="setExposureCompensation" return="void" abstract="false" @@ -72512,6 +72817,19 @@ <parameter name="timestamp" type="long"> </parameter> </method> +<method name="setISOValue" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="iso" type="java.lang.String"> +</parameter> +</method> <method name="setJpegQuality" return="void" abstract="false" @@ -72553,6 +72871,19 @@ <parameter name="height" type="int"> </parameter> </method> +<method name="setLensShade" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="lensshade" type="java.lang.String"> +</parameter> +</method> <method name="setPictureFormat" return="void" abstract="false" @@ -72635,6 +72966,19 @@ <parameter name="rotation" type="int"> </parameter> </method> +<method name="setSaturation" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="saturation" type="float"> +</parameter> +</method> <method name="setSceneMode" return="void" abstract="false" @@ -72648,6 +72992,32 @@ <parameter name="value" type="java.lang.String"> </parameter> </method> +<method name="setSharpness" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="sharpness" type="float"> +</parameter> +</method> +<method name="setSmartContrastEnabled" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="enabled" type="boolean"> +</parameter> +</method> <method name="setWhiteBalance" return="void" abstract="false" @@ -72731,6 +73101,39 @@ visibility="public" > </field> +<field name="AUTO_EXPOSURE_CENTER_WEIGHTED" + type="java.lang.String" + transient="false" + volatile="false" + value=""meter-center"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="AUTO_EXPOSURE_FRAME_AVG" + type="java.lang.String" + transient="false" + volatile="false" + value=""meter-average"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="AUTO_EXPOSURE_SPOT_METERING" + type="java.lang.String" + transient="false" + volatile="false" + value=""meter-spot"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="EFFECT_AQUA" type="java.lang.String" transient="false" @@ -72940,6 +73343,116 @@ visibility="public" > </field> +<field name="FOCUS_MODE_NORMAL" + type="java.lang.String" + transient="false" + volatile="false" + value=""normal"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ISO_100" + type="java.lang.String" + transient="false" + volatile="false" + value=""100"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ISO_1250" + type="java.lang.String" + transient="false" + volatile="false" + value=""1250"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ISO_200" + type="java.lang.String" + transient="false" + volatile="false" + value=""200"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ISO_400" + type="java.lang.String" + transient="false" + volatile="false" + value=""400"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ISO_800" + type="java.lang.String" + transient="false" + volatile="false" + value=""800"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ISO_AUTO" + type="java.lang.String" + transient="false" + volatile="false" + value=""auto"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ISO_HJR" + type="java.lang.String" + transient="false" + volatile="false" + value=""deblur"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="LENSSHADE_DISABLE" + type="java.lang.String" + transient="false" + volatile="false" + value=""disable"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="LENSSHADE_ENABLE" + type="java.lang.String" + transient="false" + volatile="false" + value=""enable"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="SCENE_MODE_ACTION" type="java.lang.String" transient="false" @@ -73311,6 +73824,80 @@ > </field> </class> +<class name="CameraSwitch" + extends="java.lang.Object" + abstract="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<constructor name="CameraSwitch" + type="android.hardware.CameraSwitch" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</constructor> +<method name="hasCameraSwitch" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="openCamera" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="cameraNode" type="java.lang.String"> +</parameter> +</method> +<method name="openMainCamera" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<field name="SWITCH_CAMERA_MAIN" + type="java.lang.String" + transient="false" + volatile="false" + value=""main"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="SWITCH_CAMERA_SECONDARY" + type="java.lang.String" + transient="false" + volatile="false" + value=""secondary"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> <class name="GeomagneticField" extends="java.lang.Object" abstract="false" @@ -82539,6 +83126,17 @@ <parameter name="quality" type="int"> </parameter> </method> +<field name="QUALITY_FRONT" + type="int" + transient="false" + volatile="false" + value="2" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="QUALITY_HIGH" type="int" transient="false" @@ -82745,6 +83343,226 @@ > </field> </class> +<class name="EncoderCapabilities" + extends="java.lang.Object" + abstract="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<method name="getAudioEncoders" + return="java.util.List<android.media.EncoderCapabilities.AudioEncoderCap>" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getOutputFileFormats" + return="int[]" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getVideoEncoders" + return="java.util.List<android.media.EncoderCapabilities.VideoEncoderCap>" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +</class> +<class name="EncoderCapabilities.AudioEncoderCap" + extends="java.lang.Object" + abstract="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<field name="mCodec" + type="int" + transient="false" + volatile="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="mMaxBitRate" + type="int" + transient="false" + volatile="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="mMaxChannels" + type="int" + transient="false" + volatile="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="mMaxSampleRate" + type="int" + transient="false" + volatile="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="mMinBitRate" + type="int" + transient="false" + volatile="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="mMinChannels" + type="int" + transient="false" + volatile="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="mMinSampleRate" + type="int" + transient="false" + volatile="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> +<class name="EncoderCapabilities.VideoEncoderCap" + extends="java.lang.Object" + abstract="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<field name="mCodec" + type="int" + transient="false" + volatile="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="mMaxBitRate" + type="int" + transient="false" + volatile="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="mMaxFrameHeight" + type="int" + transient="false" + volatile="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="mMaxFrameRate" + type="int" + transient="false" + volatile="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="mMaxFrameWidth" + type="int" + transient="false" + volatile="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="mMinBitRate" + type="int" + transient="false" + volatile="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="mMinFrameHeight" + type="int" + transient="false" + volatile="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="mMinFrameRate" + type="int" + transient="false" + volatile="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="mMinFrameWidth" + type="int" + transient="false" + volatile="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> <class name="ExifInterface" extends="java.lang.Object" abstract="false" @@ -84544,6 +85362,21 @@ <parameter name="c" type="android.hardware.Camera"> </parameter> </method> +<method name="setCameraParameters" + return="void" + abstract="false" + native="true" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="params" type="java.lang.String"> +</parameter> +<exception name="IllegalStateException" type="java.lang.IllegalStateException"> +</exception> +</method> <method name="setMaxDuration" return="void" abstract="false" @@ -84825,6 +85658,17 @@ deprecated="not deprecated" visibility="public" > +<field name="AAC" + type="int" + transient="false" + volatile="false" + value="3" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="AMR_NB" type="int" transient="false" @@ -169362,6 +170206,94 @@ visibility="public" > </field> +<field name="KEYCODE_FUNC_1" + type="int" + transient="false" + volatile="false" + value="92" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_FUNC_2" + type="int" + transient="false" + volatile="false" + value="93" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_FUNC_3" + type="int" + transient="false" + volatile="false" + value="94" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_FUNC_4" + type="int" + transient="false" + volatile="false" + value="95" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_FUNC_5" + type="int" + transient="false" + volatile="false" + value="96" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_FUNC_6" + type="int" + transient="false" + volatile="false" + value="97" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_FUNC_7" + type="int" + transient="false" + volatile="false" + value="98" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_FUNC_8" + type="int" + transient="false" + volatile="false" + value="99" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="KEYCODE_G" type="int" transient="false" @@ -169692,6 +170624,17 @@ visibility="public" > </field> +<field name="KEYCODE_QUECHAR" + type="int" + transient="false" + volatile="false" + value="100" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="KEYCODE_R" type="int" transient="false" @@ -214021,7 +214964,7 @@ deprecated="not deprecated" visibility="public" > -<parameter name="arg0" type="T"> +<parameter name="t" type="T"> </parameter> </method> </interface> diff --git a/awt/com/android/internal/awt/AndroidGraphics2D.java b/awt/com/android/internal/awt/AndroidGraphics2D.java index 9a8ae02..b9cd091 100644 --- a/awt/com/android/internal/awt/AndroidGraphics2D.java +++ b/awt/com/android/internal/awt/AndroidGraphics2D.java @@ -406,7 +406,7 @@ public class AndroidGraphics2D extends Graphics2D { mP.setStrokeWidth(0); mC.drawText(str.toCharArray(), 0, str.toCharArray().length, x, y, - mP); + mP,false); mP.setStyle(tmp); } @@ -1276,7 +1276,7 @@ public class AndroidGraphics2D extends Graphics2D { @Override public void drawChars(char[] data, int offset, int length, int x, int y) { - mC.drawText(data, offset, length, x, y, mP); + mC.drawText(data, offset, length, x, y, mP,false); } @Override diff --git a/camera/libcameraservice/Android.mk b/camera/libcameraservice/Android.mk index df5c166..4430541 100644 --- a/camera/libcameraservice/Android.mk +++ b/camera/libcameraservice/Android.mk @@ -48,9 +48,13 @@ LOCAL_SHARED_LIBRARIES:= \ libutils \ libbinder \ libcutils \ - libmedia \ - libcamera_client \ - libsurfaceflinger_client + libmedia + +ifneq ($(BOARD_USES_ECLAIR_LIBCAMERA),true) + LOCAL_SHARED_LIBRARIES += \ + libsurfaceflinger_client \ + libcamera_client +endif LOCAL_MODULE:= libcameraservice diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp index 118249e..0ac089f 100644 --- a/camera/libcameraservice/CameraService.cpp +++ b/camera/libcameraservice/CameraService.cpp @@ -67,6 +67,16 @@ extern "C" { static int debug_frame_cnt; #endif +struct camera_size_type { + int width; + int height; +}; + +static const camera_size_type preview_sizes[] = { + { 1280, 720 }, // 720P + { 768, 432 }, +}; + static int getCallingPid() { return IPCThreadState::self()->getCallingPid(); } @@ -555,6 +565,13 @@ status_t CameraService::Client::setOverlay() CameraParameters params(mHardware->getParameters()); params.getPreviewSize(&w, &h); + //for 720p recording , preview can be 800X448 + if(w == preview_sizes[0].width && h==preview_sizes[0].height){ + LOGD("Changing overlay dimensions to 768X432 for 720p recording."); + w = preview_sizes[1].width; + h = preview_sizes[1].height; + } + if ( w != mOverlayW || h != mOverlayH ) { // Force the destruction of any previous overlay @@ -606,6 +623,13 @@ status_t CameraService::Client::registerPreviewBuffers() CameraParameters params(mHardware->getParameters()); params.getPreviewSize(&w, &h); + //for 720p recording , preview can be 800X448 + if(w == preview_sizes[0].width && h== preview_sizes[0].height){ + LOGD("registerpreviewbufs :changing dimensions to 768X432 for 720p recording."); + w = preview_sizes[1].width; + h = preview_sizes[1].height; + } + // don't use a hardcoded format here ISurface::BufferHeap buffers(w, h, w, h, HAL_PIXEL_FORMAT_YCrCb_420_SP, @@ -886,32 +910,39 @@ status_t CameraService::Client::takePicture() // snapshot taken void CameraService::Client::handleShutter( - image_rect_type *size // The width and height of yuv picture for + image_rect_type *size, // The width and height of yuv picture for // registerBuffer. If this is NULL, use the picture // size from parameters. + bool playShutterSoundOnly ) { // Play shutter sound. - if (mMediaPlayerClick.get() != NULL) { - // do not play shutter sound if stream volume is 0 - // (typically because ringer mode is silent). - int index; - AudioSystem::getStreamVolumeIndex(AudioSystem::ENFORCED_AUDIBLE, &index); - if (index != 0) { - mMediaPlayerClick->seekTo(0); - mMediaPlayerClick->start(); + + if(playShutterSoundOnly) { + + if (mMediaPlayerClick.get() != NULL) { + // do not play shutter sound if stream volume is 0 + // (typically because ringer mode is silent). + int index; + AudioSystem::getStreamVolumeIndex(AudioSystem::ENFORCED_AUDIBLE, &index); + if (index != 0) { + mMediaPlayerClick->seekTo(0); + mMediaPlayerClick->start(); + } + } + sp<ICameraClient> c = mCameraClient; + if (c != NULL) { + c->notifyCallback(CAMERA_MSG_SHUTTER, 0, 0); } + return ; } + // Screen goes black after the buffer is unregistered. if (mSurface != 0 && !mUseOverlay) { mSurface->unregisterBuffers(); } - sp<ICameraClient> c = mCameraClient; - if (c != NULL) { - c->notifyCallback(CAMERA_MSG_SHUTTER, 0, 0); - } mHardware->disableMsgType(CAMERA_MSG_SHUTTER); // It takes some time before yuvPicture callback to be called. @@ -928,6 +959,7 @@ void CameraService::Client::handleShutter( h &= ~1; LOGV("Snapshot image width=%d, height=%d", w, h); } + // FIXME: don't use hardcoded format constants here ISurface::BufferHeap buffers(w, h, w, h, HAL_PIXEL_FORMAT_YCrCb_420_SP, mOrientation, 0, @@ -1085,7 +1117,7 @@ void CameraService::Client::notifyCallback(int32_t msgType, int32_t ext1, int32_ switch (msgType) { case CAMERA_MSG_SHUTTER: // ext1 is the dimension of the yuv picture. - client->handleShutter((image_rect_type *)ext1); + client->handleShutter((image_rect_type *)ext1, (bool)ext2); break; default: sp<ICameraClient> c = client->mCameraClient; diff --git a/camera/libcameraservice/CameraService.h b/camera/libcameraservice/CameraService.h index 75e96c6..f04f39c 100644 --- a/camera/libcameraservice/CameraService.h +++ b/camera/libcameraservice/CameraService.h @@ -144,7 +144,7 @@ private: static sp<Client> getClientFromCookie(void* user); void handlePreviewData(const sp<IMemory>&); - void handleShutter(image_rect_type *image); + void handleShutter(image_rect_type *image, bool playShutterSoundOnly); void handlePostview(const sp<IMemory>&); void handleRawPicture(const sp<IMemory>&); void handleCompressedPicture(const sp<IMemory>&); diff --git a/camera/tests/CameraServiceTest/Android.mk b/camera/tests/CameraServiceTest/Android.mk index 9bb190a..5bacb30 100644 --- a/camera/tests/CameraServiceTest/Android.mk +++ b/camera/tests/CameraServiceTest/Android.mk @@ -6,7 +6,7 @@ LOCAL_SRC_FILES:= CameraServiceTest.cpp LOCAL_MODULE:= CameraServiceTest -LOCAL_MODULE_TAGS := tests +LOCAL_MODULE_TAGS := eng tests LOCAL_C_INCLUDES += \ frameworks/base/libs @@ -17,8 +17,12 @@ LOCAL_SHARED_LIBRARIES += \ libbinder \ libcutils \ libutils \ - libui \ - libcamera_client \ - libsurfaceflinger_client + libui + +ifneq ($(BOARD_USES_ECLAIR_LIBCAMERA),true) + LOCAL_SHARED_LIBRARIES += \ + libcamera_client \ + libsurfaceflinger_client +endif include $(BUILD_EXECUTABLE) @@ -1,3 +1,4 @@ +#!/system/bin/sh # Script to start "am" on the device, which has a very rudimentary # shell. # diff --git a/cmds/bmgr/bmgr b/cmds/bmgr/bmgr index 6b4bbe2..60b5833 100755 --- a/cmds/bmgr/bmgr +++ b/cmds/bmgr/bmgr @@ -1,3 +1,4 @@ +#!/system/bin/sh # Script to start "bmgr" on the device, which has a very rudimentary # shell. # diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk index 2b89759..2876362 100644 --- a/cmds/bootanimation/Android.mk +++ b/cmds/bootanimation/Android.mk @@ -21,8 +21,12 @@ LOCAL_SHARED_LIBRARIES := \ libui \ libskia \ libEGL \ - libGLESv1_CM \ - libsurfaceflinger_client + libGLESv1_CM + +ifneq ($(BOARD_USES_ECLAIR_LIBCAMERA),true) + LOCAL_SHARED_LIBRARIES += \ + libsurfaceflinger_client +endif LOCAL_C_INCLUDES := \ $(call include-path-for, corecg graphics) diff --git a/cmds/ime/ime b/cmds/ime/ime index 96c56d3..1a1fdd9 100755 --- a/cmds/ime/ime +++ b/cmds/ime/ime @@ -1,3 +1,4 @@ +#!/system/bin/sh # Script to start "pm" on the device, which has a very rudimentary # shell. # diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c index 41f070c..8d571dc 100644 --- a/cmds/installd/commands.c +++ b/cmds/installd/commands.c @@ -405,6 +405,7 @@ int create_cache_path(char path[PKG_PATH_MAX], const char *src) char *tmp; int srclen; int dstlen; + char dexopt_data_only[PROPERTY_VALUE_MAX]; srclen = strlen(src); @@ -417,7 +418,15 @@ int create_cache_path(char path[PKG_PATH_MAX], const char *src) return -1; } - dstlen = srclen + strlen(DALVIK_CACHE_PREFIX) + + const char *cache_path = DALVIK_CACHE_PREFIX; + if (!strncmp(src, "/system", 7)) { + property_get("dalvik.vm.dexopt-data-only", dexopt_data_only, ""); + if (strcmp(dexopt_data_only, "1") != 0) { + cache_path = DALVIK_SYSTEM_CACHE_PREFIX; + } + } + + dstlen = srclen + strlen(cache_path) + strlen(DALVIK_CACHE_POSTFIX) + 1; if (dstlen > PKG_PATH_MAX) { @@ -425,11 +434,11 @@ int create_cache_path(char path[PKG_PATH_MAX], const char *src) } sprintf(path,"%s%s%s", - DALVIK_CACHE_PREFIX, + cache_path, src + 1, /* skip the leading / */ DALVIK_CACHE_POSTFIX); - for(tmp = path + strlen(DALVIK_CACHE_PREFIX); *tmp; tmp++) { + for(tmp = path + strlen(cache_path); *tmp; tmp++) { if (*tmp == '/') { *tmp = '@'; } @@ -454,6 +463,77 @@ static void run_dexopt(int zip_fd, int odex_fd, const char* input_file_name, LOGE("execl(%s) failed: %s\n", DEX_OPT_BIN, strerror(errno)); } +static void run_check_zipalign(const char* input_file) +{ + static const char* ZIPALIGN_BIN = "/system/bin/zipalign"; + execl(ZIPALIGN_BIN, ZIPALIGN_BIN, "-c", "4", input_file, (char*) NULL); +} + +static void run_zipalign(const char* input_file, const char* output_file) +{ + static const char* ZIPALIGN_BIN = "/system/bin/zipalign"; + execl(ZIPALIGN_BIN, ZIPALIGN_BIN, "4", input_file, output_file, (char*) NULL); + LOGE("execl(%s) failed: %s\n", ZIPALIGN_BIN, strerror(errno)); +} + +static int wait_check_zipalign(pid_t pid, const char* apk_path) +{ + int status; + pid_t got_pid; + + while (1) { + got_pid = waitpid(pid, &status, 0); + if (got_pid == -1 && errno == EINTR) { + printf("waitpid interrupted, retrying\n"); + } else { + break; + } + } + + if (got_pid != pid) { + return 1; + } + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { + LOGD("CheckZipAlign: --- END '%s' (not needed) ---\n", apk_path); + return 0; + } else { + LOGW("CheckZipAlign: --- END '%s' (needed) ---\n", apk_path); + return status; + } +} + +static int wait_zipalign(pid_t pid, const char* apk_path) +{ + int status; + pid_t got_pid; + + /* + * Wait for the zipalign process to finish. + */ + while (1) { + got_pid = waitpid(pid, &status, 0); + if (got_pid == -1 && errno == EINTR) { + printf("waitpid interrupted, retrying\n"); + } else { + break; + } + } + if (got_pid != pid) { + LOGW("waitpid failed: wanted %d, got %d: %s\n", + (int) pid, (int) got_pid, strerror(errno)); + return 1; + } + + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { + LOGD("ZipAlign: --- END '%s' (success) ---\n", apk_path); + return 0; + } else { + LOGW("ZipAlign: --- END '%s' --- status=0x%04x, process failed\n", + apk_path, status); + return status; /* always nonzero */ + } +} + static int wait_dexopt(pid_t pid, const char* apk_path) { int status; @@ -486,6 +566,72 @@ static int wait_dexopt(pid_t pid, const char* apk_path) } } +int zipalign(const char *apk_path, uid_t uid, int is_public) +{ + char za_path[PKG_PATH_MAX]; + struct utimbuf ut; + struct stat za_stat, apk_stat; + int res; + + pid_t pid; + pid = fork(); + if (pid == 0) { + run_check_zipalign(apk_path); + exit(67); + } else { + res = wait_check_zipalign(pid, apk_path); + if (res == 0) { + goto notneeded; + } + } + + memset(&apk_stat, 0, sizeof(apk_stat)); + stat(apk_path, &apk_stat); + + strcpy(za_path, apk_path); + strcat(za_path, ".tmp"); + LOGD("ZipAlign: --- BEGIN '%s' ---\n", apk_path); + + pid = fork(); + if (pid == 0) { + run_zipalign(apk_path, za_path); + exit(67); + } else { + res = wait_zipalign(pid, za_path); + if (res != 0) { + LOGE("zipalign failed on '%s' res = %d\n", za_path, res); + goto fail; + } + } + + if (chown(za_path, apk_stat.st_uid, apk_stat.st_gid) < 0) { + LOGE("zipalign cannot chown '%s'", apk_path); + goto fail; + } + if (chmod(za_path, S_IRUSR|S_IWUSR|S_IRGRP | + (is_public ? S_IROTH : 0)) < 0) { + LOGE("zipalign cannot chmod '%s'\n", apk_path); + goto fail; + } + + ut.actime = apk_stat.st_atime; + ut.modtime = apk_stat.st_mtime; + utime(za_path, &ut); + + unlink(apk_path); + rename(za_path, apk_path); + + return 0; + +notneeded: + return 0; + +fail: + unlink(za_path); + return -1; + +} + int dexopt(const char *apk_path, uid_t uid, int is_public) { struct utimbuf ut; @@ -501,6 +647,10 @@ int dexopt(const char *apk_path, uid_t uid, int is_public) if (strlen(apk_path) >= (PKG_PATH_MAX - 8)) { return -1; } + + if (strncmp(apk_path, "/system", 7) != 0) { + zipalign(apk_path, uid, is_public); + } /* platform-specific flags affecting optimization and verification */ property_get("dalvik.vm.dexopt-flags", dexopt_flags, ""); diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h index cfcdb98..fc63b9f 100644 --- a/cmds/installd/installd.h +++ b/cmds/installd/installd.h @@ -64,6 +64,7 @@ #define SDCARD_DIR_PREFIX getenv("ASEC_MOUNTPOINT") #define DALVIK_CACHE_PREFIX "/data/dalvik-cache/" +#define DALVIK_SYSTEM_CACHE_PREFIX "/cache/dalvik-cache/" #define DALVIK_CACHE_POSTFIX "/classes.dex" #define UPDATE_COMMANDS_DIR_PREFIX "/system/etc/updatecmds/" @@ -1,3 +1,4 @@ +#!/system/bin/sh # Script to start "pm" on the device, which has a very rudimentary # shell. # diff --git a/cmds/zipalign/Android.mk b/cmds/zipalign/Android.mk new file mode 100644 index 0000000..3451b94 --- /dev/null +++ b/cmds/zipalign/Android.mk @@ -0,0 +1,25 @@ +# +# Copyright 2008 The Android Open Source Project +# +# Zip alignment tool +# + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + ../../../../build/tools/zipalign/ZipFile.cpp \ + ../../../../build/tools/zipalign/ZipEntry.cpp \ + ../../../../build/tools/zipalign/ZipAlign.cpp + +LOCAL_C_INCLUDES := external/zlib build/tools/zipalign + +LOCAL_SHARED_LIBRARIES := \ + libz \ + libutils \ + libcutils + +LOCAL_MODULE := zipalign + +include $(BUILD_EXECUTABLE) + diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java index f0cef98..ec39ac7 100644 --- a/core/java/android/app/ApplicationErrorReport.java +++ b/core/java/android/app/ApplicationErrorReport.java @@ -174,7 +174,7 @@ public class ApplicationErrorReport implements Parcelable { /** * Return activity in receiverPackage that handles ACTION_APP_ERROR. * - * @param pm PackageManager isntance + * @param pm PackageManager instance * @param errorPackage package which caused the error * @param receiverPackage candidate package to receive the error * @return activity component within receiverPackage which handles diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index f471f57..0f9d314 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1690,8 +1690,9 @@ class ContextImpl extends Context { if (resolveInfo == null) { return null; } - Intent intent = new Intent(Intent.ACTION_MAIN); - intent.setClassName(packageName, resolveInfo.activityInfo.name); + Intent intent = new Intent(intentToResolve); + intent.setClassName(resolveInfo.activityInfo.applicationInfo.packageName, + resolveInfo.activityInfo.name); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); return intent; } diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 6fe12fc..c2d61ab 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -16,12 +16,15 @@ package android.app; +import java.io.FileOutputStream; + import android.content.Context; import android.os.RemoteException; import android.os.Handler; import android.os.IBinder; import android.os.ServiceManager; import android.util.Log; +import android.widget.Toast; /** * Class to notify the user of events that happen. This is how you tell @@ -90,6 +93,22 @@ public class NotificationManager notify(null, id, notification); } + /** @hide */ + public void updatePackageList() { + try { + if(mContext.getPackageName().equals("com.cyanogenmod.cmparts")) { + return; + } + //File file = new File(appContext.getFilesDir(), "trackball_lights"); + FileOutputStream fos = mContext.openFileOutput("trackball_lights", Context.MODE_WORLD_READABLE); + String blank = "yes"; + fos.write(blank.getBytes()); + fos.close(); + } catch(Exception e) { + Log.d("WriteApps", "Error: " + e.toString() ); + } + } + /** * Persistent notification on the status bar, * @@ -105,9 +124,13 @@ public class NotificationManager int[] idOut = new int[1]; INotificationManager service = getService(); String pkg = mContext.getPackageName(); + if((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) { + updatePackageList(); + } if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")"); try { service.enqueueNotificationWithTag(pkg, tag, id, notification, idOut); + //Log.i("NotificationManager", "Pulsing: " + pkg); if (id != idOut[0]) { Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]); } diff --git a/core/java/android/bluetooth/BluetoothClass.java b/core/java/android/bluetooth/BluetoothClass.java index c7fea9e..fc50ad0 100644 --- a/core/java/android/bluetooth/BluetoothClass.java +++ b/core/java/android/bluetooth/BluetoothClass.java @@ -201,6 +201,12 @@ public final class BluetoothClass implements Parcelable { //public static final int AUDIO_VIDEO_RESERVED = 0x0444; public static final int AUDIO_VIDEO_VIDEO_GAMING_TOY = 0x0448; + // Devices in the PERIPHERAL major class + public static final int PERIPHERAL_UNCATEGORIZED = 0x0500; + public static final int PERIPHERAL_KEYBORD = 0x0540; + public static final int PERIPHERAL_POINTING_DEVICE = 0x0580; + public static final int PERIPHERAL_COMBO_KEYBORD_POINTING = 0x05C0; + // Devices in the WEARABLE major class public static final int WEARABLE_UNCATEGORIZED = 0x0700; public static final int WEARABLE_WRIST_WATCH = 0x0704; @@ -259,6 +265,8 @@ public final class BluetoothClass implements Parcelable { public static final int PROFILE_A2DP = 1; /** @hide */ public static final int PROFILE_OPP = 2; + /** @hide */ + public static final int PROFILE_HID = 3; /** * Check class bits for possible bluetooth profile support. diff --git a/core/java/android/bluetooth/BluetoothHid.java b/core/java/android/bluetooth/BluetoothHid.java new file mode 100644 index 0000000..45347d3 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothHid.java @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2009 ISB Corporation + * Copyright (C) 2010 0xlab + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.bluetooth; + +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.server.BluetoothHidService; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.ServiceManager; +import android.os.RemoteException; +import android.os.IBinder; +import android.util.Log; + +import java.util.List; +import java.util.Arrays; +import java.util.Collections; +import java.util.Set; +import java.util.HashSet; +/** + * Public API for controlling the Bluetooth HID Profile Service. + * + * BluetoothHid is a proxy object for controlling the Bluetooth HID + * Service via IPC. + * + * + * Currently the BluetoothHid service runs in the system server and this + * proxy object will be immediately bound to the service on construction. + * However this may change in future releases, and error codes such as + * BluetoothError.ERROR_IPC_NOT_READY will be returned from this API when the + * proxy object is not yet attached. + * + * Currently this class provides methods to connect to HID devices. + * + * @hide + */ +public class BluetoothHid { + private static final String TAG = "BluetoothHid"; + private static final boolean DBG = false; + + /** int extra for DEVICE_STATE_CHANGED_ACTION */ + public static final String HID_DEVICE_STATE = + "android.bluetooth.hid.intent.HID_DEVICE_STATE"; + /** int extra for DEVICE_STATE_CHANGED_ACTION */ + public static final String HID_DEVICE_PREVIOUS_STATE = + "android.bluetooth.hid.intent.HID_DEVICE_PREVIOUS_STATE"; + + /** Indicates the state of an HID device has changed. + * This intent will always contain HID_DEVICE_STATE, and + * BluetoothIntent.ADDRESS extras. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String HID_DEVICE_STATE_CHANGED_ACTION = + "android.bluetooth.hid.intent.action.HID_DEVICE_STATE_CHANGED"; + + public static final int STATE_DISCONNECTED = 0; + public static final int STATE_CONNECTING = 1; + public static final int STATE_CONNECTED = 2; + public static final int STATE_DISCONNECTING = 3; + + /** Default priority for hid devices that we try to auto-connect + * and allow incoming connections */ + public static final int PRIORITY_AUTO_CONNECT = 1000; + /** Default priority for hid devices that should allow incoming + * connections */ + public static final int PRIORITY_ON = 100; + /** Default priority for hid devices that should not allow incoming + * connections */ + public static final int PRIORITY_OFF = 0; + /** Default priority when not set or when the device is unpaired */ + public static final int PRIORITY_UNDEFINED = -1; + + private final IBluetoothHid mService; + private final Context mContext; + + + /** + * Create a BluetoothHid proxy object. + */ + public BluetoothHid(Context c) { + mContext = c; + + IBinder b = ServiceManager.getService(BluetoothHidService.BLUETOOTH_HID_SERVICE); + if (b != null) { + mService = IBluetoothHid.Stub.asInterface(b); + } else { + Log.w(TAG, "Bluetooth HID service not available!"); + // Instead of throwing an exception which prevents people from going + // into Wireless settings in the emulator. Let it crash later when it is actually used. + mService = null; + } + } + + + /** Initiate a connection to an HID device. + * Listen for HID_DEVICE_STATE_CHANGED_ACTION to find out when the + * connection is completed. + * @param address Remote BT address. + * @return Result code, negative indicates an immediate error. + * @hide + */ + public boolean connectHidDevice(BluetoothDevice device) { + if (DBG) log("connectHidDevice(" + device + ")"); + try { + return mService.connectHidDevice(device); + } catch (RemoteException e) { + Log.w(TAG, "", e); + return false; + } + } + + /** Initiate disconnect from an HID device. + * Listen for HID_DEVICE_STATE_CHANGED_ACTION to find out when + * disconnect is completed. + * @param address Remote BT address. + * @return Result code, negative indicates an immediate error. + * @hide + */ + public boolean disconnectHidDevice(BluetoothDevice device) { + try { + return mService.disconnectHidDevice(device); + } catch (RemoteException e) { + Log.w(TAG, "", e); + return false; + } + } + + /** Check if a specified HID device is connected. + * @param address Remote BT address. + * @return True if connected (or playing), false otherwise and on error. + * @hide + */ + public boolean isHidDeviceConnected(BluetoothDevice device) { + int state = getHidDeviceState(device); + return state == STATE_CONNECTED; + } + + /** Check if any HID device is connected. + * @return a unmodifiable set of connected HID devices, or null on error. + * @hide + */ + public Set<BluetoothDevice> getConnectedSinks() { + if (DBG) log("getConnectedSinks()"); + try { + return Collections.unmodifiableSet( + new HashSet<BluetoothDevice>(Arrays.asList(mService.getConnectedSinks()))); + } catch (RemoteException e) { + Log.w(TAG, "", e); + return null; + } + } + + /** Check if any HID device is in Non Disconnected state + * i.e connected, connecting, disconnecting. + * @return a unmodifiable set of connected HID devices, or null on error. + * @hide + */ + public Set<BluetoothDevice> getNonDisconnectedSinks() { + if (DBG) log("getNonDisconnectedSinks()"); + try { + return Collections.unmodifiableSet( + new HashSet<BluetoothDevice>(Arrays.asList(mService.getNonDisconnectedSinks()))); + } catch (RemoteException e) { + Log.e(TAG, "", e); + return null; + } + } + + /** Get the state of an HID device + * @param address Remote BT address. + * @return State code, or negative on error + * @hide + */ + public int getHidDeviceState(BluetoothDevice device) { + if (DBG) log("getHidDeviceState(" + device + ")"); + try { + return mService.getHidDeviceState(device); + } catch (RemoteException e) { + Log.w(TAG, "", e); + return BluetoothHid.STATE_DISCONNECTED; + } + } + + + /** + * Set priority of HID device. + * Priority is a non-negative integer. By default paired devices will have + * a priority of PRIORITY_AUTO, and unpaired device PRIORITY_NONE (0). + * Sinks with priority greater than zero will accept incoming connections + * (if no sink is currently connected). + * Priority for unpaired sink must be PRIORITY_NONE. + * @param device Paired sink + * @param priority Integer priority, for example PRIORITY_AUTO or + * PRIORITY_NONE + * @return true if priority is set, false on error + */ + public boolean setHidDevicePriority(BluetoothDevice device, int priority) { + if (DBG) log("setHidDevicePriority(" + device + ", " + priority + ")"); + try { + return mService.setHidDevicePriority(device, priority); + } catch (RemoteException e) { + Log.e(TAG, "", e); + return false; + } + } + + /** + * Get priority of HID device. + * @param device Sink + * @return non-negative priority, or negative error code on error. + */ + public int getHidDevicePriority(BluetoothDevice device) { + if (DBG) log("getHidDevicePriority(" + device + ")"); + try { + return mService.getHidDevicePriority(device); + } catch (RemoteException e) { + Log.e(TAG, "", e); + return PRIORITY_OFF; + } + } + + /** Helper for converting a state to a string. + * For debug use only - strings are not internationalized. + * @hide + */ + public static String stateToString(int state) { + switch (state) { + case STATE_DISCONNECTED: + return "disconnected"; + case STATE_CONNECTING: + return "connecting"; + case STATE_CONNECTED: + return "connected"; + case STATE_DISCONNECTING: + return "disconnecting"; + default: + return "<unknown state " + state + ">"; + } + } + + + /** + * Check class bits for possible HID device support. + * This is a simple heuristic that tries to guess if a device with the + * given class bits might be a HID device. It is not accurate for all + * devices. It tries to err on the side of false positives. + * @return True if this device might be a HID device + */ + public static boolean doesClassMatch(BluetoothClass btClass) { + switch (btClass.getDeviceClass()) { + case BluetoothClass.Device.PERIPHERAL_KEYBORD: + case BluetoothClass.Device.PERIPHERAL_POINTING_DEVICE: + case BluetoothClass.Device.PERIPHERAL_COMBO_KEYBORD_POINTING: + return true; + default: + return false; + } + } + + private static void log(String msg) { + Log.d(TAG, msg); + } +} diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java index 4164a3d..f5f0ba6 100644 --- a/core/java/android/bluetooth/BluetoothUuid.java +++ b/core/java/android/bluetooth/BluetoothUuid.java @@ -49,10 +49,12 @@ public final class BluetoothUuid { ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB"); public static final ParcelUuid ObexObjectPush = ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb"); + public static final ParcelUuid HID = + ParcelUuid.fromString("00001124-0000-1000-8000-00805f9b34fb"); public static final ParcelUuid[] RESERVED_UUIDS = { AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget, - ObexObjectPush}; + ObexObjectPush,HID}; public static boolean isAudioSource(ParcelUuid uuid) { return uuid.equals(AudioSource); @@ -82,6 +84,10 @@ public final class BluetoothUuid { return uuid.equals(AvrcpTarget); } + public static boolean isHid(ParcelUuid uuid) { + return uuid.equals(HID); + } + /** * Returns true if ParcelUuid is present in uuidArray * diff --git a/core/java/android/bluetooth/IBluetoothHid.aidl b/core/java/android/bluetooth/IBluetoothHid.aidl new file mode 100644 index 0000000..5941083 --- /dev/null +++ b/core/java/android/bluetooth/IBluetoothHid.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2009 ISB Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.bluetooth; +import android.bluetooth.BluetoothDevice; +/** + * System private API for Bluetooth HID service + * + * {@hide} + */ +interface IBluetoothHid { + boolean connectHidDevice(in BluetoothDevice device); + boolean disconnectHidDevice(in BluetoothDevice device); + BluetoothDevice[] getConnectedSinks(); // change to Set<> once AIDL supports + BluetoothDevice[] getNonDisconnectedSinks(); // change to Set<> once AIDL supports + int getHidDeviceState(in BluetoothDevice device); + boolean setHidDevicePriority(in BluetoothDevice device, int priority); + int getHidDevicePriority(in BluetoothDevice device); +} diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 0608cc0..44e589d 100644..100755 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -25,6 +25,7 @@ import org.xmlpull.v1.XmlPullParserException; import android.graphics.Movie; import android.graphics.drawable.Drawable; import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable.ConstantState; import android.os.Build; import android.os.Bundle; import android.os.SystemProperties; @@ -41,6 +42,8 @@ import java.io.InputStream; import java.lang.ref.WeakReference; import java.util.Locale; +import android.graphics.PorterDuff.Mode; + /** * Class for accessing an application's resources. This sits on top of the * asset manager of the application (accessible through getAssets()) and @@ -66,6 +69,8 @@ public class Resources { = new LongSparseArray<Drawable.ConstantState>(); private static final SparseArray<ColorStateList> mPreloadedColorStateLists = new SparseArray<ColorStateList>(); + private static final LongSparseArray<Drawable.ConstantState> sPreloadedColorDrawables + = new LongSparseArray<Drawable.ConstantState>(); private static boolean mPreloaded; /*package*/ final TypedValue mTmpValue = new TypedValue(); @@ -75,6 +80,8 @@ public class Resources { = new LongSparseArray<WeakReference<Drawable.ConstantState> >(); private final SparseArray<WeakReference<ColorStateList> > mColorStateListCache = new SparseArray<WeakReference<ColorStateList> >(); + private final LongSparseArray<WeakReference<Drawable.ConstantState> > mColorDrawableCache + = new LongSparseArray<WeakReference<Drawable.ConstantState> >(); private boolean mPreloading; /*package*/ TypedArray mCachedStyledAttributes = null; @@ -582,6 +589,41 @@ public class Resources { } } + + /** + * Return a drawable object associated with a particular resource ID. + * Various types of objects will be returned depending on the underlying + * resource -- for example, a solid color, PNG image, scalable image, etc. + * The Drawable API hides these implementation details. + * + * mtwebster: + * This version also applies a Porter Duff color mask onto the object before + * returning the object. Put in Resources to give reusability, I plan on + * applying this to other parts of the gui + * + * @param id The desired resource identifier, as generated by the aapt + * tool. This integer encodes the package, type, and resource + * entry. The value 0 is an invalid identifier. + * + * @param mask The color mask to use (alpha-r-g-b) + * + * @param masktype The Porter Duff filter mode + * + * @throws NotFoundException Throws NotFoundException if the given ID does not exist. + * + * @return Drawable An object that can be used to draw this resource. + * @hide + */ + public Drawable getDrawable(int id, int mask, Mode maskType) throws NotFoundException { + synchronized (mTmpValue) { + TypedValue value = mTmpValue; + getValue(id, value, true); + Drawable tmpDrawable = loadDrawable(value, id); + tmpDrawable.setColorFilter(mask, maskType); + return tmpDrawable; + } + } + /** * Return a movie object associated with the particular resource ID. * @param id The desired resource identifier, as generated by the aapt @@ -1299,37 +1341,13 @@ public class Resources { (int)(mMetrics.density*160), mConfiguration.keyboard, keyboardHidden, mConfiguration.navigation, width, height, mConfiguration.screenLayout, mConfiguration.uiMode, sSdkVersion); - int N = mDrawableCache.size(); - if (DEBUG_CONFIG) { - Log.d(TAG, "Cleaning up drawables config changes: 0x" - + Integer.toHexString(configChanges)); - } - for (int i=0; i<N; i++) { - WeakReference<Drawable.ConstantState> ref = mDrawableCache.valueAt(i); - if (ref != null) { - Drawable.ConstantState cs = ref.get(); - if (cs != null) { - if (Configuration.needNewResources( - configChanges, cs.getChangingConfigurations())) { - if (DEBUG_CONFIG) { - Log.d(TAG, "FLUSHING #0x" - + Long.toHexString(mDrawableCache.keyAt(i)) - + " / " + cs + " with changes: 0x" - + Integer.toHexString(cs.getChangingConfigurations())); - } - mDrawableCache.setValueAt(i, null); - } else if (DEBUG_CONFIG) { - Log.d(TAG, "(Keeping #0x" - + Long.toHexString(mDrawableCache.keyAt(i)) - + " / " + cs + " with changes: 0x" - + Integer.toHexString(cs.getChangingConfigurations()) - + ")"); - } - } - } - } - mDrawableCache.clear(); + + clearDrawableCache(mDrawableCache, configChanges); + clearDrawableCache(mColorDrawableCache, configChanges); + mColorStateListCache.clear(); + + flushLayoutCache(); } synchronized (mSync) { @@ -1339,6 +1357,41 @@ public class Resources { } } + private void clearDrawableCache( + LongSparseArray<WeakReference<ConstantState>> cache, + int configChanges) { + int N = cache.size(); + if (DEBUG_CONFIG) { + Log.d(TAG, "Cleaning up drawables config changes: 0x" + + Integer.toHexString(configChanges)); + } + for (int i=0; i<N; i++) { + WeakReference<Drawable.ConstantState> ref = cache.valueAt(i); + if (ref != null) { + Drawable.ConstantState cs = ref.get(); + if (cs != null) { + if (Configuration.needNewResources( + configChanges, cs.getChangingConfigurations())) { + if (DEBUG_CONFIG) { + Log.d(TAG, "FLUSHING #0x" + + Long.toHexString(mDrawableCache.keyAt(i)) + + " / " + cs + " with changes: 0x" + + Integer.toHexString(cs.getChangingConfigurations())); + } + cache.setValueAt(i, null); + } else if (DEBUG_CONFIG) { + Log.d(TAG, "(Keeping #0x" + + Long.toHexString(cache.keyAt(i)) + + " / " + cs + " with changes: 0x" + + Integer.toHexString(cs.getChangingConfigurations()) + + ")"); + } + } + } + } + cache.clear(); + } + /** * Update the system resources configuration if they have previously * been initialized. @@ -1661,13 +1714,18 @@ public class Resources { } final long key = (((long) value.assetCookie) << 32) | value.data; - Drawable dr = getCachedDrawable(key); + boolean isColorDrawable = false; + if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT && + value.type <= TypedValue.TYPE_LAST_COLOR_INT) { + isColorDrawable = true; + } + Drawable dr = getCachedDrawable(isColorDrawable ? mColorDrawableCache : mDrawableCache, key); if (dr != null) { return dr; } - Drawable.ConstantState cs = sPreloadedDrawables.get(key); + Drawable.ConstantState cs = isColorDrawable ? sPreloadedColorDrawables.get(key) : sPreloadedDrawables.get(key); if (cs != null) { dr = cs.newDrawable(this); } else { @@ -1726,13 +1784,21 @@ public class Resources { cs = dr.getConstantState(); if (cs != null) { if (mPreloading) { - sPreloadedDrawables.put(key, cs); + if (isColorDrawable) { + sPreloadedColorDrawables.put(key, cs); + } else { + sPreloadedDrawables.put(key, cs); + } } else { synchronized (mTmpValue) { //Log.i(TAG, "Saving cached drawable @ #" + // Integer.toHexString(key.intValue()) // + " in " + this + ": " + cs); - mDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs)); + if (isColorDrawable) { + mColorDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs)); + } else { + mDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs)); + } } } } @@ -1741,9 +1807,11 @@ public class Resources { return dr; } - private Drawable getCachedDrawable(long key) { + private Drawable getCachedDrawable( + LongSparseArray<WeakReference<ConstantState>> drawableCache, + long key) { synchronized (mTmpValue) { - WeakReference<Drawable.ConstantState> wr = mDrawableCache.get(key); + WeakReference<Drawable.ConstantState> wr = drawableCache.get(key); if (wr != null) { // we have the key Drawable.ConstantState entry = wr.get(); if (entry != null) { @@ -1753,7 +1821,7 @@ public class Resources { return entry.newDrawable(this); } else { // our entry has been purged - mDrawableCache.delete(key); + drawableCache.delete(key); } } } diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 59c386d..014e21d 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -162,9 +162,18 @@ public class Camera { * example, if the camera is in use by another process). */ public static Camera open() { + CameraSwitch.openMainCamera(); return new Camera(); } + /** + * Returns a new Camera object for the given cameraNode + */ + public static Camera open(String cameraNode) { + CameraSwitch.openCamera(cameraNode); + return new Camera(); + } + Camera() { mShutterCallback = null; mRawImageCallback = null; @@ -804,7 +813,11 @@ public class Camera { * @see #getParameters() */ public void setParameters(Parameters params) { - native_setParameters(params.flatten()); + try { + native_setParameters(params.flatten()); + } catch (RuntimeException ex) { + Log.e(TAG, "Failed to set all parameters"); + } } /** @@ -901,6 +914,8 @@ public class Camera { private static final String KEY_SCENE_MODE = "scene-mode"; private static final String KEY_FLASH_MODE = "flash-mode"; private static final String KEY_FOCUS_MODE = "focus-mode"; + private static final String KEY_ISO_MODE = "iso"; + private static final String KEY_LENSSHADE = "lensshade"; private static final String KEY_FOCAL_LENGTH = "focal-length"; private static final String KEY_HORIZONTAL_VIEW_ANGLE = "horizontal-view-angle"; private static final String KEY_VERTICAL_VIEW_ANGLE = "vertical-view-angle"; @@ -908,11 +923,26 @@ public class Camera { private static final String KEY_MAX_EXPOSURE_COMPENSATION = "max-exposure-compensation"; private static final String KEY_MIN_EXPOSURE_COMPENSATION = "min-exposure-compensation"; private static final String KEY_EXPOSURE_COMPENSATION_STEP = "exposure-compensation-step"; + private static final String KEY_AUTO_EXPOSURE = "meter-mode"; private static final String KEY_ZOOM = "zoom"; private static final String KEY_MAX_ZOOM = "max-zoom"; private static final String KEY_ZOOM_RATIOS = "zoom-ratios"; private static final String KEY_ZOOM_SUPPORTED = "zoom-supported"; private static final String KEY_SMOOTH_ZOOM_SUPPORTED = "smooth-zoom-supported"; + private static final String KEY_SHARPNESS = "sharpness"; + private static final String KEY_MAX_SHARPNESS = "sharpness-max"; + private static final String KEY_DEFAULT_SHARPNESS = "sharpness-def"; + private static final String KEY_CONTRAST = "contrast"; + private static final String KEY_MAX_CONTRAST = "contrast-max"; + private static final String KEY_DEFAULT_CONTRAST = "contrast-def"; + private static final String KEY_SATURATION = "saturation"; + private static final String KEY_MAX_SATURATION = "saturation-max"; + private static final String KEY_DEFAULT_SATURATION = "saturation-def"; + private static final String KEY_BRIGHTNESS = "brightness"; + private static final String KEY_MAX_BRIGHTNESS = "brightness-max"; + private static final String KEY_DEFAULT_BRIGHTNESS = "brightness-def"; + private static final String KEY_SMART_CONTRAST = "smart-contrast"; + // Parameter key suffix for supported values. private static final String SUPPORTED_VALUES_SUFFIX = "-values"; @@ -939,12 +969,34 @@ public class Camera { public static final String EFFECT_BLACKBOARD = "blackboard"; public static final String EFFECT_AQUA = "aqua"; + // Values for auto exposure settings. + public static final String AUTO_EXPOSURE_FRAME_AVG = "meter-average"; + public static final String AUTO_EXPOSURE_CENTER_WEIGHTED = "meter-center"; + public static final String AUTO_EXPOSURE_SPOT_METERING = "meter-spot"; + // Values for antibanding settings. public static final String ANTIBANDING_AUTO = "auto"; public static final String ANTIBANDING_50HZ = "50hz"; public static final String ANTIBANDING_60HZ = "60hz"; public static final String ANTIBANDING_OFF = "off"; + //Values for ISO settings + + public static final String ISO_AUTO = "auto"; + public static final String ISO_HJR = "deblur"; + public static final String ISO_100 = "100"; + public static final String ISO_200 = "200"; + public static final String ISO_400 = "400"; + public static final String ISO_800 = "800"; + public static final String ISO_1250 = "1250"; + + //Values for Lens Shading + + public static final String LENSSHADE_ENABLE = "enable"; + public static final String LENSSHADE_DISABLE= "disable"; + + + // Values for flash mode settings. /** * Flash will not be fired. @@ -1008,6 +1060,7 @@ public class Camera { * {@link #autoFocus(AutoFocusCallback)} in this mode. */ public static final String FOCUS_MODE_INFINITY = "infinity"; + public static final String FOCUS_MODE_NORMAL = "normal"; public static final String FOCUS_MODE_MACRO = "macro"; /** @@ -1145,7 +1198,8 @@ public class Camera { * @return the int value of the parameter */ public int getInt(String key) { - return Integer.parseInt(mMap.get(key)); + String value = mMap.get(key); + return value == null ? 0 : Integer.parseInt(value); } /** @@ -1330,6 +1384,9 @@ public class Camera { */ public List<Integer> getSupportedPreviewFormats() { String str = get(KEY_PREVIEW_FORMAT + SUPPORTED_VALUES_SUFFIX); + if (str == null) { + str = get(KEY_PREVIEW_FORMAT); + } ArrayList<Integer> formats = new ArrayList<Integer>(); for (String s : split(str)) { int f = pixelFormatForCameraFormat(s); @@ -1626,6 +1683,166 @@ public class Camera { /** + * Get Sharpness level + * + * @return sharpness level + */ + public float getSharpness(){ + return getFloat(KEY_SHARPNESS, 0.0f); + } + + /** + * Set Sharpness Level + * + * @param sharpness level + */ + public void setSharpness(float sharpness){ + if((sharpness < 0) || (sharpness > getMaxSharpness()) ) + throw new IllegalArgumentException( + "Invalid Sharpness " + sharpness); + + set(KEY_SHARPNESS, String.valueOf(sharpness)); + } + + /** + * Get Max Sharpness Level + * + * @return max sharpness level + */ + public float getMaxSharpness(){ + return getFloat(KEY_MAX_SHARPNESS, 0.0f); + } + + /** + * Get default sharpness level + * + * @return default sharpness level + */ + public float getDefaultSharpness() { + return getFloat(KEY_DEFAULT_SHARPNESS, 0.0f); + } + + /** + * Get Contrast level + * + * @return contrast level + */ + public float getContrast(){ + return getFloat(KEY_CONTRAST, 0.0f); + } + + /** + * Set Contrast Level + * + * @param contrast level + */ + public void setContrast(float contrast){ + if((contrast < 0 ) || (contrast > getMaxContrast())) + throw new IllegalArgumentException( + "Invalid Contrast " + contrast); + + set(KEY_CONTRAST, String.valueOf(contrast)); + } + + /** + * Get Max Contrast Level + * + * @return max contrast level + */ + public float getMaxContrast(){ + return getFloat(KEY_MAX_CONTRAST, 0.0f); + } + + /** + * Get default contrast level + * + * @return default contrast level + */ + public float getDefaultContrast() { + return getFloat(KEY_DEFAULT_CONTRAST, 0.0f); + } + + /** + * Get Saturation level + * + * @return saturation level + */ + public float getSaturation(){ + return getFloat(KEY_SATURATION, 0.0f); + } + + /** + * Set Saturation Level + * + * @param saturation level + */ + public void setSaturation(float saturation){ + if((saturation < 0 ) || (saturation > getMaxSaturation())) + throw new IllegalArgumentException( + "Invalid Saturation " + saturation); + + set(KEY_SATURATION, String.valueOf(saturation)); + } + + /** + * Get Max Saturation Level + * + * @return max contrast level + */ + public float getMaxSaturation(){ + return getFloat(KEY_MAX_SATURATION, 0.0f); + } + + /** + * Get default saturation level + * + * @return default saturation level + */ + public float getDefaultSaturation() { + return getFloat(KEY_DEFAULT_SATURATION, 0.0f); + } + + /** + * Get brightness level + * + * @return brightness level + */ + public float getBrightness(){ + return getFloat(KEY_BRIGHTNESS, 0.0f); + } + + /** + * Set brightness level + * + * @param brightness level + */ + public void setBrightness(float brightness){ + if((brightness < 0 ) || (brightness > getMaxBrightness())) + throw new IllegalArgumentException( + "Invalid Brightness " + brightness); + + set(KEY_BRIGHTNESS, String.valueOf(brightness)); + } + + /** + * Get Max Brightness Level + * + * @return max brightness level + */ + public float getMaxBrightness(){ + return getFloat(KEY_MAX_BRIGHTNESS, 0.0f); + } + + /** + * Get default brightness level + * + * @return default brightness level + */ + public float getDefaultBrightness() { + return getFloat(KEY_DEFAULT_BRIGHTNESS, 0.0f); + } + + /** * Gets the current antibanding setting. * * @return current antibanding. null if antibanding setting is not @@ -1903,7 +2120,11 @@ public class Camera { * @param value zoom value. The valid range is 0 to {@link #getMaxZoom}. */ public void setZoom(int value) { - set(KEY_ZOOM, value); + if (mMap.containsKey("taking-picture-zoom")) { + set("taking-picture-zoom", value); + } else { + set(KEY_ZOOM, value); + } } /** @@ -1914,7 +2135,7 @@ public class Camera { */ public boolean isZoomSupported() { String str = get(KEY_ZOOM_SUPPORTED); - return TRUE.equals(str); + return TRUE.equals(str) && getMaxZoom() > 0; } /** @@ -1927,7 +2148,11 @@ public class Camera { * @return the maximum zoom value supported by the camera. */ public int getMaxZoom() { - return getInt(KEY_MAX_ZOOM, 0); + if (mMap.containsKey("taking-picture-zoom-max")) { + return getInt("taking-picture-zoom-max", 0); + } else { + return getInt(KEY_MAX_ZOOM, 0); + } } /** @@ -1955,6 +2180,112 @@ public class Camera { return TRUE.equals(str); } + /** + * Gets the current ISO setting. + * + * @return one of ISO_XXX string constant. null if ISO + * setting is not supported. + */ + public String getISOValue() { + return get(KEY_ISO_MODE); + } + + /** + * Sets the ISO. + * + * @param iso ISO_XXX string constant. + */ + public void setISOValue(String iso) { + set(KEY_ISO_MODE, iso); + } + + /** + * Gets the supported ISO values. + * + * @return a List of FLASH_MODE_XXX string constants. null if flash mode + * setting is not supported. + */ + public List<String> getSupportedIsoValues() { + String str = get(KEY_ISO_MODE + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** + * Gets the current LensShade Mode. + * + * @return LensShade Mode + */ + public String getLensShade() { + return get(KEY_LENSSHADE); + } + + /** + * Sets the current LensShade Mode. + * + * @return LensShade Mode + */ + public void setLensShade(String lensshade) { + set(KEY_LENSSHADE, lensshade); + } + + /** + * Gets the supported Lensshade modes. + * + * @return a List of LENS_MODE_XXX string constants. null if lens mode + * setting is not supported. + */ + public List<String> getSupportedLensShadeModes() { + String str = get(KEY_LENSSHADE + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** + * Gets the current auto exposure setting. + * + * @return one of AUTO_EXPOSURE_XXX string constant. null if auto exposure + * setting is not supported. + */ + public String getAutoExposure() { + return get(KEY_AUTO_EXPOSURE); + } + + /** + * Sets the current auto exposure setting. + * + * @param value AUTO_EXPOSURE_XXX string constants. + */ + public void setAutoExposure(String value) { + set(KEY_AUTO_EXPOSURE, value); + } + + /** + * Gets the supported auto exposure setting. + * + * @return a List of AUTO_EXPOSURE_XXX string constants. null if auto exposure + * setting is not supported. + */ + public List<String> getSupportedAutoexposure() { + String str = get(KEY_AUTO_EXPOSURE + SUPPORTED_VALUES_SUFFIX); + return split(str); + } + + /** + * Sets the smart-contrast feature + * @param boolean + */ + public void setSmartContrastEnabled(boolean enabled) { + set(KEY_SMART_CONTRAST, enabled ? "on" : "off"); + } + + /** + * Gets the value of smart-contrast + * + * @return if smart-contrast is enabled + */ + public boolean isSmartContrastEnabled() { + return "on".equals(get(KEY_SMART_CONTRAST)); + } + // Splits a comma delimited string to an ArrayList of String. // Return null if the passing string is null or the size is 0. private ArrayList<String> split(String str) { @@ -1986,6 +2317,9 @@ public class Camera { // Returns the value of a float parameter. private float getFloat(String key, float defaultValue) { + if (!mMap.containsKey(key)) { + return defaultValue; + } try { return Float.parseFloat(mMap.get(key)); } catch (NumberFormatException ex) { @@ -1995,6 +2329,9 @@ public class Camera { // Returns the value of a integer parameter. private int getInt(String key, int defaultValue) { + if (!mMap.containsKey(key)) { + return defaultValue; + } try { return Integer.parseInt(mMap.get(key)); } catch (NumberFormatException ex) { diff --git a/core/java/android/hardware/CameraSwitch.java b/core/java/android/hardware/CameraSwitch.java new file mode 100644 index 0000000..2475630 --- /dev/null +++ b/core/java/android/hardware/CameraSwitch.java @@ -0,0 +1,80 @@ +package android.hardware; + +import android.util.Log; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +/** + * Handle switching for HTC devices with dual cameras. + */ +public class CameraSwitch { + + public static final String SWITCH_CAMERA_MAIN = "main"; + + public static final String SWITCH_CAMERA_SECONDARY = "secondary"; + + private static final String SWITCH_CAMERA_FILE_PATH = "/sys/android_camera2/htcwc"; + + private static final String LOG_TAG = "CameraSwitch"; + + private static final boolean HAS_CAMERA_SWITCH; + + static { + final File file = new File(SWITCH_CAMERA_FILE_PATH); + HAS_CAMERA_SWITCH = file.exists(); + } + + private static boolean setHTCCameraSwitch(String cameraSwitch) { + if (!HAS_CAMERA_SWITCH) { + return false; + } + + final String node; + if (SWITCH_CAMERA_MAIN.equals(cameraSwitch)) { + node = "0"; + Log.d(LOG_TAG, "Open main camera"); + } else if (SWITCH_CAMERA_SECONDARY.equals(cameraSwitch)) { + node = "1"; + Log.d(LOG_TAG, "Open secondary camera"); + } else { + Log.e(LOG_TAG, "Unknown camera node: " + cameraSwitch + ", using main"); + node = "0"; + } + + final File file = new File(SWITCH_CAMERA_FILE_PATH); + BufferedWriter writer = null; + try { + writer = new BufferedWriter(new FileWriter(file)); + writer.write(node); + writer.flush(); + } catch (IOException e) { + Log.e(LOG_TAG, "Can't open " + SWITCH_CAMERA_FILE_PATH, e); + return false; + } finally { + try { + if (writer != null) { + writer.close(); + } + } catch (IOException e) { + Log.e(LOG_TAG, "Error closing " + SWITCH_CAMERA_FILE_PATH, e); + return false; + } + } + return true; + } + + public static void openCamera(String cameraNode) { + setHTCCameraSwitch(cameraNode); + } + + public static void openMainCamera() { + setHTCCameraSwitch(SWITCH_CAMERA_MAIN); + } + + public static boolean hasCameraSwitch() { + return HAS_CAMERA_SWITCH; + } +} diff --git a/core/java/android/hardware/HtcFrontFacingCamera.java b/core/java/android/hardware/HtcFrontFacingCamera.java new file mode 100644 index 0000000..5846579 --- /dev/null +++ b/core/java/android/hardware/HtcFrontFacingCamera.java @@ -0,0 +1,14 @@ +package android.hardware; + +/** + * For compatibility with apps written for the EVO 4G. + * + * @hide + */ +public class HtcFrontFacingCamera extends Camera { + + public static Camera getCamera() { + return open(CameraSwitch.SWITCH_CAMERA_SECONDARY); + } + +} diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 280ded6..daf86c8 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -200,7 +200,8 @@ public class ConnectivityManager private IConnectivityManager mService; static public boolean isNetworkTypeValid(int networkType) { - return networkType >= 0 && networkType <= MAX_NETWORK_TYPE; + // HACK! Accept TYPE_WIMAX even though we don't support it yet + return TYPE_WIMAX == networkType || (networkType >= 0 && networkType <= MAX_NETWORK_TYPE); } public void setNetworkPreference(int preference) { diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java index 649cb8c..cfb4b30 100644 --- a/core/java/android/net/NetworkInfo.java +++ b/core/java/android/net/NetworkInfo.java @@ -373,4 +373,13 @@ public class NetworkInfo implements Parcelable { return new NetworkInfo[size]; } }; + + /** + * HACK! Get an empty NetworkInfo object for WIMAX stub + * @hide + */ + public static final NetworkInfo getEmptyWimaxNetworkInfo() { + return new NetworkInfo(ConnectivityManager.TYPE_WIMAX, 0, "", ""); + } + } diff --git a/core/java/android/net/Proxy.java b/core/java/android/net/Proxy.java index 66eefb2..39d4ac1 100644 --- a/core/java/android/net/Proxy.java +++ b/core/java/android/net/Proxy.java @@ -16,12 +16,20 @@ package android.net; +import org.apache.harmony.luni.platform.INetworkSystem; +import org.apache.harmony.luni.platform.Platform; +import org.apache.http.HttpHost; + import android.content.ContentResolver; import android.content.Context; import android.os.SystemProperties; import android.provider.Settings; import android.util.Log; +import java.net.InetAddress; +import java.net.URI; +import java.net.UnknownHostException; + import junit.framework.Assert; /** @@ -36,6 +44,8 @@ final public class Proxy { static final public String PROXY_CHANGE_ACTION = "android.intent.action.PROXY_CHANGE"; + static final private INetworkSystem NETIMPL = Platform.getNetworkSystem(); + /** * Return the proxy host set by the user. * @param ctx A Context used to get the settings for the proxy host. @@ -120,4 +130,74 @@ final public class Proxy { } } + /** + * Returns the preferred proxy to be used by clients. This is a wrapper + * around {@link android.net.Proxy#getHost()}. Currently no proxy will + * be returned for localhost or if the active network is Wi-Fi. + * + * @param context the context which will be passed to + * {@link android.net.Proxy#getHost()} + * @param url the target URL for the request + * @note Calling this method requires permission + * android.permission.ACCESS_NETWORK_STATE + * @return The preferred proxy to be used by clients, or null if there + * is no proxy. + * + * {@hide} + */ + static final public HttpHost getPreferredHttpHost(Context context, + String url) { + if (!isLocalHost(url) && !isNetworkWifi(context)) { + final String proxyHost = Proxy.getHost(context); + if (proxyHost != null) { + return new HttpHost(proxyHost, Proxy.getPort(context), "http"); + } + } + + return null; + } + + static final private boolean isLocalHost(String url) { + if (url == null) { + return false; + } + + try { + final URI uri = URI.create(url); + final String host = uri.getHost(); + if (host != null) { + if (host.equalsIgnoreCase("localhost")) { + return true; + } + if (InetAddress.getByAddress(NETIMPL.ipStringToByteArray(host)) + .isLoopbackAddress()) { + return true; + } + } + } catch (UnknownHostException uex) { + // Ignore (INetworkSystem.ipStringToByteArray) + } catch (IllegalArgumentException iex) { + // Ignore (URI.create) + } + + return false; + } + + static final private boolean isNetworkWifi(Context context) { + if (context == null) { + return false; + } + + final ConnectivityManager connectivity = (ConnectivityManager) + context.getSystemService(Context.CONNECTIVITY_SERVICE); + if (connectivity != null) { + final NetworkInfo info = connectivity.getActiveNetworkInfo(); + if (info != null && + info.getType() == ConnectivityManager.TYPE_WIFI) { + return true; + } + } + + return false; + } }; diff --git a/core/java/android/net/http/Connection.java b/core/java/android/net/http/Connection.java index 43fb5f1..5e5affc 100644 --- a/core/java/android/net/http/Connection.java +++ b/core/java/android/net/http/Connection.java @@ -47,11 +47,16 @@ abstract class Connection { */ static final int SOCKET_TIMEOUT = 60000; + private static final int MAX_PRIORITY = 1000; + private static final int SEND = 0; private static final int READ = 1; private static final int DRAIN = 2; private static final int DONE = 3; - private static final String[] states = {"SEND", "READ", "DRAIN", "DONE"}; + private static final int PRIO = 4; + private static final String[] states = {"SEND", "READ", "DRAIN", "DONE", "PRIO"}; + + private ConnectionThread mConnectionThread; Context mContext; @@ -110,6 +115,7 @@ abstract class Connection { mCanPersist = false; mHttpContext = new BasicHttpContext(null); + mConnectionThread = null; } HttpHost getHost() { @@ -140,6 +146,10 @@ abstract class Connection { return mCertificate; } + void setConnectionThread(ConnectionThread thread) { + mConnectionThread = thread; + } + /** * Close current network connection * Note: this runs in non-network thread @@ -157,6 +167,7 @@ abstract class Connection { */ void processRequests(Request firstRequest) { Request req = null; + Request peek = null; boolean empty; int error = EventHandler.OK; Exception exception = null; @@ -197,6 +208,36 @@ abstract class Connection { state = DRAIN; break; } + + // ### synchronize on mRequestFeeder instead of requeing newreq? + + if (req.mPriority == -1 || + req.mPriority > MAX_PRIORITY) { + /* + || pipe.size() + 1 == maxPipe + || req.mPriority == -1) { + */ + peek = mRequestFeeder.peekRequest(); + if (peek != null) { + int ppri = peek.mPriority; + if ((req.mPriority == -1 && ppri >= 0) + || (req.mPriority >= 0 && ppri < req.mPriority)) { + Request newreq = mRequestFeeder.getRequest(); + if (newreq != null) { + if (!newreq.equals(peek) + || peek.mPriority != ppri) { + mRequestFeeder.requeueRequest(newreq, false, true); + } else { + mConnectionThread.setNewRequest(newreq); + state = PRIO; + mRequestFeeder.requeueRequest(req, false, true); + break; + } + } + } + } + } + req.setConnection(this); /* Don't work on cancelled requests. */ @@ -251,27 +292,34 @@ abstract class Connection { pipe.addLast(req); } exception = null; - state = clearPipe(pipe) ? DONE : SEND; + if (clearPipe(pipe)) + state = DONE; + else if (state != PRIO) + state = SEND; minPipe = maxPipe = 1; break; } pipe.addLast(req); - if (!mCanPersist) state = READ; + if (!mCanPersist && state != PRIO) state = READ; break; } + case PRIO: case DRAIN: case READ: { empty = !mRequestFeeder.haveRequest(mHost); int pipeSize = pipe.size(); - if (state != DRAIN && pipeSize < minPipe && + if (state != PRIO && state != DRAIN && pipeSize < minPipe && !empty && mCanPersist) { state = SEND; break; } else if (pipeSize == 0) { /* Done if no other work to do */ - state = empty ? DONE : SEND; + if (state == PRIO) + state = DONE; + else + state = empty ? DONE : SEND; break; } @@ -312,7 +360,8 @@ abstract class Connection { mHttpContext.removeAttribute(HTTP_CONNECTION); clearPipe(pipe); minPipe = maxPipe = 1; - state = SEND; + if (state != PRIO) + state = SEND; } break; } @@ -335,7 +384,7 @@ abstract class Connection { tReq = (Request)pipe.removeLast(); if (HttpLog.LOGV) HttpLog.v( "clearPipe() adding back " + mHost + " " + tReq); - mRequestFeeder.requeueRequest(tReq); + mRequestFeeder.requeueRequest(tReq, true, false); empty = false; } if (empty) empty = !mRequestFeeder.haveRequest(mHost); @@ -406,7 +455,7 @@ abstract class Connection { } else { if (req.mFailCount < RETRY_REQUEST_LIMIT) { // requeue - mRequestFeeder.requeueRequest(req); + mRequestFeeder.requeueRequest(req, true, false); req.mFailCount++; } else { httpFailure(req, error, exception); diff --git a/core/java/android/net/http/ConnectionThread.java b/core/java/android/net/http/ConnectionThread.java index 32191d2..9b0363e 100644 --- a/core/java/android/net/http/ConnectionThread.java +++ b/core/java/android/net/http/ConnectionThread.java @@ -40,6 +40,8 @@ class ConnectionThread extends Thread { private Context mContext; private RequestQueue.ConnectionManager mConnectionManager; private RequestFeeder mRequestFeeder; + private volatile HttpHost mCurrentHost; + private volatile Request mNewRequest; private int mId; Connection mConnection; @@ -54,15 +56,25 @@ class ConnectionThread extends Thread { mId = id; mConnectionManager = connectionManager; mRequestFeeder = requestFeeder; + mCurrentHost = null; + mNewRequest = null; } - void requestStop() { + public void requestStop() { synchronized (mRequestFeeder) { mRunning = false; mRequestFeeder.notify(); } } + public HttpHost getCurrentHost() { + return mCurrentHost; + } + + public void setNewRequest(Request req) { + mNewRequest = req; + } + /** * Loop until app shutdown. Runs connections in priority * order. @@ -87,7 +99,11 @@ class ConnectionThread extends Thread { Request request; /* Get a request to process */ - request = mRequestFeeder.getRequest(); + if (mNewRequest != null) { + request = mNewRequest; + mNewRequest = null; + } else + request = mRequestFeeder.getRequest(); /* wait for work */ if (request == null) { @@ -103,13 +119,18 @@ class ConnectionThread extends Thread { mCurrentThreadTime = SystemClock .currentThreadTimeMillis(); } + // Make sure the connection does not start to drain before the first request has been processed } } else { if (HttpLog.LOGV) HttpLog.v("ConnectionThread: new request " + request.mHost + " " + request ); - mConnection = mConnectionManager.getConnection(mContext, - request.mHost); + // ### this should possibly have some kind of lock to prevent the requestqueue from seeing the host as busy when it's not + mCurrentHost = request.mHost; + synchronized (this) { + mConnection = mConnectionManager.getConnection(mContext, request.mHost); + mConnection.setConnectionThread(this); + } mConnection.processRequests(request); if (mConnection.getCanPersist()) { if (!mConnectionManager.recycleConnection(mConnection)) { @@ -118,7 +139,12 @@ class ConnectionThread extends Thread { } else { mConnection.closeConnection(); } - mConnection = null; + synchronized (this) { + mConnection.setConnectionThread(null); + mConnection = null; + } + + mCurrentHost = null; if (mCurrentThreadTime > 0) { long start = mCurrentThreadTime; diff --git a/core/java/android/net/http/Headers.java b/core/java/android/net/http/Headers.java index 09f6f4f..74c0de8 100644 --- a/core/java/android/net/http/Headers.java +++ b/core/java/android/net/http/Headers.java @@ -262,7 +262,14 @@ public final class Headers { break; case HASH_CACHE_CONTROL: if (name.equals(CACHE_CONTROL)) { - mHeaders[IDX_CACHE_CONTROL] = val; + // In case where we receive more than one header, create a ',' separated list. + // This should be ok, according to RFC 2616 chapter 4.2 + if (mHeaders[IDX_CACHE_CONTROL] != null && + mHeaders[IDX_CACHE_CONTROL].length() > 0) { + mHeaders[IDX_CACHE_CONTROL] += (',' + val); + } else { + mHeaders[IDX_CACHE_CONTROL] = val; + } } break; case HASH_LAST_MODIFIED: diff --git a/core/java/android/net/http/Request.java b/core/java/android/net/http/Request.java index 8c0d503..0cca90b 100644 --- a/core/java/android/net/http/Request.java +++ b/core/java/android/net/http/Request.java @@ -71,6 +71,9 @@ class Request { /** True if request has been cancelled */ volatile boolean mCancelled = false; + /** Priority */ + volatile int mPriority = -1; + int mFailCount = 0; // This will be used to set the Range field if we retry a connection. This @@ -111,13 +114,15 @@ class Request { Request(String method, HttpHost host, HttpHost proxyHost, String path, InputStream bodyProvider, int bodyLength, EventHandler eventHandler, - Map<String, String> headers) { + Map<String, String> headers, + int pri) { mEventHandler = eventHandler; mHost = host; mProxyHost = proxyHost; mPath = path; mBodyProvider = bodyProvider; mBodyLength = bodyLength; + mPriority = pri; if (bodyProvider == null && !"POST".equalsIgnoreCase(method)) { mHttpRequest = new BasicHttpRequest(method, getUri()); diff --git a/core/java/android/net/http/RequestFeeder.java b/core/java/android/net/http/RequestFeeder.java index 34ca267..739db27 100644 --- a/core/java/android/net/http/RequestFeeder.java +++ b/core/java/android/net/http/RequestFeeder.java @@ -29,6 +29,7 @@ interface RequestFeeder { Request getRequest(); Request getRequest(HttpHost host); + Request peekRequest(); /** * @return true if a request for this host is available @@ -39,4 +40,5 @@ interface RequestFeeder { * Put request back on head of queue */ void requeueRequest(Request request); + void requeueRequest(Request request, boolean commit, boolean notif); } diff --git a/core/java/android/net/http/RequestHandle.java b/core/java/android/net/http/RequestHandle.java index 103fd94..07533ca 100644 --- a/core/java/android/net/http/RequestHandle.java +++ b/core/java/android/net/http/RequestHandle.java @@ -456,6 +456,6 @@ public class RequestHandle { mRequest = mRequestQueue.queueRequest( mUrl, mUri, mMethod, mHeaders, mRequest.mEventHandler, mBodyProvider, - mBodyLength).mRequest; + mBodyLength, -1, false).mRequest; } } diff --git a/core/java/android/net/http/RequestQueue.java b/core/java/android/net/http/RequestQueue.java index a31639f..50ea7f5 100644 --- a/core/java/android/net/http/RequestQueue.java +++ b/core/java/android/net/http/RequestQueue.java @@ -36,6 +36,9 @@ import android.text.TextUtils; import android.util.Log; import java.io.InputStream; +import java.util.Comparator; +import java.util.Collections; +import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; @@ -57,6 +60,7 @@ public class RequestQueue implements RequestFeeder { private final Context mContext; private final ActivePool mActivePool; private final ConnectivityManager mConnectivityManager; + private final HashSet<HttpHost> mPriorities; private HttpHost mProxyHost = null; private BroadcastReceiver mProxyChangeReceiver; @@ -213,6 +217,7 @@ public class RequestQueue implements RequestFeeder { mContext = context; mPending = new LinkedHashMap<HttpHost, LinkedList<Request>>(32); + mPriorities = new HashSet<HttpHost>(); mActivePool = new ActivePool(connectionCount); mActivePool.startup(); @@ -221,6 +226,58 @@ public class RequestQueue implements RequestFeeder { context.getSystemService(Context.CONNECTIVITY_SERVICE); } + public synchronized boolean setRequestPriority(WebAddress uri, int priority) { + // ### this lookup won't work if a proxy is being used + HttpHost host = new HttpHost(uri.mHost, uri.mPort, uri.mScheme); + if (mPending.containsKey(host)) { + LinkedList<Request> reqList = mPending.get(host); + // ### O(n) lookup, certainly not ideal + ListIterator iter = reqList.listIterator(0); + while (iter.hasNext()) { + Request request = (Request)iter.next(); + if (request.mPath.equals(uri.mPath)) { + request.mPriority = priority; + mPriorities.add(host); + return true; + } + } + } + return false; + } + + private void commitPrioritiesForList(LinkedList<Request> reqList) { + Collections.sort(reqList, new Comparator<Object>() { + public int compare(Object o1, Object o2) { + int r1 = ((Request)o1).mPriority; + int r2 = ((Request)o2).mPriority; + + if (r1 == r2) + return 0; + else if (r1 == -1) + return 1; + else if (r2 == -1) + return -1; + else if (r1 < r2) + return -1; + return 1; + } + }); + } + + public synchronized void commitRequestPriorities() { + if (mPriorities.isEmpty()) + return; + Iterator iter = mPriorities.iterator(); + while (iter.hasNext()) { + HttpHost host = (HttpHost)iter.next(); + if (mPending.containsKey(host)) { + LinkedList<Request> reqList = mPending.get(host); + commitPrioritiesForList(reqList); + } + } + mPriorities.clear(); + } + /** * Enables data state and proxy tracking */ @@ -297,9 +354,17 @@ public class RequestQueue implements RequestFeeder { String url, String method, Map<String, String> headers, EventHandler eventHandler, InputStream bodyProvider, int bodyLength) { + return queueRequest(url, method, headers, eventHandler, + bodyProvider, bodyLength, -1, false); + } + + public RequestHandle queueRequest( + String url, String method, + Map<String, String> headers, EventHandler eventHandler, + InputStream bodyProvider, int bodyLength, int pri, boolean commit) { WebAddress uri = new WebAddress(url); return queueRequest(url, uri, method, headers, eventHandler, - bodyProvider, bodyLength); + bodyProvider, bodyLength, pri, commit); } /** @@ -316,7 +381,7 @@ public class RequestQueue implements RequestFeeder { public RequestHandle queueRequest( String url, WebAddress uri, String method, Map<String, String> headers, EventHandler eventHandler, - InputStream bodyProvider, int bodyLength) { + InputStream bodyProvider, int bodyLength, int pri, boolean commit) { if (HttpLog.LOGV) HttpLog.v("RequestQueue.queueRequest " + uri); @@ -331,9 +396,9 @@ public class RequestQueue implements RequestFeeder { // set up request req = new Request(method, httpHost, mProxyHost, uri.mPath, bodyProvider, - bodyLength, eventHandler, headers); + bodyLength, eventHandler, headers, pri); - queueRequest(req, false); + queueRequest(req, false, commit); mActivePool.mTotalRequest++; @@ -359,12 +424,18 @@ public class RequestQueue implements RequestFeeder { public Request getRequest(HttpHost host) { return getRequest(); } + public Request peekRequest() { + return mRequest; + } public boolean haveRequest(HttpHost host) { return mRequest != null; } public void requeueRequest(Request r) { mRequest = r; } + public void requeueRequest(Request r, boolean commit, boolean notif) { + requeueRequest(r); + } } public RequestHandle queueSynchronousRequest(String url, WebAddress uri, @@ -378,7 +449,7 @@ public class RequestQueue implements RequestFeeder { HttpHost host = new HttpHost(uri.mHost, uri.mPort, uri.mScheme); Request req = new Request(method, host, mProxyHost, uri.mPath, - bodyProvider, bodyLength, eventHandler, headers); + bodyProvider, bodyLength, eventHandler, headers, 0); // Open a new connection that uses our special RequestFeeder // implementation. @@ -444,16 +515,55 @@ public class RequestQueue implements RequestFeeder { HttpLog.v(dump.toString()); } + private Map.Entry<HttpHost, LinkedList<Request>> priorityList() { + int curPri = -1; + int entryPri; + Map.Entry<HttpHost, LinkedList<Request>> ret = null; + if (!mPending.isEmpty()) { + Iterator<Map.Entry<HttpHost, LinkedList<Request>>> iter = mPending.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry<HttpHost, LinkedList<Request>> entry = iter.next(); + if (ret == null) { + ret = entry; + } + entryPri = entry.getValue().getFirst().mPriority; + if (entryPri != -1 && (curPri == -1 || curPri > entryPri)) { + ret = entry; + curPri = entryPri; + } + } + } + return ret; + } + + public synchronized Request peekRequest() { + Request ret = null; + + Map.Entry<HttpHost, LinkedList<Request>> entry = priorityList(); + if (entry != null && !entry.getValue().isEmpty()) { + if (entry.getValue().getFirst().mPriority != -1) + ret = entry.getValue().getFirst(); + } + + return ret; + } + /* * RequestFeeder implementation */ public synchronized Request getRequest() { Request ret = null; - if (!mPending.isEmpty()) { - ret = removeFirst(mPending); + Map.Entry<HttpHost, LinkedList<Request>> entry = priorityList(); + if (entry != null) { + LinkedList<Request> reqList = entry.getValue(); + ret = reqList.removeFirst(); + if (reqList.isEmpty()) { + mPending.remove(entry.getKey()); + } } if (HttpLog.LOGV) HttpLog.v("RequestQueue.getRequest() => " + ret); + return ret; } @@ -485,7 +595,13 @@ public class RequestQueue implements RequestFeeder { * Put request back on head of queue */ public void requeueRequest(Request request) { - queueRequest(request, true); + requeueRequest(request, true, true); + } + + public void requeueRequest(Request request, boolean commit, boolean notif) { + queueRequest(request, true, commit); + if (notif) + mActivePool.startConnectionThread(); } /** @@ -495,7 +611,7 @@ public class RequestQueue implements RequestFeeder { mActivePool.shutdown(); } - protected synchronized void queueRequest(Request request, boolean head) { + protected synchronized void queueRequest(Request request, boolean head, boolean commit) { HttpHost host = request.mProxyHost == null ? request.mHost : request.mProxyHost; LinkedList<Request> reqList; if (mPending.containsKey(host)) { @@ -509,6 +625,11 @@ public class RequestQueue implements RequestFeeder { } else { reqList.add(request); } + if (commit && request.mPriority != -1) { + commitPrioritiesForList(reqList); + } else if (!commit && request.mPriority != -1) { + mPriorities.add(host); + } } diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index 2e14667..86f9a6b 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -94,7 +94,8 @@ public final class Debug /** * Default trace file path and file */ - private static final String DEFAULT_TRACE_PATH_PREFIX = "/sdcard/"; + private static final String DEFAULT_TRACE_PATH_PREFIX = + Environment.getExternalStorageDirectory().getPath() + "/"; private static final String DEFAULT_TRACE_BODY = "dmtrace"; private static final String DEFAULT_TRACE_EXTENSION = ".trace"; private static final String DEFAULT_TRACE_FILE_PATH = @@ -127,7 +128,7 @@ public final class Debug public int otherPrivateDirty; /** The shared dirty pages used by everything else. */ public int otherSharedDirty; - + public MemoryInfo() { } @@ -137,21 +138,21 @@ public final class Debug public int getTotalPss() { return dalvikPss + nativePss + otherPss; } - + /** * Return total private dirty memory usage in kB. */ public int getTotalPrivateDirty() { return dalvikPrivateDirty + nativePrivateDirty + otherPrivateDirty; } - + /** * Return total shared dirty memory usage in kB. */ public int getTotalSharedDirty() { return dalvikSharedDirty + nativeSharedDirty + otherSharedDirty; } - + public int describeContents() { return 0; } @@ -179,7 +180,7 @@ public final class Debug otherPrivateDirty = source.readInt(); otherSharedDirty = source.readInt(); } - + public static final Creator<MemoryInfo> CREATOR = new Creator<MemoryInfo>() { public MemoryInfo createFromParcel(Parcel source) { return new MemoryInfo(source); @@ -460,7 +461,7 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo * Like startMethodTracing(String, int, int), but taking an already-opened * FileDescriptor in which the trace is written. The file name is also * supplied simply for logging. Makes a dup of the file descriptor. - * + * * Not exposed in the SDK unless we are really comfortable with supporting * this and find it would be useful. * @hide @@ -1070,7 +1071,7 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo * static { * // Sets all the fields * Debug.setFieldsOn(MyDebugVars.class); - * + * * // Sets only the fields annotated with @Debug.DebugProperty * // Debug.setFieldsOn(MyDebugVars.class, true); * } diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index 01cc408..90ef1cb 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -39,4 +39,11 @@ interface IPowerManager // sets the brightness of the backlights (screen, keyboard, button) 0-255 void setBacklightBrightness(int brightness); void setAttentionLight(boolean on, int color); + + // custom backlight things + int getLightSensorValue(); + int getRawLightSensorValue(); + int getLightSensorScreenBrightness(); + int getLightSensorButtonBrightness(); + int getLightSensorKeyboardBrightness(); } diff --git a/core/java/android/preference/ListPreference.java b/core/java/android/preference/ListPreference.java index f842d75..f44cbe4 100644 --- a/core/java/android/preference/ListPreference.java +++ b/core/java/android/preference/ListPreference.java @@ -39,6 +39,7 @@ public class ListPreference extends DialogPreference { private CharSequence[] mEntries; private CharSequence[] mEntryValues; private String mValue; + private String mSummary; private int mClickedDialogEntryIndex; public ListPreference(Context context, AttributeSet attrs) { @@ -49,8 +50,16 @@ public class ListPreference extends DialogPreference { mEntries = a.getTextArray(com.android.internal.R.styleable.ListPreference_entries); mEntryValues = a.getTextArray(com.android.internal.R.styleable.ListPreference_entryValues); a.recycle(); + + /* Retrieve the Preference summary attribute since it's private + * in the Preference class. + */ + a = context.obtainStyledAttributes(attrs, + com.android.internal.R.styleable.Preference, 0, 0); + mSummary = a.getString(com.android.internal.R.styleable.Preference_summary); + a.recycle(); } - + public ListPreference(Context context) { this(context, null); } @@ -127,6 +136,43 @@ public class ListPreference extends DialogPreference { } /** + * Returns the summary of this ListPreference. If the summary + * has a {@linkplain java.lang.String#format String formatting} + * marker in it (i.e. "%s" or "%1$s"), then the current entry + * value will be substituted in its place. + * + * @return the summary with appropriate string substitution + */ + @Override + public CharSequence getSummary() { + final CharSequence entry = getEntry(); + if (mSummary == null || entry == null) { + return super.getSummary(); + } else { + return String.format(mSummary, entry); + } + } + + /** + * Sets the summary for this Preference with a CharSequence. + * If the summary has a + * {@linkplain java.lang.String#format String formatting} + * marker in it (i.e. "%s" or "%1$s"), then the current entry + * value will be substituted in its place when it's retrieved. + * + * @param summary The summary for the preference. + */ + @Override + public void setSummary(CharSequence summary) { + super.setSummary(summary); + if (summary == null && mSummary != null) { + mSummary = null; + } else if (summary != null && !summary.equals(mSummary)) { + mSummary = summary.toString(); + } + } + + /** * Sets the value to the given index from the entry values. * * @param index The index of the value to set. diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java index 2fba1d7..9c3a725 100644 --- a/core/java/android/provider/Browser.java +++ b/core/java/android/provider/Browser.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2006 The Android Open Source Project + * Copyright (c) 2010, Code Aurora Forum. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -342,6 +343,34 @@ public class Browser { } /** + * Returns top num of visited URLs in the history. + * Requires {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS} + * @param cr The ContentResolver used to access the database. + * @hide pending API council approval + */ + public static final String[] getVisitedHistoryByOrder(ContentResolver cr, String order, int num) { + try { + String[] projection = new String[] { + "url" + }; + Cursor c = cr.query(BOOKMARKS_URI, projection, "visits > 0", null, + order); + + int count = (c.getCount() > num)? num:c.getCount(); + String[] str = new String[count]; + int i = 0; + while ((i<count) && c.moveToNext()) { + str[i] = c.getString(0); + i++; + } + c.deactivate(); + return str; + } catch (IllegalStateException e) { + return new String[0]; + } + } + + /** * If there are more than MAX_HISTORY_COUNT non-bookmark history * items in the bookmark/history table, delete TRUNCATE_N_OLDEST * of them. This is used to keep our history table to a diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index e12dfb0..9cd0d3a 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1271,6 +1271,136 @@ public final class Settings { public static final int SCREEN_BRIGHTNESS_MODE_AUTOMATIC = 1; /** + * Indicates that custom light sensor settings has changed. + * The value is random and changes reloads light settings. + * + * @hide + */ + public static final String LIGHTS_CHANGED = "lights_changed"; + + /** + * Whether custom light sensor levels & values are enabled. The value is + * boolean (1 or 0). + * + * @hide + */ + public static final String LIGHT_SENSOR_CUSTOM = "light_sensor_custom"; + + /** + * Screen dim value to use if LIGHT_SENSOR_CUSTOM is set. The value is int. + * Default is android.os.BRIGHTNESS_DIM. + * + * @hide + */ + public static final String LIGHT_SCREEN_DIM = "light_screen_dim"; + + /** + * Custom light sensor levels. The value is a comma separated int array + * with length N. + * Example: "100,300,3000". + * + * @hide + */ + public static final String LIGHT_SENSOR_LEVELS = "light_sensor_levels"; + + /** + * Custom light sensor lcd values. The value is a comma separated int array + * with length N+1. + * Example: "10,50,100,255". + * + * @hide + */ + public static final String LIGHT_SENSOR_LCD_VALUES = "light_sensor_lcd_values"; + + /** + * Custom light sensor lcd values. The value is a comma separated int array + * with length N+1. + * Example: "10,50,100,255". + * + * @hide + */ + public static final String LIGHT_SENSOR_BUTTON_VALUES = "light_sensor_button_values"; + + /** + * Custom light sensor lcd values. The value is a comma separated int array + * with length N+1. + * Example: "10,50,100,255". + * + * @hide + */ + public static final String LIGHT_SENSOR_KEYBOARD_VALUES = "light_sensor_keyboard_values"; + + /** + * Whether light sensor is allowed to decrease when calculating automatic + * backlight. The value is boolean (1 or 0). + * + * @hide + */ + public static final String LIGHT_DECREASE = "light_decrease"; + + /** + * Light sensor hysteresis for decreasing backlight. The value is + * int (0-99) representing % (0-0.99 as float). Example: + * + * Levels Output + * 0 - 100 50 + * 100 - 200 100 + * 200 - Inf 255 + * + * Current sensor value is 150 which gives light value 100. Hysteresis is 50. + * Current level lower bound is 100 and previous lower bound is 0. + * Sensor value must drop below 100-(100-0)*(50/100)=50 for output to become 50 + * (corresponding to the 0 - 100 level). + * @hide + */ + public static final String LIGHT_HYSTERESIS = "light_hysteresis"; + + /** + * Whether light sensor used when calculating automatic backlight should + * be filtered through an moving average filter. + * The value is boolean (1 or 0). + * + * @hide + */ + public static final String LIGHT_FILTER = "light_filter"; + + /** + * Window length of filter used when calculating automatic backlight. + * One minute means that the average sensor value last minute is used. + * The value is integer (milliseconds) + * + * @hide + */ + public static final String LIGHT_FILTER_WINDOW = "light_filter_window"; + + /** + * Reset threshold of filter used when calculating automatic backlight. + * Sudden large jumps in sensor value resets the filter. This is used + * to make the filter respond quickly to large enough changes in input + * while still filtering small changes. Example: + * + * Current filter value (average) is 100 and sensor value is changing to + * 10, 150, 100, 30, 50. The filter is continously taking the average of + * the samples. Now the user goes outside and the value jumps over 1000. + * The difference between current average and new sample is larger than + * the reset threshold and filter is reset. It begins calculating a new + * average on samples around 1000 (say, 800, 1200, 1000, 1100 etc.) + * + * The value is integer (lux) + * + * @hide + */ + public static final String LIGHT_FILTER_RESET = "light_filter_reset"; + + /** + * Sample interval of filter used when calculating automatic backlight. + * The value is integer (milliseconds) + * + * @hide + */ + public static final String LIGHT_FILTER_INTERVAL = "light_filter_interval"; + + /** * Control whether the process CPU usage meter should be shown. */ public static final String SHOW_PROCESSES = "show_processes"; @@ -1526,14 +1656,214 @@ public final class Settings { public static final String FANCY_IME_ANIMATIONS = "fancy_ime_animations"; /** + * Whether WebViews reflow content when zooming in by pinching. The value is + * boolean (1 or 0). + * @hide + */ + public static final String WEB_VIEW_PINCH_REFLOW = "web_view_pinch_reflow"; + + /** * Control whether the accelerometer will be used to change screen * orientation. If 0, it will not be used unless explicitly requested * by the application; if 1, it will be used by default unless explicitly * disabled by the application. */ public static final String ACCELEROMETER_ROTATION = "accelerometer_rotation"; + + /** + * Control the type of rotation which can be performed using the accelerometer + * if ACCELEROMETER_ROTATION is enabled. + * Value is a bitwise combination of + * 1 = 90 degrees (left) + * 2 = 180 degrees (inverted) + * 4 = 270 degrees (right) + * Normal portrait (0 degrees) is always enabled + * Default is 5 (90 & 270 degrees) like stock Froyo + * @hide + */ + public static final String ACCELEROMETER_ROTATION_MODE = "accelerometer_rotation_mode"; + + /** + * Specifies the number of recent apps to show (8, 12, 16) + * @hide + */ + public static final String RECENT_APPS_NUMBER = "recent_apps_number"; + + /** + * Specifies the number of recent apps to show (8, 12, 16) + * @hide + */ + public static final String RECENT_APPS_SHOW_TITLE = "recent_apps_show_title"; + + /** + * Specifies whether or not to use a custom app instead of the recent applications dialog + * @hide + */ + public static final String USE_CUSTOM_APP = "use_custom_app"; + + /** + * Stores the uri of the custom application to use + * @hide + */ + public static final String SELECTED_CUSTOM_APP = "selected_custom_app"; + + /** + * Toggles whether to display the PLMN field on the Lockscreen + * @hide + */ + public static final String SHOW_PLMN_LS = "show_plmn_ls"; + + /** + * Toggles whether to display the SPN field on the Lockscreen + * @hide + */ + public static final String SHOW_SPN_LS = "show_spn_ls"; + + /** + * Toggles whether to display the PLMN field on the Notification bar + * @hide + */ + public static final String SHOW_PLMN_SB = "show_plmn_sb"; + + /** + * Toggles whether to display the SPN field on the Notification bar + * @hide + */ + public static final String SHOW_SPN_SB = "show_spn_sb"; + + /** + * Specifies whether to show or hide clock + * @hide + */ + public static final String SHOW_STATUS_CLOCK = "show_status_clock"; + + /** + * Specifies the clock color + * @hide + */ + public static final String CLOCK_COLOR = "clock_color"; + + /** + * Specifies whether to show or hide the dbm signal level + * @hide + */ + public static final String SHOW_STATUS_DBM = "show_status_dbm"; + + /** + * Specifies the dbm signal level color + * @hide + */ + public static final String DBM_COLOR = "dbm_color"; + + /** + * Specifies whether to prompt on the power dialog + * @hide + */ + public static final String POWER_DIALOG_PROMPT = "power_dialog_prompt"; + + /** + * Specifies notification count color + * @hide + */ + public static final String NOTIF_COUNT_COLOR = "notifications_count_color"; + + /** + * Specifies the date color + * @hide + */ + public static final String DATE_COLOR = "date_color"; + + /** + * Specifies new notification ticker color + * @hide + */ + public static final String NEW_NOTIF_TICKER_COLOR = "new_notifications_ticker_color"; + + /** + * Specifies no notifications color + * @hide + */ + public static final String NO_NOTIF_COLOR = "no_notifications_color"; + + /** + * Specifies latest nofitication color + * @hide + */ + public static final String LATEST_NOTIF_COLOR = "latest_notifications_color"; + + /** + * Specifies ongoing notification color + * @hide + */ + public static final String ONGOING_NOTIF_COLOR = "ongoing_notifications_color"; + + /** + * Specifies spn label color + * @hide + */ + public static final String SPN_LABEL_COLOR = "spn_label_color"; + + /** + * Specifies plmn color + * @hide + */ + public static final String PLMN_LABEL_COLOR = "plmn_label_color"; + + /** + * Specifies clear button color + * @hide + */ + public static final String CLEAR_BUTTON_LABEL_COLOR = "clear_button_label_color"; + + /** + * Specifies notification item title color. + * @hide + */ + public static final String NOTIF_ITEM_TITLE_COLOR = "notifications_title_color"; + + /** + * Specifies notification item text color. + * @hide + */ + public static final String NOTIF_ITEM_TEXT_COLOR = "notifications_text_color"; + + /** + * Specifies notification item time color. + * @hide + */ + public static final String NOTIF_ITEM_TIME_COLOR = "notifications_time_color"; /** + * Whether to show the battery level percentage overlayed on the icon. + * @hide + */ + public static final String BATTERY_PERCENTAGE_STATUS_ICON = "battery_percentage_status_icon"; + + /** + * Specifies battery percentage status color + * @hide + */ + public static final String BATTERY_PERCENTAGE_STATUS_COLOR = "battery_status_color_title"; + + /** + * Specifies whether to show AM/PM indicators for 12-hour clock + * @hide + */ + public static final String SHOW_TWELVE_HOUR_CLOCK_PERIOD = "show_clock_period"; + + /** + * How many ms to delay before enabling the screen lock when the screen goes off due to timeout + * @hide + */ + public static final String SCREEN_LOCK_TIMEOUT_DELAY = "screen_lock_timeout_delay"; + + /** + * How many ms to delay before enabling the screen lock when the screen is turned off by the user + * @hide + */ + public static final String SCREEN_LOCK_SCREENOFF_DELAY = "screen_lock_screenoff_delay"; + + /** * Whether the audible DTMF tones are played by the dialer when dialing. The value is * boolean (1 or 0). */ @@ -1596,6 +1926,72 @@ public final class Settings { public static final String HAPTIC_FEEDBACK_ENABLED = "haptic_feedback_enabled"; /** + * Whether haptic feedback is enabled on virtual key release (as opposed to pressed) + * boolean (1 or 0). + * @hide + */ + public static final String HAPTIC_FEEDBACK_UP_ENABLED = "haptic_feedback_up_enabled"; + + /** + * Whether haptic is also activated for all screen interaction (follows Sound Effects Enabled behaviour) + * boolean (1 or 0). + * @hide + */ + public static final String HAPTIC_FEEDBACK_ALL_ENABLED = "haptic_feedback_all_enabled"; + + /** + * Value for haptic down (string will be converted to array - + * format is ***"delay in msec before turning on"_"delay in msec before turning off__**repeat**) + * @hide + */ + public static final String HAPTIC_DOWN_ARRAY = "haptic_down_array"; + + /** + * Value for haptic up - same format as _DOWN_ARRAY + * @hide + */ + public static final String HAPTIC_UP_ARRAY = "haptic_up_array"; + + /** + * Value for long presses - same format as _DOWN_ARRAY + * @hide + */ + public static final String HAPTIC_LONG_ARRAY = "haptic_long_array"; + + /** + * these store the ORIGINAL default haptic values from config.xml + * this is so HapticAdjust can easily pull them when resetting defaults + * these are created and acted on in PhoneWindowManager + * @hide + */ + public static final String HAPTIC_DOWN_ARRAY_DEFAULT = "haptic_down_array_default"; + + /** + * Same as HAPTIC_DOWN_ARRAY_DEFAULT but for key releases + * @hide + */ + public static final String HAPTIC_UP_ARRAY_DEFAULT = "haptic_up_array_default"; + + /** + * Same as HAPTIC_DOWN_ARRAY_DEFAULT but for key releases + * @hide + */ + public static final String HAPTIC_LONG_ARRAY_DEFAULT = "haptic_long_array_default"; + + /** + * Set values for haptic feedback from typing on keypad (new for Froyo) + * @hide + */ + public static final String HAPTIC_TAP_ARRAY = "haptic_tap_array"; + + /** + * Default values for haptic feedback from typing on keypad (new for Froyo) - pulled + * from config.xml + * @hide + */ + public static final String HAPTIC_TAP_ARRAY_DEFAULT = "haptic_tap_array_default"; + + /** * Whether live web suggestions while the user types into search dialogs are * enabled. Browsers and other search UIs should respect this, as it allows * a user to avoid sending partial queries to a search engine, if it poses @@ -1679,6 +2075,114 @@ public final class Settings { public static final String UNLOCK_SOUND = "unlock_sound"; /** + * Whether to wake the screen with the trackball. The value is boolean (1 or 0). + * @hide + */ + public static final String TRACKBALL_WAKE_SCREEN = "trackball_wake_screen"; + + /** + * Whether to unlock the screen with the trackball. The value is boolean (1 or 0). + * @hide + */ + public static final String TRACKBALL_UNLOCK_SCREEN = "trackball_unlock_screen"; + + /** + * Pulse the Trackball with Screen On. The value is boolean (1 or 0). + * @hide + */ + public static final String TRACKBALL_SCREEN_ON = "trackball_screen_on"; + + /** + * Pulse notifications in Succession. The value is boolean (1 or 0). + * @hide + */ + public static final String TRACKBALL_NOTIFICATION_SUCCESSION = "trackball_sucession"; + + /** + * Pulse notifications in Succession. The value is boolean (1 or 0). + * @hide + */ + public static final String TRACKBALL_NOTIFICATION_RANDOM = "trackball_random_colors"; + + /** + * Pulse notifications in Succession. The value is boolean (1 or 0). + * @hide + */ + public static final String TRACKBALL_NOTIFICATION_PULSE_ORDER = "trackball_pulse_in_order"; + + /** + * Beldn Notification Colors. The value is boolean (1 or 0). + * @hide + */ + public static final String TRACKBALL_NOTIFICATION_BLEND_COLOR = "trackball_blend_color"; + + /** + * Trackball Notification Colors. The value is String pkg=color|pkg=color + * @hide + */ + public static final String NOTIFICATION_PACKAGE_COLORS = "|"; + + /** + * Trackball Notification List. The value is String pkg|pkg + * @hide + */ + public static final String NOTIFICATION_PACKAGE_LIST = "|"; + + /** + * Trackball Notification Colors Debugging. The value is boolean (1 or 0) + * @hide + */ + public static final String NOTIFICATION_PACKAGE_COLORS_GET_PACK = "0"; + + /** + * Whether to unlock the menu key. The value is boolean (1 or 0). + * @hide + */ + public static final String MENU_UNLOCK_SCREEN = "menu_unlock_screen"; + + /** + * Color mask tp apply to notification bar when custom is set + * @hide + */ + public static final String NOTIF_BAR_COLOR = "notif_bar_color"; + + /** + * Whether to use custom notification bar + * @hide + */ + public static final String NOTIF_BAR_CUSTOM = "notif_bar_custom"; + + /** + * Whether to use custom notification bar + * @hide + */ + public static final String LOCKSCREEN_MUSIC_CONTROLS = "lockscreen_music_controls"; + + /** + * Whether to use custom notification bar + * @hide + */ + public static final String LOCKSCREEN_ALWAYS_MUSIC_CONTROLS = "lockscreen_always_music_controls"; + + /** + * Whether to use a custom pull-down notification screen + * @hide + */ + public static final String NOTIF_EXPANDED_BAR_COLOR = "notif_expanded_bar_color"; + + /** + * Color mask to apply to pull-down notification screen + * @hide + */ + public static final String NOTIF_EXPANDED_BAR_CUSTOM = "notif_expanded_bar_custom"; + + /** + * Whether to keep the home app at a higher OOM adjustement + * @hide + */ + public static final String LOCK_HOME_IN_MEMORY = "lock_home_in_memory"; + + /** * Settings to backup. This is here so that it's in the same place as the settings * keys and easy to update. * @hide @@ -1738,7 +2242,21 @@ public final class Settings { DOCK_SOUNDS_ENABLED, LOCKSCREEN_SOUNDS_ENABLED, SHOW_WEB_SUGGESTIONS, - NOTIFICATION_LIGHT_PULSE + NOTIFICATION_LIGHT_PULSE, + HAPTIC_FEEDBACK_UP_ENABLED, + HAPTIC_FEEDBACK_ALL_ENABLED, + HAPTIC_DOWN_ARRAY, + HAPTIC_UP_ARRAY, + HAPTIC_LONG_ARRAY, + HAPTIC_DOWN_ARRAY_DEFAULT, + HAPTIC_UP_ARRAY_DEFAULT, + HAPTIC_LONG_ARRAY_DEFAULT, + HAPTIC_TAP_ARRAY, + HAPTIC_TAP_ARRAY_DEFAULT, + NOTIF_BAR_COLOR, + NOTIF_BAR_CUSTOM, + NOTIF_EXPANDED_BAR_COLOR, + NOTIF_EXPANDED_BAR_CUSTOM }; // Settings moved to Settings.Secure @@ -2237,6 +2755,12 @@ public final class Settings { public static final String ADB_ENABLED = "adb_enabled"; /** + * Whether to show ADB notifications. + * @hide + */ + public static final String ADB_NOTIFY = "adb_notify"; + + /** * Setting to allow mock locations and location provider status to be injected into the * LocationManager service for testing purposes during application development. These * locations and status values override actual location and status information generated @@ -2275,6 +2799,14 @@ public final class Settings { } /** + * Get the key that retrieves a bluetooth hid device's priority. + * @hide + */ + public static final String getBluetoothHidDevicePriorityKey(String address) { + return ("bluetooth_hid_device_priority_" + address.toUpperCase()); + } + + /** * Whether or not data roaming is enabled. (0 = false, 1 = true) */ public static final String DATA_ROAMING = "data_roaming"; @@ -2341,6 +2873,48 @@ public final class Settings { "lock_pattern_tactile_feedback_enabled"; /** + * LOCK_DOTS_VISIBLE + * @hide + */ + public static final String LOCK_DOTS_VISIBLE = "lock_pattern_dotsvisible"; + + /** + * LOCK_SHOW_ERROR_PATH + * @hide + */ + public static final String LOCK_SHOW_ERROR_PATH = "lock_pattern_show_error_path"; + + /** + * LOCK_INCORRECT_DELAY + * @hide + */ + public static final String LOCK_INCORRECT_DELAY = "lock_pattern_incorrect_delay"; + + /** + * SHOW_UNLOCK_TEXT + * @hide + */ + public static final String SHOW_UNLOCK_TEXT = "lock_pattern_show_unlock_text"; + + /** + * SHOW_UNLOCK_ERR_TEXT + * @hide + */ + public static final String SHOW_UNLOCK_ERR_TEXT = "lock_pattern_show_unlock_err_text"; + + /** + * LOCK_SHOW_CUSTOM_MSG + * @hide + */ + public static final String LOCK_SHOW_CUSTOM_MSG = "lock_screen_show_custom_msg"; + + /** + * LOCK_CUSTOM_MSG + * @hide + */ + public static final String LOCK_CUSTOM_MSG = "lock_screen_custom_msg"; + + /** * Whether assisted GPS should be enabled or not. * @hide */ @@ -3360,6 +3934,13 @@ public final class Settings { /** + * Whether to allow move of any app to external storage + * @hide + */ + public static final String ALLOW_MOVE_ALL_APPS_EXTERNAL = + "allow_move_all_apps_external"; + + /** * @hide */ public static final String[] SETTINGS_TO_BACKUP = { diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index bf9e854..d271e93 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -25,6 +25,7 @@ import android.content.Intent; import android.database.Cursor; import android.database.sqlite.SqliteWrapper; import android.net.Uri; +import android.os.Environment; import android.telephony.SmsMessage; import android.text.TextUtils; import android.util.Config; @@ -1526,7 +1527,8 @@ public final class Telephony { * which streams the captured image to the uri. Internally we write the media content * to this file. It's named '.temp.jpg' so Gallery won't pick it up. */ - public static final String SCRAP_FILE_PATH = "/sdcard/mms/scrapSpace/.temp.jpg"; + public static final String SCRAP_FILE_PATH = + Environment.getExternalStorageDirectory().getPath() + "/mms/scrapSpace/.temp.jpg"; } public static final class Intents { diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java index 893db2e..1cac1cd 100644 --- a/core/java/android/server/BluetoothA2dpService.java +++ b/core/java/android/server/BluetoothA2dpService.java @@ -22,6 +22,8 @@ package android.server; +import android.app.AlarmManager; +import android.app.PendingIntent; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; @@ -35,6 +37,7 @@ import android.content.IntentFilter; import android.media.AudioManager; import android.os.Handler; import android.os.Message; +import android.os.SystemClock; import android.provider.Settings; import android.util.Log; @@ -55,7 +58,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { private static final String BLUETOOTH_ENABLED = "bluetooth_enabled"; - private static final int MESSAGE_CONNECT_TO = 1; + private static final String ACTION_CONNECT_TO = "android.bluetooth_a2dp.action.CONNECT_TO"; private static final String PROPERTY_STATE = "State"; @@ -111,8 +114,24 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { // after a delay. We delay to avoid connection collisions, // and to give other profiles such as HFP a chance to // connect first. - Message msg = Message.obtain(mHandler, MESSAGE_CONNECT_TO, device); - mHandler.sendMessageDelayed(msg, 6000); + AlarmManager mgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + Intent i = new Intent(ACTION_CONNECT_TO); + i.putExtra(BluetoothDevice.EXTRA_DEVICE, device); + PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0); + mgr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 6000, pi); + } + } else if (action.equals(ACTION_CONNECT_TO)) { + // check bluetooth is still on, device is still preferred, and + // nothing is currently connected + if (mBluetoothService.isEnabled() && + getSinkPriority(device) == BluetoothA2dp.PRIORITY_AUTO_CONNECT && + lookupSinksMatchingStates(new int[] { + BluetoothA2dp.STATE_CONNECTING, + BluetoothA2dp.STATE_CONNECTED, + BluetoothA2dp.STATE_PLAYING, + BluetoothA2dp.STATE_DISCONNECTING}).size() == 0) { + log("Auto-connecting A2DP to sink " + device); + connectSink(device); } } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) { synchronized (this) { @@ -180,6 +199,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { mIntentFilter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); mIntentFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); mIntentFilter.addAction(AudioManager.VOLUME_CHANGED_ACTION); + mIntentFilter.addAction(ACTION_CONNECT_TO); mContext.registerReceiver(mReceiver, mIntentFilter); mAudioDevices = new HashMap<BluetoothDevice, Integer>(); @@ -201,23 +221,6 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { - switch (msg.what) { - case MESSAGE_CONNECT_TO: - BluetoothDevice device = (BluetoothDevice) msg.obj; - // check bluetooth is still on, device is still preferred, and - // nothing is currently connected - if (mBluetoothService.isEnabled() && - getSinkPriority(device) == BluetoothA2dp.PRIORITY_AUTO_CONNECT && - lookupSinksMatchingStates(new int[] { - BluetoothA2dp.STATE_CONNECTING, - BluetoothA2dp.STATE_CONNECTED, - BluetoothA2dp.STATE_PLAYING, - BluetoothA2dp.STATE_DISCONNECTING}).size() == 0) { - log("Auto-connecting A2DP to sink " + device); - connectSink(device); - } - break; - } } }; diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java index c0e4600..b53779b 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2008 The Android Open Source Project + * Copyright (c) 2010, Code Aurora Forum. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +21,7 @@ import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHid; import android.bluetooth.BluetoothUuid; import android.content.Context; import android.content.Intent; @@ -380,6 +382,13 @@ class BluetoothEventLoop { mBluetoothService.setLinkTimeout(address, 8000); } } else { + // Check and clean-up if bonding is in progress + if (mBluetoothService.getBondState().getBondState(address) == + BluetoothDevice.BOND_BONDING) { + mBluetoothService.getBondState().setBondState(address, + BluetoothDevice.BOND_NONE); + } + intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED); } intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); @@ -555,6 +564,7 @@ class BluetoothEventLoop { boolean authorized = false; ParcelUuid uuid = ParcelUuid.fromString(deviceUuid); BluetoothA2dp a2dp = new BluetoothA2dp(mContext); + BluetoothHid hid = new BluetoothHid(mContext); // Bluez sends the UUID of the local service being accessed, _not_ the // remote service @@ -569,6 +579,15 @@ class BluetoothEventLoop { } else { Log.i(TAG, "Rejecting incoming A2DP / AVRCP connection from " + address); } + } else if( mBluetoothService.isEnabled() && BluetoothUuid.isHid(uuid)) { + Log.i(TAG, "Allowing incoming HID connection from " + address); + BluetoothDevice device = mAdapter.getRemoteDevice(address); + authorized = hid.getHidDevicePriority(device) > BluetoothHid.PRIORITY_OFF; + if (authorized) { + Log.i(TAG, "Allowing incoming HID connection from " + address); + } else { + Log.i(TAG, "Rejecting incoming HID connection from " + address); + } } else { Log.i(TAG, "Rejecting incoming " + deviceUuid + " connection from " + address); } diff --git a/core/java/android/server/BluetoothHidService.java b/core/java/android/server/BluetoothHidService.java new file mode 100644 index 0000000..1fd2e04 --- /dev/null +++ b/core/java/android/server/BluetoothHidService.java @@ -0,0 +1,462 @@ +/* + * Copyright (C) 2009 ISB Corporation + * Copyright (C) 2010 0xlab + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * TODO: Move this to services.jar + * and make the contructor package private again. + * @hide + */ + +package android.server; + +import android.bluetooth.BluetoothHid; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothUuid; +import android.bluetooth.IBluetoothHid; +import android.os.ParcelUuid; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Handler; +import android.os.Message; +import android.provider.Settings; +import android.util.Log; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + +public class BluetoothHidService extends IBluetoothHid.Stub { + private static final String TAG = "BluetoothHidService"; + private static final boolean DBG = false; + + public static final String BLUETOOTH_HID_SERVICE = "bluetooth_hid"; + + private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; + private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; + private static final String BLUETOOTH_ENABLED = "bluetooth_enabled"; + + private final Context mContext; + private final IntentFilter mIntentFilter; + private HashMap<BluetoothDevice, Integer> mHidDevices; + private final BluetoothService mBluetoothService; + private final BluetoothAdapter mAdapter; + + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + BluetoothDevice device = + intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { + int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, + BluetoothAdapter.ERROR); + switch (state) { + case BluetoothAdapter.STATE_ON: + onBluetoothEnable(); + break; + case BluetoothAdapter.STATE_TURNING_OFF: + onBluetoothDisable(); + break; + } + } else if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) { + int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, + BluetoothDevice.ERROR); + switch(bondState) { + case BluetoothDevice.BOND_BONDED: + setHidDevicePriority(device, BluetoothHid.PRIORITY_ON); + break; + case BluetoothDevice.BOND_BONDING: + case BluetoothDevice.BOND_NONE: + setHidDevicePriority(device, BluetoothHid.PRIORITY_OFF); + break; + } + } else if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) { + synchronized (this) { + if (mHidDevices.containsKey(device)) { + int state = mHidDevices.get(device); + handleHIDStateChange(device, state, BluetoothHid.STATE_CONNECTED); + } + } + } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) { + synchronized (this) { + if (mHidDevices.containsKey(device)) { + int state = mHidDevices.get(device); + handleHIDStateChange(device, state, BluetoothHid.STATE_DISCONNECTED); + } + } + } + } + }; + + + public BluetoothHidService(Context context, BluetoothService bluetoothService) { + mContext = context; + + mBluetoothService = bluetoothService; + if (mBluetoothService == null) { + throw new RuntimeException("Platform does not support Bluetooth"); + } + + if (!initNative()) { + throw new RuntimeException("Could not init BluetoothHidService"); + } + + mAdapter = BluetoothAdapter.getDefaultAdapter(); + mIntentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED); + mIntentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); + mIntentFilter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); + mIntentFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); + mContext.registerReceiver(mReceiver, mIntentFilter); + + mHidDevices = new HashMap<BluetoothDevice, Integer>(); + + if (mBluetoothService.isEnabled()) + onBluetoothEnable(); + } + + + @Override + protected void finalize() throws Throwable { + try { + cleanupNative(); + } finally { + super.finalize(); + } + } + + private boolean isHidDevice(BluetoothDevice device) { + ParcelUuid[] uuids = mBluetoothService.getRemoteUuids(device.getAddress()); + if (uuids != null && BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HID)) { + return true; + } + return false; + } + + private synchronized boolean addHidDevice (BluetoothDevice device) { + String path = mBluetoothService.getObjectPathFromAddress(device.getAddress()); + String propValues[] = (String []) getHidPropertiesNative(path); + if (DBG) log("addHidDevice device = " + device); + + if (propValues == null) { + Log.e(TAG, "Error while getting HID properties for device: " + device); + return false; + } + Integer state = null; + String name = propValues[0]; + if (name.equals("Connected")) { + state = BluetoothHid.STATE_DISCONNECTED; + if (propValues[1].equals("true")) + state = BluetoothHid.STATE_CONNECTED; + } + mHidDevices.put(device, state); + handleHIDStateChange(device, BluetoothHid.STATE_DISCONNECTED, state); + return true; + } + + + private synchronized void onBluetoothEnable() { + String devices = mBluetoothService.getProperty("Devices"); + + if (devices != null) { + String [] paths = devices.split(","); + for (String path: paths) { + String address = mBluetoothService.getAddressFromObjectPath(path); + BluetoothDevice device = mAdapter.getRemoteDevice(address); + ParcelUuid[] remoteUuids = mBluetoothService.getRemoteUuids(address); + if (remoteUuids != null) + if (BluetoothUuid.containsAnyUuid(remoteUuids, + new ParcelUuid[] {BluetoothUuid.HID})) { + addHidDevice(device); + } + } + } + } + + private synchronized void onBluetoothDisable() { + if (!mHidDevices.isEmpty()) { + BluetoothDevice[] devices = new BluetoothDevice[mHidDevices.size()]; + devices = mHidDevices.keySet().toArray(devices); + + for (BluetoothDevice device : devices) { + int state = mHidDevices.get(device); + switch (state) { + case BluetoothHid.STATE_CONNECTING: + case BluetoothHid.STATE_CONNECTED: + disconnectHidDeviceNative(mBluetoothService.getObjectPathFromAddress( + device.getAddress())); + handleHIDStateChange(device, state, BluetoothHid.STATE_DISCONNECTED); + break; + case BluetoothHid.STATE_DISCONNECTING: + handleHIDStateChange(device, BluetoothHid.STATE_DISCONNECTING, + BluetoothHid.STATE_DISCONNECTED); + break; + } + } + mHidDevices.clear(); + } + + } + + public synchronized boolean connectHidDevice(BluetoothDevice device) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + if (DBG) log("connectHidDevice(" + device + ")"); + + // ignore if there are any active sinks + if (lookupSinksMatchingStates(new int[] { + BluetoothHid.STATE_CONNECTING, + BluetoothHid.STATE_CONNECTED, + BluetoothHid.STATE_DISCONNECTING}).size() != 0) { + return false; + } + if (mHidDevices.get(device) == null && !addHidDevice(device)) + return false; + + int state = mHidDevices.get(device); + + switch (state) { + case BluetoothHid.STATE_CONNECTED: + case BluetoothHid.STATE_DISCONNECTING: + return false; + case BluetoothHid.STATE_CONNECTING: + return true; + } + + String path = mBluetoothService.getObjectPathFromAddress(device.getAddress()); + if (path == null) + return false; + + // State is DISCONNECTED + if (!connectHidDeviceNative(path)) { + return false; + } + return true; + } + + public synchronized boolean disconnectHidDevice(BluetoothDevice device) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + if (DBG) log("disconnectHidDevice(" + device + ")"); + + if (mHidDevices.get(device) == null) + return false; + + int state = mHidDevices.get(device); + switch (state) { + case BluetoothHid.STATE_DISCONNECTED: + return false; + case BluetoothHid.STATE_DISCONNECTING: + return true; + } + + String path = mBluetoothService.getObjectPathFromAddress(device.getAddress()); + if (path == null) { + return false; + } + + if (!disconnectHidDeviceNative(path)) { + return false; + } + return true; + } + + public synchronized BluetoothDevice[] getConnectedSinks() { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + Set<BluetoothDevice> sinks = lookupSinksMatchingStates( + new int[] {BluetoothHid.STATE_CONNECTED}); + return sinks.toArray(new BluetoothDevice[sinks.size()]); + } + + public synchronized BluetoothDevice[] getNonDisconnectedSinks() { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + Set<BluetoothDevice> sinks = lookupSinksMatchingStates( + new int[] {BluetoothHid.STATE_CONNECTED, + BluetoothHid.STATE_CONNECTING, + BluetoothHid.STATE_DISCONNECTING}); + return sinks.toArray(new BluetoothDevice[sinks.size()]); + } + + public synchronized int getHidDeviceState(BluetoothDevice device) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + Integer state = mHidDevices.get(device); + if (state == null){ + return BluetoothHid.STATE_DISCONNECTED; + } + return state; + } + + public synchronized int getHidDevicePriority(BluetoothDevice device) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.getBluetoothHidDevicePriorityKey(device.getAddress()), + BluetoothHid.PRIORITY_UNDEFINED); + } + + + public synchronized boolean setHidDevicePriority(BluetoothDevice device, int priority) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) { + return false; + } + return Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.getBluetoothHidDevicePriorityKey(device.getAddress()), priority); + + } + + + public synchronized void onPropertyChanged(String path, String []propValues) { + if (!mBluetoothService.isEnabled()) { + return; + } + + String name = propValues[0]; + String address = mBluetoothService.getAddressFromObjectPath(path); + + if (address == null) { + Log.e(TAG, "onPropertyChanged: Address of the remote device in null"); + return; + } + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + if (DBG) log("HID Device property changed: " + address + " property: " + name); + + if (name.equals("Connected")) { + int state = BluetoothHid.STATE_DISCONNECTED; + if (propValues[1].equals("true")) { + state = BluetoothHid.STATE_CONNECTED; + } + if (mHidDevices.get(device) == null) { + // This is for an incoming connection for a device not known to us. + // We have authorized it and bluez state has changed. + addHidDevice(device); + } else { + int prevState = mHidDevices.get(device); + handleHIDStateChange(device, prevState, state); + } + } + + } + + private void handleHIDStateChange(BluetoothDevice device, int prevState, int state) { + + if(DBG) log("HID device: " + device + " State:" + prevState + "->" + state); + + if (state != prevState) { + mHidDevices.put(device, state); + + if (getHidDevicePriority(device) > BluetoothHid.PRIORITY_OFF && + state == BluetoothHid.STATE_CONNECTING || + state == BluetoothHid.STATE_CONNECTED) { + setHidDevicePriority(device, BluetoothHid.PRIORITY_AUTO_CONNECT); + } + Intent intent = new Intent(BluetoothHid.HID_DEVICE_STATE_CHANGED_ACTION); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); + intent.putExtra(BluetoothHid.HID_DEVICE_PREVIOUS_STATE, prevState); + intent.putExtra(BluetoothHid.HID_DEVICE_STATE, state); + mContext.sendBroadcast(intent, BLUETOOTH_PERM); + } + } + + private synchronized Set<BluetoothDevice> lookupSinksMatchingStates(int[] states) { + Set<BluetoothDevice> sinks = new HashSet<BluetoothDevice>(); + if (mHidDevices.isEmpty()) { + return sinks; + } + for (BluetoothDevice device: mHidDevices.keySet()) { + int sinkState = getHidDeviceState(device); + for (int state : states) { + if (state == sinkState) { + sinks.add(device); + break; + } + } + } + return sinks; + } + + public synchronized void onHidDeviceConnected(String path) { + if (mHidDevices == null) return; + + if (!mBluetoothService.isEnabled()) { + return; + } + + String address = mBluetoothService.getAddressFromObjectPath(path); + if (address == null) { + Log.e(TAG, "onHidDeviceConnected: Address of the remote device in null"); + return; + } + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + if(DBG) log(" onHidDeviceConnected device = " + device); + if (mHidDevices.containsKey(device)) { + int prevState = mHidDevices.get(device); + handleHIDStateChange(device, prevState, BluetoothHid.STATE_CONNECTED); + } + + } + + public synchronized void onHidDeviceDisconnected(String path) { + if (mHidDevices == null) return; + + if (!mBluetoothService.isEnabled()) { + return; + } + + String address = mBluetoothService.getAddressFromObjectPath(path); + if (address == null) { + Log.e(TAG, "onHidDeviceDisconnected: Address of the remote device in null"); + return; + } + + BluetoothDevice device = mAdapter.getRemoteDevice(address); + if(DBG) log(" onHidDeviceDisconnected device = " + device); + if (mHidDevices.containsKey(device)) { + int prevState = mHidDevices.get(device); + handleHIDStateChange(device, prevState, BluetoothHid.STATE_DISCONNECTED); + } + } + + @Override + protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (mHidDevices.isEmpty()) return; + pw.println("Cached hid devices:"); + for (BluetoothDevice device : mHidDevices.keySet()) { + int state = mHidDevices.get(device); + pw.println(device + " " + BluetoothHid.stateToString(state)); + } + } + + private static void log(String msg) { + Log.d(TAG, msg); + } + + private native boolean initNative(); + private native void cleanupNative(); + + private synchronized native boolean connectHidDeviceNative(String path); + private synchronized native boolean disconnectHidDeviceNative(String path); + private synchronized native Object []getHidPropertiesNative(String path); + +} diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index c0affd3..7095dbe 100644 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2008 The Android Open Source Project + * Copyright (c) 2010, Code Aurora Forum. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -1167,7 +1168,13 @@ public class BluetoothService extends IBluetooth.Stub { if (!BluetoothAdapter.checkBluetoothAddress(address)) { return false; } - return removeDeviceNative(getObjectPathFromAddress(address)); + + if (removeDeviceNative(getObjectPathFromAddress(address))) { + removeRemoteDeviceProperties(address); + return true; + } + + return false; } public synchronized String[] listBonds() { @@ -1275,6 +1282,7 @@ public class BluetoothService extends IBluetooth.Stub { if (propVal != null) { propVal.put(name, value); mDeviceProperties.put(address, propVal); + Log.d(TAG, "setRemoteDeviceProperty addr = " + address + " name = " + name + " value = " + value); } else { Log.e(TAG, "setRemoteDeviceProperty for a device not in cache:" + address); } diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 38ac9b7..4063aef 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -305,13 +305,13 @@ public abstract class Layout { if (spans[n] instanceof LeadingMarginSpan) { LeadingMarginSpan margin = (LeadingMarginSpan) spans[n]; - if (dir == DIR_RIGHT_TO_LEFT) { - margin.drawLeadingMargin(c, paint, right, dir, ltop, - lbaseline, lbottom, buf, - start, end, isFirstParaLine, this); - - right -= margin.getLeadingMargin(isFirstParaLine); - } else { + // if (dir == DIR_RIGHT_TO_LEFT) { + // margin.drawLeadingMargin(c, paint, right, dir, ltop, + // lbaseline, lbottom, buf, + // start, end, isFirstParaLine, this); + // + // right -= margin.getLeadingMargin(isFirstParaLine); + //} else { margin.drawLeadingMargin(c, paint, left, dir, ltop, lbaseline, lbottom, buf, start, end, isFirstParaLine, this); @@ -320,7 +320,7 @@ public abstract class Layout { if (margin instanceof LeadingMarginSpan.LeadingMarginSpan2) { int count = ((LeadingMarginSpan.LeadingMarginSpan2)margin).getLeadingMarginLineCount(); useMargin = count > i; - } + // } left += margin.getLeadingMargin(useMargin); } } @@ -364,8 +364,7 @@ public abstract class Layout { Assert.assertTrue(dir == DIR_LEFT_TO_RIGHT); Assert.assertNotNull(c); } - // XXX: assumes there's nothing additional to be done - c.drawText(buf, start, end, x, lbaseline, paint); + c.drawText(buf, start, end, x, lbaseline, paint,false); } else { drawText(c, buf, start, end, dir, directions, x, ltop, lbaseline, lbottom, paint, mWorkPaint, @@ -1810,7 +1809,7 @@ public abstract class Layout { public static class Directions { private short[] mDirections; - // The values in mDirections are the offsets from the first character + // The values in mDirections are the offsets from the last flip in direction // in the line to the next flip in direction. Runs at even indices // are left-to-right, the others are right-to-left. So, for example, // a line that starts with a right-to-left run has 0 at mDirections[0], @@ -1823,6 +1822,36 @@ public abstract class Layout { /* package */ Directions(short[] dirs) { mDirections = dirs; } + + static int baseDirection(Directions dir,int length) { + if (dir == DIRS_ALL_LEFT_TO_RIGHT) { + return DIR_LEFT_TO_RIGHT; + } else if (dir == DIRS_ALL_RIGHT_TO_LEFT) { + return DIR_RIGHT_TO_LEFT; + } + + int sum=0; + int lastSwitch=0; + int i=0; + while ((i+1) < dir.mDirections.length) { + sum+=dir.mDirections[i];//-lastSwitch; + sum-=dir.mDirections[i+1];//-dir.mDirections[i]; + lastSwitch=dir.mDirections[i+1]; + i+=2; + } + + if ((i+1)==dir.mDirections.length) { + sum+=dir.mDirections[i];//-lastSwitch); + } else if (i==dir.mDirections.length) { + sum-=length-lastSwitch; + } + + if (sum>=0) { + return DIR_LEFT_TO_RIGHT; + } else { + return DIR_RIGHT_TO_LEFT; + } + } } /** diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java index caaafa1..b6a9b17 100644 --- a/core/java/android/text/SpannableStringBuilder.java +++ b/core/java/android/text/SpannableStringBuilder.java @@ -1042,14 +1042,14 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable, checkRange("drawText", start, end); if (end <= mGapStart) { - c.drawText(mText, start, end - start, x, y, p); + c.drawText(mText, start, end - start, x, y, p,true); } else if (start >= mGapStart) { - c.drawText(mText, start + mGapLength, end - start, x, y, p); + c.drawText(mText, start + mGapLength, end - start, x, y, p,true); } else { char[] buf = TextUtils.obtain(end - start); getChars(start, end, buf, 0); - c.drawText(buf, 0, end - start, x, y, p); + c.drawText(buf, 0, end - start, x, y, p,true); TextUtils.recycle(buf); } } diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index f02ad2a..047e6156 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -689,6 +689,7 @@ extends Layout if (cur == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) chInfo[j] = Character.DIRECTIONALITY_ARABIC_NUMBER; + else chInfo[j] = Character.DIRECTIONALITY_LEFT_TO_RIGHT; } } @@ -777,7 +778,7 @@ extends Layout cur = d; if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) - chInfo[j] = cur; + chInfo[j] = Character.DIRECTIONALITY_LEFT_TO_RIGHT; } // dump(chdirs, n, "W7"); @@ -790,9 +791,10 @@ extends Layout if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT || d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) { cur = d; - } else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER || - d == Character.DIRECTIONALITY_ARABIC_NUMBER) { + } else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) { cur = Character.DIRECTIONALITY_RIGHT_TO_LEFT; + } else if (d == Character.DIRECTIONALITY_ARABIC_NUMBER) { + cur = Character.DIRECTIONALITY_LEFT_TO_RIGHT; } else { byte dd = SOR; int k; @@ -804,8 +806,10 @@ extends Layout dd == Character.DIRECTIONALITY_RIGHT_TO_LEFT) { break; } - if (dd == Character.DIRECTIONALITY_EUROPEAN_NUMBER || - dd == Character.DIRECTIONALITY_ARABIC_NUMBER) { + if (dd == Character.DIRECTIONALITY_EUROPEAN_NUMBER) { + dd = Character.DIRECTIONALITY_LEFT_TO_RIGHT; + break; + } else if (dd == Character.DIRECTIONALITY_ARABIC_NUMBER) { dd = Character.DIRECTIONALITY_RIGHT_TO_LEFT; break; } @@ -1244,7 +1248,7 @@ extends Layout } public int getParagraphDirection(int line) { - return mLines[mColumns * line + DIR] >> DIR_SHIFT; + return Directions.baseDirection(mLineDirections[line],getLineEnd(line)-getLineStart(line)); } public boolean getLineContainsTab(int line) { diff --git a/core/java/android/text/Styled.java b/core/java/android/text/Styled.java index 513b2cd..d2be383 100644 --- a/core/java/android/text/Styled.java +++ b/core/java/android/text/Styled.java @@ -129,7 +129,7 @@ public class Styled } canvas.drawText(tmp, tmpstart, tmpend, - x - ret, y + workPaint.baselineShift, workPaint); + x - ret, y + workPaint.baselineShift, workPaint,false); } else { if (needWidth) { if (!haveWidth) { @@ -139,7 +139,7 @@ public class Styled } canvas.drawText(tmp, tmpstart, tmpend, - x, y + workPaint.baselineShift, workPaint); + x, y + workPaint.baselineShift, workPaint,false); } } else { if (needWidth && !haveWidth) { @@ -261,13 +261,13 @@ public class Styled if (canvas != null) canvas.drawText(tmp, 0, tmpend, - x - ret, y, paint); + x - ret, y, paint,false); } else { if (needWidth) ret = paint.measureText(text, start, end); if (canvas != null) - canvas.drawText(text, start, end, x, y, paint); + canvas.drawText(text, start, end, x, y, paint,false); } if (fmi != null) { diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java index 8eae111..c05a8fe 100644 --- a/core/java/android/text/format/Time.java +++ b/core/java/android/text/format/Time.java @@ -32,7 +32,7 @@ public class Time { private static final String Y_M_D_T_H_M_S_000 = "%Y-%m-%dT%H:%M:%S.000"; private static final String Y_M_D_T_H_M_S_000_Z = "%Y-%m-%dT%H:%M:%S.000Z"; private static final String Y_M_D = "%Y-%m-%d"; - + public static final String TIMEZONE_UTC = "UTC"; /** @@ -170,11 +170,11 @@ public class Time { public Time() { this(TimeZone.getDefault().getID()); } - + /** * A copy constructor. Construct a Time object by copying the given * Time object. No normalization occurs. - * + * * @param other */ public Time(Time other) { @@ -185,17 +185,17 @@ public class Time { * Ensures the values in each field are in range. For example if the * current value of this calendar is March 32, normalize() will convert it * to April 1. It also fills in weekDay, yearDay, isDst and gmtoff. - * + * * <p> * If "ignoreDst" is true, then this method sets the "isDst" field to -1 * (the "unknown" value) before normalizing. It then computes the * correct value for "isDst". - * + * * <p> * See {@link #toMillis(boolean)} for more information about when to * use <tt>true</tt> or <tt>false</tt> for "ignoreDst". - * - * @return the UTC milliseconds since the epoch + * + * @return the UTC milliseconds since the epoch */ native public long normalize(boolean ignoreDst); @@ -379,13 +379,13 @@ public class Time { * Parses a date-time string in either the RFC 2445 format or an abbreviated * format that does not include the "time" field. For example, all of the * following strings are valid: - * + * * <ul> * <li>"20081013T160000Z"</li> * <li>"20081013T160000"</li> * <li>"20081013"</li> * </ul> - * + * * Returns whether or not the time is in UTC (ends with Z). If the string * ends with "Z" then the timezone is set to UTC. If the date-time string * included only a date and no time field, then the <code>allDay</code> @@ -396,10 +396,10 @@ public class Time { * <code>yearDay</code>, and <code>gmtoff</code> are always set to zero, * and the field <code>isDst</code> is set to -1 (unknown). To set those * fields, call {@link #normalize(boolean)} after parsing. - * + * * To parse a date-time string and convert it to UTC milliseconds, do * something like this: - * + * * <pre> * Time time = new Time(); * String date = "20081013T160000Z"; @@ -428,25 +428,25 @@ public class Time { * Parse a time in RFC 3339 format. This method also parses simple dates * (that is, strings that contain no time or time offset). For example, * all of the following strings are valid: - * + * * <ul> * <li>"2008-10-13T16:00:00.000Z"</li> * <li>"2008-10-13T16:00:00.000+07:00"</li> * <li>"2008-10-13T16:00:00.000-07:00"</li> * <li>"2008-10-13"</li> * </ul> - * + * * <p> * If the string contains a time and time offset, then the time offset will * be used to convert the time value to UTC. * </p> - * + * * <p> * If the given string contains just a date (with no time field), then * the {@link #allDay} field is set to true and the {@link #hour}, * {@link #minute}, and {@link #second} fields are set to zero. * </p> - * + * * <p> * Returns true if the resulting time value is in UTC time. * </p> @@ -462,7 +462,7 @@ public class Time { } return false; } - + native private boolean nativeParse3339(String s); /** @@ -484,13 +484,13 @@ public class Time { * <em>not</em> change any of the fields in this Time object. If you want * to normalize the fields in this Time object and also get the milliseconds * then use {@link #normalize(boolean)}. - * + * * <p> * If "ignoreDst" is false, then this method uses the current setting of the * "isDst" field and will adjust the returned time if the "isDst" field is * wrong for the given time. See the sample code below for an example of * this. - * + * * <p> * If "ignoreDst" is true, then this method ignores the current setting of * the "isDst" field in this Time object and will instead figure out the @@ -499,27 +499,27 @@ public class Time { * correct value of the "isDst" field is when the time is inherently * ambiguous because it falls in the hour that is repeated when switching * from Daylight-Saving Time to Standard Time. - * + * * <p> * Here is an example where <tt>toMillis(true)</tt> adjusts the time, * assuming that DST changes at 2am on Sunday, Nov 4, 2007. - * + * * <pre> * Time time = new Time(); - * time.set(2007, 10, 4); // set the date to Nov 4, 2007, 12am + * time.set(4, 10, 2007); // set the date to Nov 4, 2007, 12am * time.normalize(); // this sets isDst = 1 * time.monthDay += 1; // changes the date to Nov 5, 2007, 12am * millis = time.toMillis(false); // millis is Nov 4, 2007, 11pm * millis = time.toMillis(true); // millis is Nov 5, 2007, 12am * </pre> - * + * * <p> * To avoid this problem, use <tt>toMillis(true)</tt> * after adding or subtracting days or explicitly setting the "monthDay" * field. On the other hand, if you are adding * or subtracting hours or minutes, then you should use * <tt>toMillis(false)</tt>. - * + * * <p> * You should also use <tt>toMillis(false)</tt> if you want * to read back the same milliseconds that you set with {@link #set(long)} @@ -531,14 +531,14 @@ public class Time { * Sets the fields in this Time object given the UTC milliseconds. After * this method returns, all the fields are normalized. * This also sets the "isDst" field to the correct value. - * + * * @param millis the time in UTC milliseconds since the epoch. */ native public void set(long millis); /** * Format according to RFC 2445 DATETIME type. - * + * * <p> * The same as format("%Y%m%dT%H%M%S"). */ @@ -584,7 +584,7 @@ public class Time { * Sets the date from the given fields. Also sets allDay to true. * Sets weekDay, yearDay and gmtoff to 0, and isDst to -1. * Call {@link #normalize(boolean)} if you need those. - * + * * @param monthDay the day of the month (in the range [1,31]) * @param month the zero-based month number (in the range [0,11]) * @param year the year @@ -606,7 +606,7 @@ public class Time { /** * Returns true if the time represented by this Time object occurs before * the given time. - * + * * @param that a given Time object to compare against * @return true if this time is less than the given time */ @@ -618,7 +618,7 @@ public class Time { /** * Returns true if the time represented by this Time object occurs after * the given time. - * + * * @param that a given Time object to compare against * @return true if this time is greater than the given time */ @@ -632,12 +632,12 @@ public class Time { * closest Thursday yearDay. */ private static final int[] sThursdayOffset = { -3, 3, 2, 1, 0, -1, -2 }; - + /** * Computes the week number according to ISO 8601. The current Time * object must already be normalized because this method uses the * yearDay and weekDay fields. - * + * * <p> * In IS0 8601, weeks start on Monday. * The first week of the year (week 1) is defined by ISO 8601 as the @@ -645,12 +645,12 @@ public class Time { * Or equivalently, the week containing January 4. Or equivalently, * the week with the year's first Thursday in it. * </p> - * + * * <p> * The week number can be calculated by counting Thursdays. Week N * contains the Nth Thursday of the year. * </p> - * + * * @return the ISO week number. */ public int getWeekNumber() { @@ -661,7 +661,7 @@ public class Time { if (closestThursday >= 0 && closestThursday <= 364) { return closestThursday / 7 + 1; } - + // The week crosses a year boundary. Time temp = new Time(this); temp.monthDay += sThursdayOffset[weekDay]; @@ -670,7 +670,7 @@ public class Time { } /** - * Return a string in the RFC 3339 format. + * Return a string in the RFC 3339 format. * <p> * If allDay is true, expresses the time as Y-M-D</p> * <p> @@ -691,13 +691,13 @@ public class Time { int offset = (int)Math.abs(gmtoff); int minutes = (offset % 3600) / 60; int hours = offset / 3600; - + return String.format("%s%s%02d:%02d", base, sign, hours, minutes); } } - + /** - * Returns true if the day of the given time is the epoch on the Julian Calendar + * Returns true if the day of the given time is the epoch on the Julian Calendar * (January 1, 1970 on the Gregorian calendar). * * @param time the time to test @@ -707,7 +707,7 @@ public class Time { long millis = time.toMillis(true); return getJulianDay(millis, 0) == EPOCH_JULIAN_DAY; } - + /** * Computes the Julian day number, given the UTC milliseconds * and the offset (in seconds) from UTC. The Julian day for a given @@ -716,10 +716,10 @@ public class Time { * what timezone is being used. The Julian day is useful for testing * if two events occur on the same day and for determining the relative * time of an event from the present ("yesterday", "3 days ago", etc.). - * + * * <p> * Use {@link #toMillis(boolean)} to get the milliseconds. - * + * * @param millis the time in UTC milliseconds * @param gmtoff the offset from UTC in seconds * @return the Julian day @@ -729,7 +729,7 @@ public class Time { long julianDay = (millis + offsetMillis) / DateUtils.DAY_IN_MILLIS; return (int) julianDay + EPOCH_JULIAN_DAY; } - + /** * <p>Sets the time from the given Julian day number, which must be based on * the same timezone that is set in this Time object. The "gmtoff" field @@ -738,7 +738,7 @@ public class Time { * After this method returns all the fields will be normalized and the time * will be set to 12am at the beginning of the given Julian day. * </p> - * + * * <p> * The only exception to this is if 12am does not exist for that day because * of daylight saving time. For example, Cairo, Eqypt moves time ahead one @@ -746,7 +746,7 @@ public class Time { * also change daylight saving time at 12am. In those cases, the time * will be set to 1am. * </p> - * + * * @param julianDay the Julian day in the timezone for this Time object * @return the UTC milliseconds for the beginning of the Julian day */ @@ -756,13 +756,13 @@ public class Time { // the day. long millis = (julianDay - EPOCH_JULIAN_DAY) * DateUtils.DAY_IN_MILLIS; set(millis); - + // Figure out how close we are to the requested Julian day. // We can't be off by more than a day. int approximateDay = getJulianDay(millis, gmtoff); int diff = julianDay - approximateDay; monthDay += diff; - + // Set the time to 12am and re-normalize. hour = 0; minute = 0; diff --git a/core/java/android/view/HapticFeedbackConstants.java b/core/java/android/view/HapticFeedbackConstants.java index 8f40260..e21689d 100644 --- a/core/java/android/view/HapticFeedbackConstants.java +++ b/core/java/android/view/HapticFeedbackConstants.java @@ -36,6 +36,12 @@ public class HapticFeedbackConstants { public static final int VIRTUAL_KEY = 1; /** + * User has released a virtual on-screen key. + * @hide + */ + public static final int VIRTUAL_RELEASED = 2; + + /** * The user has pressed a soft keyboard key. */ public static final int KEYBOARD_TAP = 3; diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index d4f9787..2c78f2c 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -120,6 +120,15 @@ public class KeyEvent implements Parcelable { public static final int KEYCODE_MEDIA_REWIND = 89; public static final int KEYCODE_MEDIA_FAST_FORWARD = 90; public static final int KEYCODE_MUTE = 91; + public static final int KEYCODE_FUNC_1 = 92; + public static final int KEYCODE_FUNC_2 = 93; + public static final int KEYCODE_FUNC_3 = 94; + public static final int KEYCODE_FUNC_4 = 95; + public static final int KEYCODE_FUNC_5 = 96; + public static final int KEYCODE_FUNC_6 = 97; + public static final int KEYCODE_FUNC_7 = 98; + public static final int KEYCODE_FUNC_8 = 99; + public static final int KEYCODE_QUECHAR = 100; // NOTE: If you add a new keycode here you must also add it to: // isSystem() @@ -135,7 +144,7 @@ public class KeyEvent 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_MUTE; + private static final int LAST_KEYCODE = KEYCODE_QUECHAR; /** * @deprecated There are now more than MAX_KEYCODE keycodes. diff --git a/core/java/android/view/RawInputEvent.java b/core/java/android/view/RawInputEvent.java index 3bbfea8..a4475a1 100644 --- a/core/java/android/view/RawInputEvent.java +++ b/core/java/android/view/RawInputEvent.java @@ -28,6 +28,7 @@ public class RawInputEvent { public static final int CLASS_TRACKBALL = 0x00000008; public static final int CLASS_TOUCHSCREEN_MT = 0x00000010; public static final int CLASS_DPAD = 0x00000020; + public static final int CLASS_MOUSE= 0x00000040; // More special classes for QueuedEvent below. public static final int CLASS_CONFIGURATION_CHANGED = 0x10000000; diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index 03efea9..2591a04 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -2586,6 +2586,9 @@ public final class ViewRoot extends Handler implements ViewParent, switch (effectId) { case SoundEffectConstants.CLICK: audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK); + if (audioManager.queryHapticsAllEnabled()){ + performHapticFeedback(HapticFeedbackConstants.VIRTUAL_RELEASED, false); + }; return; case SoundEffectConstants.NAVIGATION_DOWN: audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_DOWN); diff --git a/core/java/android/view/WindowOrientationListener.java b/core/java/android/view/WindowOrientationListener.java index 25df1f4..0d6f973 100755 --- a/core/java/android/view/WindowOrientationListener.java +++ b/core/java/android/view/WindowOrientationListener.java @@ -16,11 +16,15 @@ package android.view; +import android.content.ContentResolver; import android.content.Context; +import android.database.ContentObserver; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; +import android.os.Handler; +import android.provider.Settings; import android.util.Config; import android.util.Log; @@ -39,6 +43,10 @@ public abstract class WindowOrientationListener { private int mRate; private Sensor mSensor; private SensorEventListenerImpl mSensorEventListener; + private Context mContext; + private Handler mHandler; + private SettingsObserver mSettingsObserver; + private int mAccelerometerMode; /** * Creates a new WindowOrientationListener. @@ -70,6 +78,8 @@ public abstract class WindowOrientationListener { // Create listener only if sensors do exist mSensorEventListener = new SensorEventListenerImpl(); } + mContext = context; + mHandler = new Handler(); } /** @@ -85,6 +95,10 @@ public abstract class WindowOrientationListener { if (localLOGV) Log.d(TAG, "WindowOrientationListener enabled"); mSensorManager.registerListener(mSensorEventListener, mSensor, mRate); mEnabled = true; + if (mSettingsObserver == null) { + mSettingsObserver = new SettingsObserver(mHandler); + } + mSettingsObserver.observe(); } } @@ -100,6 +114,10 @@ public abstract class WindowOrientationListener { if (localLOGV) Log.d(TAG, "WindowOrientationListener disabled"); mSensorManager.unregisterListener(mSensorEventListener); mEnabled = false; + if (mSettingsObserver != null) { + mSettingsObserver.stop(); + mSettingsObserver = null; + } } } @@ -110,6 +128,38 @@ public abstract class WindowOrientationListener { return -1; } + class SettingsObserver extends ContentObserver { + SettingsObserver(Handler handler) { + super(handler); + } + + void observe() { + ContentResolver resolver = mContext.getContentResolver(); + resolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.ACCELEROMETER_ROTATION_MODE), false, this); + if (localLOGV) Log.i(TAG, "SettingsObserver enabled"); + update(); + } + + public void stop() { + ContentResolver resolver = mContext.getContentResolver(); + resolver.unregisterContentObserver(this); + if (localLOGV) Log.i(TAG, "SettingsObserver disabled"); + } + + @Override + public void onChange(boolean selfChange) { + update(); + } + + public void update() { + ContentResolver resolver = mContext.getContentResolver(); + mAccelerometerMode = Settings.System.getInt(resolver, + Settings.System.ACCELEROMETER_ROTATION_MODE, 5); + if (localLOGV) Log.i(TAG, "mAccelerometerMode=" + mAccelerometerMode); + } + } + 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); @@ -123,14 +173,15 @@ public abstract class WindowOrientationListener { // ROTATION_90 = right side of device facing the sky, etc. private static final int ROTATION_0 = 0; private static final int ROTATION_90 = 1; - private static final int ROTATION_270 = 2; + private static final int ROTATION_180 = 2; + private static final int ROTATION_270 = 3; // 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}; + Surface.ROTATION_180, 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. @@ -140,16 +191,18 @@ public abstract class WindowOrientationListener { // 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[][][] { - {{60, 180}, {180, 300}}, - {{0, 45}, {45, 165}, {330, 360}}, - {{0, 30}, {195, 315}, {315, 360}} + {{60, 165}, {165, 195}, {195, 300}}, + {{0, 30}, {150, 210}, {30, 150}, {330, 360}}, + {{240, 345}, {15, 120}, {0, 15}, {345, 360}}, + {{0, 30}, {150, 195}, {195, 315}, {315, 360}} }; // See THRESHOLDS private final int[][] ROTATE_TO = new int[][] { - {ROTATION_270, ROTATION_90}, - {ROTATION_0, ROTATION_270, ROTATION_0}, - {ROTATION_0, ROTATION_90, ROTATION_0} + {ROTATION_270, ROTATION_180, ROTATION_90}, + {ROTATION_0, ROTATION_180, ROTATION_270, ROTATION_0}, + {ROTATION_90, ROTATION_270, ROTATION_0, ROTATION_0}, + {ROTATION_0, ROTATION_180, ROTATION_90, ROTATION_0} }; // Maximum absolute tilt angle at which to consider orientation changes. Beyond this (i.e. @@ -159,7 +212,7 @@ public abstract class WindowOrientationListener { // 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 // particular orientation here. - private final int[] MAX_TRANSITION_TILT = new int[] {MAX_TILT, MAX_TILT, MAX_TILT}; + private final int[] MAX_TRANSITION_TILT = new int[] {MAX_TILT, MAX_TILT, MAX_TILT, MAX_TILT}; // 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 @@ -224,6 +277,25 @@ public abstract class WindowOrientationListener { return; } + boolean allowed = rotation == ROTATION_0; + if (!allowed) { + switch (rotation) { + case ROTATION_90: + allowed = (mAccelerometerMode & 1) != 0; + break; + case ROTATION_180: + allowed = (mAccelerometerMode & 2) != 0; + break; + case ROTATION_270: + allowed = (mAccelerometerMode & 4) != 0; + break; + } + } + if (!allowed) { + if (localLOGV) Log.i(TAG, " not allowed rotation = " + rotation); + return; + } + if (localLOGV) Log.i(TAG, " new rotation = " + rotation); mRotation = rotation; onOrientationChanged(SURFACE_ROTATIONS[rotation]); diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java index 219a469..ec0858c 100644 --- a/core/java/android/webkit/BrowserFrame.java +++ b/core/java/android/webkit/BrowserFrame.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2006 The Android Open Source Project + * Copyright (c) 2010, Code Aurora Forum. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -229,6 +230,22 @@ class BrowserFrame extends Handler { } } + public void startDnsPrefetch() { + if (DebugFlags.BROWSER_FRAME) { + Log.v(LOGTAG, "Starting DNS prefetch"); + } + + DnsResolver dnsResolver = DnsResolver.getInstance(); + if(dnsResolver == null ) + return; + + HashMap hostsMap = nativeGetEmbeddedHostNames(dnsResolver.getMaxParallelDnsQueryPerPage()); + if(hostsMap == null) + return; + + dnsResolver.resolveDnsForHostMap(hostsMap); + } + /** * Load a url from the network or the filesystem into the main frame. * Following the same behaviour as Safari, javascript: URLs are not passed @@ -643,7 +660,9 @@ class BrowserFrame extends Handler { boolean userGesture, boolean synchronous, String username, - String password) { + String password, + int priority, + boolean commit) { PerfChecker checker = new PerfChecker(); if (mSettings.getCacheMode() != WebSettings.LOAD_DEFAULT) { @@ -717,6 +736,8 @@ class BrowserFrame extends Handler { LoadListener loadListener = LoadListener.getLoadListener(mContext, this, url, loaderHandle, synchronous, isMainFramePage, mainResource, userGesture, postDataIdentifier, username, password); + loadListener.setPriority(priority); + loadListener.setShouldCommit(commit); mCallbackProxy.onLoadResource(url); @@ -967,6 +988,8 @@ class BrowserFrame extends Handler { private native void nativeLoadData(String baseUrl, String data, String mimeType, String encoding, String historyUrl); + private native HashMap nativeGetEmbeddedHostNames(int maxDnsHostCount); + /** * Stop loading the current page. */ diff --git a/core/java/android/webkit/DnsResolver.java b/core/java/android/webkit/DnsResolver.java new file mode 100644 index 0000000..80a0808 --- /dev/null +++ b/core/java/android/webkit/DnsResolver.java @@ -0,0 +1,206 @@ +/*
+ * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Code Aurora nor
+ * the names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package android.webkit;
+
+import android.os.Handler;
+import android.util.Log;
+import android.os.Message;
+import android.os.Process;
+
+import java.lang.Thread;
+import java.lang.InterruptedException;
+import java.net.UnknownHostException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Semaphore;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+final class DnsResolver {
+
+ private static final String LOGTAG = "webcore";
+
+ /* Max Thread pool size is taken from data published by google
+ * that mentions max hosts per webpage = 8 */
+ private final int MAX_DNS_RESOLVER_THREAD_POOL_SIZE = 8;
+
+ /* This number is derived by considering various factors such as OS DNS cache size,
+ * Browser Cache size and realistic need to spent time on DNS prefetch
+ */
+ private final int MAX_PARALLEL_DNS_QUERIES_PER_PAGE = 64;
+
+ private volatile boolean mDnsResolverThreadPoolRunning = false;
+
+ private volatile boolean mShutDownInProgress = false;
+
+ private static DnsResolver sDnsResolver;
+
+ private HashMap mHostNamesToBeResolved;
+
+ private ExecutorService mDnsResolverThreadPool;
+
+ private static int mDnsResolverRefCount = 0;
+
+ /* Lock to synchronize the access to threadpool */
+ private static Object mThreadPoolLock = new Object();
+
+ public static synchronized DnsResolver createDnsResolver() {
+ if (sDnsResolver == null) {
+ sDnsResolver = new DnsResolver();
+ }
+ ++mDnsResolverRefCount;
+ return sDnsResolver;
+ }
+
+ public static DnsResolver getInstance() {
+ return sDnsResolver;
+ }
+
+ private DnsResolver() {
+ createDnsResolverThreadPool();
+ }
+
+ private void createDnsResolverThreadPool() {
+ final Runnable startDnsResolver = new Runnable() {
+ public void run() {
+ /* DNS resolver priority should be same as of HTTP thread pool */
+ Process.setThreadPriority( android.os.Process.THREAD_PRIORITY_DEFAULT +
+ android.os.Process.THREAD_PRIORITY_LESS_FAVORABLE);
+ mDnsResolverThreadPool = Executors.newFixedThreadPool(MAX_DNS_RESOLVER_THREAD_POOL_SIZE);
+ mHostNamesToBeResolved = new HashMap();
+ boolean bResolvedPriorityHostNames = false;
+ int dnsQueryCounter = 0;
+ int numHosts = 0;
+ while(!mShutDownInProgress) {
+ synchronized(mHostNamesToBeResolved) {
+ numHosts = mHostNamesToBeResolved.size();
+ }
+ if(numHosts <= 0) {
+ try {
+ dnsQueryCounter = 0;
+ bResolvedPriorityHostNames = false;
+ mDnsResolverThreadPoolRunning = true;
+ synchronized(mThreadPoolLock) {
+ mThreadPoolLock.wait();
+ }
+ } catch(java.lang.InterruptedException e) {
+ }
+ }
+ else {
+ synchronized(mHostNamesToBeResolved) {
+ Iterator iterator = mHostNamesToBeResolved.entrySet().iterator();
+ while(iterator.hasNext() && mDnsResolverThreadPoolRunning && (dnsQueryCounter < MAX_PARALLEL_DNS_QUERIES_PER_PAGE)) {
+ Map.Entry entry = (Map.Entry) iterator.next();
+ final String hostName = (String)entry.getKey();
+ final String priority = (String)entry.getValue();
+ if( (!bResolvedPriorityHostNames && priority.equalsIgnoreCase("1")) ||
+ ( bResolvedPriorityHostNames && priority.equalsIgnoreCase("0"))
+ ) {
+ ++dnsQueryCounter;
+ iterator.remove();
+ Runnable task = new Runnable() {
+ public void run() {
+ try {
+ java.net.InetAddress.getByName(hostName);
+ } catch(java.net.UnknownHostException e) {
+ }
+ }
+ };
+ mDnsResolverThreadPool.execute (task);
+ }
+ }
+ if(!mDnsResolverThreadPoolRunning || (dnsQueryCounter >= MAX_PARALLEL_DNS_QUERIES_PER_PAGE)) {
+ mHostNamesToBeResolved.clear();
+ }
+ bResolvedPriorityHostNames = (bResolvedPriorityHostNames) ? false:true;
+ }
+ }
+ }
+ mDnsResolverThreadPool.shutdown();
+ sDnsResolver = null;
+ }
+ };
+ Thread dnsResolver = new Thread(startDnsResolver);
+ dnsResolver.setName("DNS resolver");
+ dnsResolver.start();
+ }
+
+ public synchronized void destroyDnsResolver() {
+ --mDnsResolverRefCount;
+ if(mDnsResolverRefCount == 0) {
+ mShutDownInProgress = true;
+ mDnsResolverThreadPoolRunning = false;
+ synchronized(mThreadPoolLock) {
+ mThreadPoolLock.notifyAll();
+ }
+ }
+ }
+
+ public void resolveDnsForHost(String hostName, String priority) {
+ if(hostName == null) {
+ return;
+ }
+ synchronized(mHostNamesToBeResolved) {
+ if(mHostNamesToBeResolved.size() > 0 ) {
+ return;
+ }
+ mHostNamesToBeResolved.put(hostName,priority);
+ }
+ resumeDnsResolverThreadPool();
+ }
+
+ public void resolveDnsForHostMap(HashMap hostMap) {
+ if(hostMap == null) {
+ return;
+ }
+ synchronized (mHostNamesToBeResolved) {
+ mHostNamesToBeResolved.putAll(hostMap);
+ }
+ resumeDnsResolverThreadPool();
+ }
+
+ /* pause will flush all pending DNS queries at the DNS resolver */
+ public void pauseDnsResolverThreadPool() {
+ mDnsResolverThreadPoolRunning = false;
+ }
+
+ /* resume will start DNS resolver executing DNS queries */
+ public void resumeDnsResolverThreadPool() {
+ mDnsResolverThreadPoolRunning = true;
+ synchronized(mThreadPoolLock) {
+ mThreadPoolLock.notifyAll();
+ }
+ }
+
+ /* returns the max number of DNS queries that can be made in background for a page */
+ public int getMaxParallelDnsQueryPerPage() {
+ return MAX_PARALLEL_DNS_QUERIES_PER_PAGE;
+ }
+}
diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java index eda2e72..20b987c 100644 --- a/core/java/android/webkit/HTML5VideoViewProxy.java +++ b/core/java/android/webkit/HTML5VideoViewProxy.java @@ -345,7 +345,7 @@ class HTML5VideoViewProxy extends Handler // Start the download. Called on WebCore thread. public void start() { retainQueue(); - mRequestHandle = mRequestQueue.queueRequest(mUrl, "GET", null, this, null, 0); + mRequestHandle = mRequestQueue.queueRequest(mUrl, "GET", null, this, null, 0, -1, false); } // Cancel the download if active and release the queue. Called on WebCore thread. public void cancelAndReleaseQueue() { diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java index e6fa405..3cdd86b 100644 --- a/core/java/android/webkit/LoadListener.java +++ b/core/java/android/webkit/LoadListener.java @@ -131,6 +131,9 @@ class LoadListener extends Handler implements EventHandler { private final String mUsername; private final String mPassword; + private int mPriority = -1; + private boolean mCommit = true; + // ========================================================================= // Public functions // ========================================================================= @@ -183,6 +186,22 @@ class LoadListener extends Handler implements EventHandler { mNativeLoader = 0; } + public void setPriority(int pri) { + mPriority = pri; + } + + public int priority() { + return mPriority; + } + + public void setShouldCommit(boolean commit) { + mCommit = commit; + } + + public boolean shouldCommit() { + return mCommit; + } + /* * This message handler is to facilitate communication between the network * thread and the browser thread. @@ -1242,6 +1261,24 @@ class LoadListener extends Handler implements EventHandler { } /** + * native callback + * Propagates the priority to the network stack + */ + void setPriority(String url, int pri) { + Network net = Network.getInstance(mContext); + net.setPriority(url, pri); + } + + /** + * native callback + * Commits the priorities set on the network stack + */ + void commitPriorities() { + Network net = Network.getInstance(mContext); + net.commitPriorities(); + } + + /** * Cancel a request. * FIXME: This will only work if the request has yet to be handled. This * is in no way guarenteed if requests are served in a separate thread. diff --git a/core/java/android/webkit/Network.java b/core/java/android/webkit/Network.java index 598f20d..accbebb 100644 --- a/core/java/android/webkit/Network.java +++ b/core/java/android/webkit/Network.java @@ -17,6 +17,8 @@ package android.webkit; import android.content.Context; +import android.net.WebAddress; +import android.net.ParseException; import android.net.http.*; import android.os.*; import android.util.Log; @@ -142,6 +144,25 @@ class Network { mRequestQueue = new RequestQueue(context); } + public boolean setPriority(String url, int priority) { + WebAddress uri = null; + if (URLUtil.isNetworkUrl(url)) { + String nurl = URLUtil.stripAnchor(url); + try { + uri = new WebAddress(nurl); + } catch (ParseException e) { + e.printStackTrace(); + } + } + if (uri != null) + return mRequestQueue.setRequestPriority(uri, priority); + return false; + } + + public void commitPriorities() { + mRequestQueue.commitRequestPriorities(); + } + /** * Request a url from either the network or the file system. * @param url The url to load. @@ -189,7 +210,8 @@ class Network { loader.loadSynchronousMessages(); } else { handle = q.queueRequest(url, loader.getWebAddress(), method, - headers, loader, bodyProvider, bodyLength); + headers, loader, bodyProvider, bodyLength, + loader.priority(), loader.shouldCommit()); // FIXME: Although this is probably a rare condition, normal network // requests are processed in a separate thread. This means that it // is possible to process part of the request before setting the diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index b767f11..cfb6b92 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -424,6 +424,14 @@ public class WebSettings { public boolean getNavDump() { return mNavDump; } + + /** + * Whether or not to display zoom controls + * @hide + */ + public void showZoomControls(boolean value) { + mWebView.showZoomControls(value); + } /** * Set whether the WebView supports zoom diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 2df250d..f3ddbb6 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2006 The Android Open Source Project + * Copyright (c) 2010, Code Aurora Forum. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,6 +47,7 @@ import android.os.Handler; import android.os.Message; import android.os.ServiceManager; import android.os.SystemClock; +import android.provider.Settings; import android.text.IClipboard; import android.text.Selection; import android.text.Spannable; @@ -774,6 +776,9 @@ public class WebView extends AbsoluteLayout public static final String SCHEME_GEO = "geo:0,0?q="; private int mBackgroundColor = Color.WHITE; + + //Wysie + private boolean showZoomControls = true; // Used to notify listeners of a new picture. private PictureListener mPictureListener; @@ -945,6 +950,11 @@ public class WebView extends AbsoluteLayout updateMultiTouchSupport(context); } + + //Wysie + void showZoomControls(boolean value) { + showZoomControls = value; + } void updateMultiTouchSupport(Context context) { WebSettings settings = getSettings(); @@ -961,17 +971,21 @@ public class WebView extends AbsoluteLayout private void updateZoomButtonsEnabled() { if (mZoomButtonsController == null) return; - boolean canZoomIn = mActualScale < mMaxZoomScale; - boolean canZoomOut = mActualScale > mMinZoomScale && !mInZoomOverview; - if (!canZoomIn && !canZoomOut) { - // Hide the zoom in and out buttons, as well as the fit to page - // button, if the page cannot zoom + if (!showZoomControls) { mZoomButtonsController.getZoomControls().setVisibility(View.GONE); } else { - // Set each one individually, as a page may be able to zoom in - // or out. - mZoomButtonsController.setZoomInEnabled(canZoomIn); - mZoomButtonsController.setZoomOutEnabled(canZoomOut); + boolean canZoomIn = mActualScale < mMaxZoomScale; + boolean canZoomOut = mActualScale > mMinZoomScale && !mInZoomOverview; + if (!canZoomIn && !canZoomOut) { + // Hide the zoom in and out buttons, as well as the fit to page + // button, if the page cannot zoom + mZoomButtonsController.getZoomControls().setVisibility(View.GONE); + } else { + // Set each one individually, as a page may be able to zoom in + // or out. + mZoomButtonsController.setZoomInEnabled(canZoomIn); + mZoomButtonsController.setZoomOutEnabled(canZoomOut); + } } } @@ -1285,6 +1299,13 @@ public class WebView extends AbsoluteLayout } /** + * @hide pending API council approval. + */ + public void startDnsPrefetch() { + mWebViewCore.sendMessage(EventHub.START_DNS_PREFETCH); + } + + /** * Inform WebView of the network state. This is used to set * the javascript property window.navigator.isOnline and * generates the online/offline event as specified in HTML5, sec. 5.7.7 @@ -4601,6 +4622,10 @@ public class WebView extends AbsoluteLayout boolean reflowNow = (mActualScale - mMinZoomScale <= MINIMUM_SCALE_INCREMENT) || ((mActualScale <= 0.8 * mTextWrapScale)); + if(mActualScale > mTextWrapScale) { + reflowNow |= Settings.System.getInt(mContext.getContentResolver(), + Settings.System.WEB_VIEW_PINCH_REFLOW, 0) != 0; + } // force zoom after mPreviewZoomOnly is set to false so that the // new view size will be passed to the WebKit setNewZoomScale(mActualScale, reflowNow, true); diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 4118119..68c0fb8 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2007 The Android Open Source Project + * Copyright (c) 2010, Code Aurora Forum. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -197,6 +198,8 @@ final class WebViewCore { // WebCore thread handler. mEventHub.transferMessages(); + DnsResolver.createDnsResolver(); + // Send a message back to WebView to tell it that we have set up the // WebCore thread. if (mWebView != null) { @@ -771,6 +774,7 @@ final class WebViewCore { "ON_RESUME", // = 144 "FREE_MEMORY", // = 145 "VALID_NODE_BOUNDS", // = 146 + "START_DNS_PREFETCH", // = 151 }; class EventHub { @@ -839,6 +843,7 @@ final class WebViewCore { // Network-based messaging static final int CLEAR_SSL_PREF_TABLE = 150; + static final int START_DNS_PREFETCH = 151; // Test harness messages static final int REQUEST_EXT_REPRESENTATION = 160; @@ -944,6 +949,7 @@ final class WebViewCore { break; case LOAD_URL: { + DnsResolver.getInstance().pauseDnsResolverThreadPool(); GetUrlData param = (GetUrlData) msg.obj; loadUrl(param.mUrl, param.mExtraHeaders); break; @@ -1000,6 +1006,7 @@ final class WebViewCore { break; case RELOAD: + DnsResolver.getInstance().pauseDnsResolverThreadPool(); mBrowserFrame.reload(false); break; @@ -1043,6 +1050,7 @@ final class WebViewCore { if (!mBrowserFrame.committed() && msg.arg1 == -1 && (mBrowserFrame.loadType() == BrowserFrame.FRAME_LOADTYPE_STANDARD)) { + DnsResolver.getInstance().pauseDnsResolverThreadPool(); mBrowserFrame.reload(true); } else { mBrowserFrame.goBackOrForward(msg.arg1); @@ -1357,6 +1365,10 @@ final class WebViewCore { BrowserFrame.sJavaBridge.removePackageName( (String) msg.obj); break; + + case START_DNS_PREFETCH: + mBrowserFrame.startDnsPrefetch(); + break; } } }; @@ -1544,6 +1556,10 @@ final class WebViewCore { } mEventHub.blockMessages(); } + + DnsResolver r = DnsResolver.getInstance(); + if (r != null) + r.destroyDnsResolver(); } //------------------------------------------------------------------------- diff --git a/core/java/android/widget/TabHost.java b/core/java/android/widget/TabHost.java index 02cd6a8..e0f1583 100644 --- a/core/java/android/widget/TabHost.java +++ b/core/java/android/widget/TabHost.java @@ -51,6 +51,14 @@ public class TabHost extends FrameLayout implements ViewTreeObserver.OnTouchMode * {@hide} */ protected int mCurrentTab = -1; + /** + * This field when set to true ensures that first tab is not set as the current tab + * by preventing the addTab() from calling setCurrentTab() with index set to 0. + * To ensure that the operations of an application are not changed, it should be set to + * false whenever mCurrenttab is set to -1. + * {@hide} + */ + private boolean mAvoidFirstTabLoad = false; private View mCurrentView = null; /** * This field should be made private, so it is hidden from the SDK. @@ -75,6 +83,7 @@ public class TabHost extends FrameLayout implements ViewTreeObserver.OnTouchMode setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); mCurrentTab = -1; + mAvoidFirstTabLoad = false; mCurrentView = null; } @@ -209,7 +218,9 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1"); mTabWidget.addView(tabIndicator); mTabSpecs.add(tabSpec); - if (mCurrentTab == -1) { + // The second test is always true except when an application explicitly sets + // mAvoidFirstTabLoad to true. + if (mCurrentTab == -1 && !mAvoidFirstTabLoad) { setCurrentTab(0); } } @@ -252,7 +263,18 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1"); public View getCurrentView() { return mCurrentView; } - + /** + * {@hide} + */ + public void setAvoidFirstTabLoad(boolean flag) { + mAvoidFirstTabLoad = flag; + } + /** + * {@hide} + */ + public void setCurrentTabToZero() { + mCurrentTab = 0; + } public void setCurrentTabByTag(String tag) { int i; for (i = 0; i < mTabSpecs.size(); i++) { diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 64c9c99..2b37887 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -2778,7 +2778,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public void drawText(Canvas c, int start, int end, float x, float y, Paint p) { - c.drawText(mChars, start + mStart, end - start, x, y, p); + c.drawText(mChars, start + mStart, end - start, x, y, p,false); } public float measureText(int start, int end, Paint p) { diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java index 107b145..4a0617c 100644 --- a/core/java/com/android/internal/app/AlertController.java +++ b/core/java/com/android/internal/app/AlertController.java @@ -435,6 +435,7 @@ public class AlertController { View titleTemplate = mWindow.findViewById(R.id.title_template); titleTemplate.setVisibility(View.GONE); mIconView.setVisibility(View.GONE); + topPanel.setVisibility(View.GONE); hasTitle = false; } } diff --git a/core/java/com/android/internal/app/ShutdownThread.java b/core/java/com/android/internal/app/ShutdownThread.java index a96253b..310ae99 100644 --- a/core/java/com/android/internal/app/ShutdownThread.java +++ b/core/java/com/android/internal/app/ShutdownThread.java @@ -94,17 +94,33 @@ public final class ShutdownThread extends Thread { Log.d(TAG, "Notifying thread to start radio shutdown"); if (confirm) { - final AlertDialog dialog = new AlertDialog.Builder(context) - .setIcon(android.R.drawable.ic_dialog_alert) - .setTitle(com.android.internal.R.string.power_off) - .setMessage(com.android.internal.R.string.shutdown_confirm) - .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - beginShutdownSequence(context); - } - }) - .setNegativeButton(com.android.internal.R.string.no, null) - .create(); + final AlertDialog dialog; + // Set different dialog message based on whether or not we're rebooting + if (mReboot) { + dialog = new AlertDialog.Builder(context) + .setIcon(android.R.drawable.ic_dialog_alert) + .setTitle(com.android.internal.R.string.reboot_system) + .setMessage(com.android.internal.R.string.reboot_confirm) + .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + beginShutdownSequence(context); + } + }) + .setNegativeButton(com.android.internal.R.string.no, null) + .create(); + } else { + dialog = new AlertDialog.Builder(context) + .setIcon(android.R.drawable.ic_dialog_alert) + .setTitle(com.android.internal.R.string.power_off) + .setMessage(com.android.internal.R.string.shutdown_confirm) + .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + beginShutdownSequence(context); + } + }) + .setNegativeButton(com.android.internal.R.string.no, null) + .create(); + } dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); if (!context.getResources().getBoolean( com.android.internal.R.bool.config_sf_slowBlur)) { @@ -139,8 +155,13 @@ public final class ShutdownThread extends Thread { // throw up an indeterminate system dialog to indicate radio is // shutting down. ProgressDialog pd = new ProgressDialog(context); - pd.setTitle(context.getText(com.android.internal.R.string.power_off)); - pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress)); + if (mReboot) { + pd.setTitle(context.getText(com.android.internal.R.string.reboot_system)); + pd.setMessage(context.getText(com.android.internal.R.string.reboot_progress)); + } else { + pd.setTitle(context.getText(com.android.internal.R.string.power_off)); + pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress)); + } pd.setIndeterminate(true); pd.setCancelable(false); pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index aadb576..55b25f6 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -844,7 +844,7 @@ public final class BatteryStatsImpl extends BatteryStats { private final Map<String, KernelWakelockStats> readKernelWakelockStats() { - byte[] buffer = new byte[4096]; + byte[] buffer = new byte[8192]; int len; try { @@ -891,9 +891,11 @@ public final class BatteryStatsImpl extends BatteryStats { for (endIndex=startIndex; endIndex < len && wlBuffer[endIndex] != '\n' && wlBuffer[endIndex] != '\0'; endIndex++); - // Don't go over the end of the buffer - if (endIndex < len) { - endIndex++; // endIndex is an exclusive upper bound. + endIndex++; // endIndex is an exclusive upper bound. + // Don't go over the end of the buffer, Process.parseProcLine might + // write to wlBuffer[endIndex] + if (endIndex >= (len - 1) ) { + return m; } String[] nameStringArray = mProcWakelocksName; @@ -1224,6 +1226,8 @@ public final class BatteryStatsImpl extends BatteryStats { if (mPhoneSignalStrengthBin != bin) { if (mPhoneSignalStrengthBin >= 0) { mPhoneSignalStrengthsTimer[mPhoneSignalStrengthBin].stopRunningLocked(this); + } else { + mPhoneSignalStrengthsTimer[SIGNAL_STRENGTH_NONE_OR_UNKNOWN].stopRunningLocked(this); } mPhoneSignalStrengthBin = bin; mPhoneSignalStrengthsTimer[bin].startRunningLocked(this); diff --git a/core/java/com/android/internal/os/SamplingProfilerIntegration.java b/core/java/com/android/internal/os/SamplingProfilerIntegration.java index 5f5c7a4..127fb23 100644 --- a/core/java/com/android/internal/os/SamplingProfilerIntegration.java +++ b/core/java/com/android/internal/os/SamplingProfilerIntegration.java @@ -85,7 +85,8 @@ public class SamplingProfilerIntegration { pending = true; snapshotWriter.execute(new Runnable() { public void run() { - String dir = "/sdcard/snapshots"; + String dir = + Environment.getExternalStorageDirectory().getPath() + "/snapshots"; if (!dirMade) { new File(dir).mkdirs(); if (new File(dir).isDirectory()) { diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index dbbd286..b227d67 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -90,7 +90,7 @@ public class LockPatternUtils { private final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline"; private final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen"; public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type"; - private final static String LOCK_PASSWORD_SALT_KEY = "lockscreen.password_salt"; + private final static String LOCK_PASSWORD_SALT_KEY = "lockscreen.password_salt"; private final Context mContext; private final ContentResolver mContentResolver; @@ -572,6 +572,62 @@ public class LockPatternUtils { public void setTactileFeedbackEnabled(boolean enabled) { setBoolean(Settings.Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED, enabled); } + + public void setVisibleDotsEnabled(boolean enabled) { + setBoolean(Settings.Secure.LOCK_DOTS_VISIBLE, enabled); + } + + public boolean isVisibleDotsEnabled() { + return getBoolean(Settings.Secure.LOCK_DOTS_VISIBLE, true); + } + + public void setShowErrorPath(boolean enabled) { + setBoolean(Settings.Secure.LOCK_SHOW_ERROR_PATH, enabled); + } + + public boolean isShowErrorPath() { + return getBoolean(Settings.Secure.LOCK_SHOW_ERROR_PATH, true); + } + + public void setShowCustomMsg(boolean enabled) { + setBoolean(Settings.Secure.LOCK_SHOW_CUSTOM_MSG, enabled); + } + + public boolean isShowCustomMsg() { + return getBoolean(Settings.Secure.LOCK_SHOW_CUSTOM_MSG, false); + } + + public void setCustomMsg(String msg) { + setString(Settings.Secure.LOCK_CUSTOM_MSG, msg); + } + + public String getCustomMsg() { + return getString(Settings.Secure.LOCK_CUSTOM_MSG); + } + + public int getIncorrectDelay() { + return getInt(Settings.Secure.LOCK_INCORRECT_DELAY, 2000); + } + + public void setIncorrectDelay(int delay) { + setInt(Settings.Secure.LOCK_INCORRECT_DELAY, delay); + } + + public void setShowUnlockMsg(boolean enabled) { + setBoolean(Settings.Secure.SHOW_UNLOCK_TEXT, enabled); + } + + public boolean isShowUnlockMsg() { + return getBoolean(Settings.Secure.SHOW_UNLOCK_TEXT, true); + } + + public void setShowUnlockErrMsg(boolean enabled) { + setBoolean(Settings.Secure.SHOW_UNLOCK_ERR_TEXT, enabled); + } + + public boolean isShowUnlockErrMsg() { + return getBoolean(Settings.Secure.SHOW_UNLOCK_ERR_TEXT, true); + } /** * Set and store the lockout deadline, meaning the user can't attempt his/her unlock @@ -636,6 +692,13 @@ public class LockPatternUtils { return 1 == android.provider.Settings.Secure.getInt(mContentResolver, secureSettingKey, 0); } + + private boolean getBoolean(String systemSettingKey, boolean defaultValue) { + return 1 == + android.provider.Settings.Secure.getInt( + mContentResolver, + systemSettingKey, defaultValue ? 1 : 0); + } private void setBoolean(String secureSettingKey, boolean enabled) { android.provider.Settings.Secure.putInt(mContentResolver, secureSettingKey, @@ -649,6 +712,27 @@ public class LockPatternUtils { private void setLong(String secureSettingKey, long value) { android.provider.Settings.Secure.putLong(mContentResolver, secureSettingKey, value); } + + private int getInt(String systemSettingKey, int def) { + return android.provider.Settings.Secure.getInt(mContentResolver, systemSettingKey, def); + } + + private void setInt(String systemSettingKey, int value) { + android.provider.Settings.Secure.putInt(mContentResolver, systemSettingKey, value); + } + + private String getString(String systemSettingKey) { + String s = android.provider.Settings.Secure.getString(mContentResolver, systemSettingKey); + + if (s == null) + return ""; + + return s; + } + + private void setString(String systemSettingKey, String value) { + android.provider.Settings.Secure.putString(mContentResolver, systemSettingKey, value); + } public boolean isSecure() { long mode = getKeyguardStoredPasswordQuality(); diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java index 007e7b9..8ae5a2f 100644 --- a/core/java/com/android/internal/widget/LockPatternView.java +++ b/core/java/com/android/internal/widget/LockPatternView.java @@ -101,6 +101,10 @@ public class LockPatternView extends View { private boolean mInStealthMode = false; private boolean mTactileFeedbackEnabled = true; private boolean mPatternInProgress = false; + + private boolean mVisibleDots = true; + private boolean mShowErrorPath = true; + private int mDelay = 2000; private float mDiameterFactor = 0.5f; private float mHitFactor = 0.6f; @@ -332,6 +336,22 @@ public class LockPatternView extends View { public void setInStealthMode(boolean inStealthMode) { mInStealthMode = inStealthMode; } + + public void setVisibleDots(boolean visibleDots) { + mVisibleDots = visibleDots; + } + + public boolean isVisibleDots() { + return mVisibleDots; + } + + public void setShowErrorPath(boolean showErrorPath) { + mShowErrorPath = showErrorPath; + } + + public boolean isShowErrorPath() { + return mShowErrorPath; + } /** * Set whether the view will use tactile feedback. If true, there will be @@ -433,6 +453,14 @@ public class LockPatternView extends View { public void enableInput() { mInputEnabled = true; } + + public int getIncorrectDelay() { + return mDelay; + } + + public void setIncorrectDelay(int delay) { + mDelay = delay; + } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { @@ -838,7 +866,10 @@ public class LockPatternView extends View { // only the last segment of the path should be computed here // draw the path of the pattern (unless the user is in progress, and // we are in stealth mode) - final boolean drawPath = (!mInStealthMode || mPatternDisplayMode == DisplayMode.Wrong); + final boolean drawPath = ((!mInStealthMode && mPatternDisplayMode != DisplayMode.Wrong) + || (mPatternDisplayMode == DisplayMode.Wrong && mShowErrorPath)); + + //final boolean drawPath = (!mInStealthMode || mPatternDisplayMode == DisplayMode.Wrong); if (drawPath) { boolean anyCircles = false; for (int i = 0; i < count; i++) { @@ -872,13 +903,15 @@ public class LockPatternView extends View { // draw the circles final int paddingTop = mPaddingTop; final int paddingLeft = mPaddingLeft; - - for (int i = 0; i < 3; i++) { - float topY = paddingTop + i * squareHeight; - //float centerY = mPaddingTop + i * mSquareHeight + (mSquareHeight / 2); - for (int j = 0; j < 3; j++) { - float leftX = paddingLeft + j * squareWidth; - drawCircle(canvas, (int) leftX, (int) topY, drawLookup[i][j]); + + if (mVisibleDots) { + for (int i = 0; i < 3; i++) { + float topY = paddingTop + i * squareHeight; + //float centerY = mPaddingTop + i * mSquareHeight + (mSquareHeight / 2); + for (int j = 0; j < 3; j++) { + float leftX = paddingLeft + j * squareWidth; + drawCircle(canvas, (int) leftX, (int) topY, drawLookup[i][j]); + } } } @@ -959,8 +992,15 @@ public class LockPatternView extends View { innerCircle = mBitmapBtnTouched; } else if (mPatternDisplayMode == DisplayMode.Wrong) { // the pattern is wrong - outerCircle = mBitmapCircleRed; - innerCircle = mBitmapBtnDefault; + + if (mShowErrorPath) { + outerCircle = mBitmapCircleRed; + innerCircle = mBitmapBtnDefault; + } + else { + outerCircle = mBitmapCircleDefault; + innerCircle = mBitmapBtnDefault; + } } else if (mPatternDisplayMode == DisplayMode.Correct || mPatternDisplayMode == DisplayMode.Animate) { // the pattern is correct @@ -989,7 +1029,7 @@ public class LockPatternView extends View { return new SavedState(superState, LockPatternUtils.patternToString(mPattern), mPatternDisplayMode.ordinal(), - mInputEnabled, mInStealthMode, mTactileFeedbackEnabled); + mInputEnabled, mInStealthMode, mTactileFeedbackEnabled, mVisibleDots, mShowErrorPath); } @Override @@ -1003,6 +1043,8 @@ public class LockPatternView extends View { mInputEnabled = ss.isInputEnabled(); mInStealthMode = ss.isInStealthMode(); mTactileFeedbackEnabled = ss.isTactileFeedbackEnabled(); + mVisibleDots = ss.isVisibleDots(); + mShowErrorPath = ss.isShowErrorPath(); } /** @@ -1015,18 +1057,22 @@ public class LockPatternView extends View { private final boolean mInputEnabled; private final boolean mInStealthMode; private final boolean mTactileFeedbackEnabled; + private final boolean mVisibleDots; + private final boolean mShowErrorPath; /** * Constructor called from {@link LockPatternView#onSaveInstanceState()} */ private SavedState(Parcelable superState, String serializedPattern, int displayMode, - boolean inputEnabled, boolean inStealthMode, boolean tactileFeedbackEnabled) { + boolean inputEnabled, boolean inStealthMode, boolean tactileFeedbackEnabled, boolean visibleDots, boolean showErrorPath) { super(superState); mSerializedPattern = serializedPattern; mDisplayMode = displayMode; mInputEnabled = inputEnabled; mInStealthMode = inStealthMode; mTactileFeedbackEnabled = tactileFeedbackEnabled; + mVisibleDots = visibleDots; + mShowErrorPath = showErrorPath; } /** @@ -1039,6 +1085,8 @@ public class LockPatternView extends View { mInputEnabled = (Boolean) in.readValue(null); mInStealthMode = (Boolean) in.readValue(null); mTactileFeedbackEnabled = (Boolean) in.readValue(null); + mVisibleDots = (Boolean) in.readValue(null); + mShowErrorPath = (Boolean) in.readValue(null); } public String getSerializedPattern() { @@ -1060,6 +1108,14 @@ public class LockPatternView extends View { public boolean isTactileFeedbackEnabled(){ return mTactileFeedbackEnabled; } + + public boolean isVisibleDots() { + return mVisibleDots; + } + + public boolean isShowErrorPath() { + return mShowErrorPath; + } @Override public void writeToParcel(Parcel dest, int flags) { @@ -1069,6 +1125,8 @@ public class LockPatternView extends View { dest.writeValue(mInputEnabled); dest.writeValue(mInStealthMode); dest.writeValue(mTactileFeedbackEnabled); + dest.writeValue(mVisibleDots); + dest.writeValue(mShowErrorPath); } public static final Parcelable.Creator<SavedState> CREATOR = diff --git a/core/java/com/google/android/mms/pdu/PduParser.java b/core/java/com/google/android/mms/pdu/PduParser.java index 131ac51..4ca92e3 100644 --- a/core/java/com/google/android/mms/pdu/PduParser.java +++ b/core/java/com/google/android/mms/pdu/PduParser.java @@ -163,6 +163,13 @@ public class PduParser { // or "application/vnd.wap.multipart.related" // or "application/vnd.wap.multipart.alternative" return retrieveConf; + } else if (ctTypeStr.equals(ContentType.MULTIPART_ALTERNATIVE)) { + // "application/vnd.wap.multipart.alternative" + // should take only the first part. + PduPart firstPart = mBody.getPart(0); + mBody.removeAll(); + mBody.addPart(0, firstPart); + return retrieveConf; } return null; case PduHeaders.MESSAGE_TYPE_DELIVERY_IND: @@ -202,7 +209,18 @@ public class PduParser { PduHeaders headers = new PduHeaders(); while (keepParsing && (pduDataStream.available() > 0)) { + pduDataStream.mark(1); int headerField = extractByteValue(pduDataStream); + /* parse custom text header */ + if ((headerField >= TEXT_MIN) && (headerField <= TEXT_MAX)) { + pduDataStream.reset(); + byte [] bVal = parseWapString(pduDataStream, TYPE_TEXT_STRING); + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "TextHeader: " + new String(bVal)); + } + /* we should ignore it at the moment */ + continue; + } switch (headerField) { case PduHeaders.MESSAGE_TYPE: { @@ -780,26 +798,34 @@ public class PduParser { /* get part's data */ if (dataLength > 0) { byte[] partData = new byte[dataLength]; + String partContentType = new String(part.getContentType()); pduDataStream.read(partData, 0, dataLength); - // Check Content-Transfer-Encoding. - byte[] partDataEncoding = part.getContentTransferEncoding(); - if (null != partDataEncoding) { - String encoding = new String(partDataEncoding); - if (encoding.equalsIgnoreCase(PduPart.P_BASE64)) { - // Decode "base64" into "binary". - partData = Base64.decodeBase64(partData); - } else if (encoding.equalsIgnoreCase(PduPart.P_QUOTED_PRINTABLE)) { - // Decode "quoted-printable" into "binary". - partData = QuotedPrintable.decodeQuotedPrintable(partData); - } else { - // "binary" is the default encoding. + if (partContentType.equalsIgnoreCase(ContentType.MULTIPART_ALTERNATIVE)) { + // parse "multipart/vnd.wap.multipart.alternative". + PduBody childBody = parseParts(new ByteArrayInputStream(partData)); + // take the first part of children. + part = childBody.getPart(0); + } else { + // Check Content-Transfer-Encoding. + byte[] partDataEncoding = part.getContentTransferEncoding(); + if (null != partDataEncoding) { + String encoding = new String(partDataEncoding); + if (encoding.equalsIgnoreCase(PduPart.P_BASE64)) { + // Decode "base64" into "binary". + partData = Base64.decodeBase64(partData); + } else if (encoding.equalsIgnoreCase(PduPart.P_QUOTED_PRINTABLE)) { + // Decode "quoted-printable" into "binary". + partData = QuotedPrintable.decodeQuotedPrintable(partData); + } else { + // "binary" is the default encoding. + } } + if (null == partData) { + log("Decode part data error!"); + return null; + } + part.setData(partData); } - if (null == partData) { - log("Decode part data error!"); - return null; - } - part.setData(partData); } /* add this part to body */ @@ -1531,18 +1557,36 @@ public class PduParser { * Attachment = <Octet 129> * Inline = <Octet 130> */ - int len = parseValueLength(pduDataStream); + int len = -1; + boolean validDispositionLength = true; + pduDataStream.mark(1); + + try { + len = parseValueLength(pduDataStream); + } catch (RuntimeException e) { + // tolerate invalid content-disposition length + len = 31; + validDispositionLength = false; + pduDataStream.reset(); + } + pduDataStream.mark(1); int thisStartPos = pduDataStream.available(); int thisEndPos = 0; int value = pduDataStream.read(); - - if (value == PduPart.P_DISPOSITION_FROM_DATA ) { - part.setContentDisposition(PduPart.DISPOSITION_FROM_DATA); - } else if (value == PduPart.P_DISPOSITION_ATTACHMENT) { - part.setContentDisposition(PduPart.DISPOSITION_ATTACHMENT); - } else if (value == PduPart.P_DISPOSITION_INLINE) { - part.setContentDisposition(PduPart.DISPOSITION_INLINE); + + if (validDispositionLength) { + if (value == PduPart.P_DISPOSITION_FROM_DATA ) { + part.setContentDisposition(PduPart.DISPOSITION_FROM_DATA); + } else if (value == PduPart.P_DISPOSITION_ATTACHMENT) { + part.setContentDisposition(PduPart.DISPOSITION_ATTACHMENT); + } else if (value == PduPart.P_DISPOSITION_INLINE) { + part.setContentDisposition(PduPart.DISPOSITION_INLINE); + } else { + pduDataStream.reset(); + /* Token-text */ + part.setContentDisposition(parseWapString(pduDataStream, TYPE_TEXT_STRING)); + } } else { pduDataStream.reset(); /* Token-text */ diff --git a/core/jni/Android.mk b/core/jni/Android.mk index df1ab9e..bd0f307 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -120,6 +120,7 @@ LOCAL_SRC_FILES:= \ android_server_BluetoothService.cpp \ android_server_BluetoothEventLoop.cpp \ android_server_BluetoothA2dpService.cpp \ + android_server_BluetoothHidService.cpp \ android_server_Watchdog.cpp \ android_message_digest_sha1.cpp \ android_ddm_DdmHandleNativeHeap.cpp \ @@ -162,8 +163,6 @@ LOCAL_SHARED_LIBRARIES := \ libbinder \ libnetutils \ libui \ - libsurfaceflinger_client \ - libcamera_client \ libskiagl \ libskia \ libsqlite \ @@ -184,6 +183,12 @@ LOCAL_SHARED_LIBRARIES := \ libwpa_client \ libjpeg +ifneq ($(BOARD_USES_ECLAIR_LIBCAMERA),true) + LOCAL_SHARED_LIBRARIES += \ + libsurfaceflinger_client \ + libcamera_client +endif + ifeq ($(BOARD_HAVE_BLUETOOTH),true) LOCAL_C_INCLUDES += \ external/dbus \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index d38d748..682f78e 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -153,6 +153,7 @@ extern int register_android_bluetooth_ScoSocket(JNIEnv *env); extern int register_android_server_BluetoothService(JNIEnv* env); extern int register_android_server_BluetoothEventLoop(JNIEnv *env); extern int register_android_server_BluetoothA2dpService(JNIEnv* env); +extern int register_android_server_BluetoothHidService(JNIEnv* env); extern int register_android_server_Watchdog(JNIEnv* env); extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env); extern int register_com_android_internal_os_ZygoteInit(JNIEnv* env); @@ -555,7 +556,10 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) } } - property_get("dalvik.vm.execution-mode", propBuf, ""); + property_get("persist.sys.jit-mode", propBuf, ""); + if (strcmp(propBuf, "") == 0) { + property_get("dalvik.vm.execution-mode", propBuf, ""); + } if (strcmp(propBuf, "int:portable") == 0) { executionMode = kEMIntPortable; } else if (strcmp(propBuf, "int:fast") == 0) { @@ -603,8 +607,12 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) //options[curOpt++].optionString = "-verbose:class"; strcpy(heapsizeOptsBuf, "-Xmx"); - property_get("dalvik.vm.heapsize", heapsizeOptsBuf+4, "16m"); - //LOGI("Heap size: %s", heapsizeOptsBuf); + property_get("persist.sys.vm.heapsize", propBuf, ""); + if (strcmp(propBuf, "") == 0) { + property_get("dalvim.vm.heapsize", propBuf, "16m"); + } + strcpy(heapsizeOptsBuf+4, propBuf); + LOGI("Heap size: %s", heapsizeOptsBuf); opt.optionString = heapsizeOptsBuf; mOptions.add(opt); @@ -1277,6 +1285,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_server_BluetoothService), REG_JNI(register_android_server_BluetoothEventLoop), REG_JNI(register_android_server_BluetoothA2dpService), + REG_JNI(register_android_server_BluetoothHidService), REG_JNI(register_android_server_Watchdog), REG_JNI(register_android_message_digest_sha1), REG_JNI(register_android_ddm_DdmHandleNativeHeap), diff --git a/core/jni/CursorWindow.cpp b/core/jni/CursorWindow.cpp index 7877921..f089583 100644 --- a/core/jni/CursorWindow.cpp +++ b/core/jni/CursorWindow.cpp @@ -18,9 +18,12 @@ #define LOG_TAG "CursorWindow" #include <utils/Log.h> +#ifdef USE_ECLAIR_MEMORYDEALER +#include <binder/MemoryDealer.h> +#else #include <binder/MemoryHeapBase.h> #include <binder/MemoryBase.h> - +#endif #include <assert.h> #include <string.h> #include <stdlib.h> @@ -38,7 +41,11 @@ CursorWindow::CursorWindow(size_t maxSize) : { } +#ifdef USE_ECLAIR_MEMORYDEALER +bool CursorWindow::setMemory(sp<IMemory> memory) +#else bool CursorWindow::setMemory(const sp<IMemory>& memory) +#endif { mMemory = memory; mData = (uint8_t *) memory->pointer(); @@ -48,6 +55,9 @@ bool CursorWindow::setMemory(const sp<IMemory>& memory) mHeader = (window_header_t *) mData; // Make the window read-only +#ifdef USE_ECLAIR_MEMORYDEALER + mHeap = NULL; +#endif ssize_t size = memory->size(); mSize = size; mMaxSize = size; @@ -59,11 +69,16 @@ LOG_WINDOW("Created CursorWindow from existing IMemory: mFreeOffset = %d, numRow bool CursorWindow::initBuffer(bool localOnly) { //TODO Use a non-memory dealer mmap region for localOnly - +#ifdef USE_ECLAIR_MEMORYDEALER + mHeap = new MemoryDealer(new SharedHeap(mMaxSize, 0, "CursorWindow")); + if (mHeap != NULL) { + mMemory = mHeap->allocate(mMaxSize); +#else sp<MemoryHeapBase> heap; heap = new MemoryHeapBase(mMaxSize, 0, "CursorWindow"); if (heap != NULL) { mMemory = new MemoryBase(heap, 0, mMaxSize); +#endif if (mMemory != NULL) { mData = (uint8_t *) mMemory->pointer(); if (mData) { diff --git a/core/jni/CursorWindow.h b/core/jni/CursorWindow.h index 3fcb560..f5d791a 100644 --- a/core/jni/CursorWindow.h +++ b/core/jni/CursorWindow.h @@ -21,9 +21,12 @@ #include <stddef.h> #include <stdint.h> +#ifdef USE_ECLAIR_MEMORYDEALER +#include <binder/MemoryDealer.h> +#else #include <binder/IMemory.h> #include <utils/RefBase.h> - +#endif #include <jni.h> #define DEFAULT_WINDOW_SIZE 4096 @@ -101,7 +104,11 @@ class CursorWindow public: CursorWindow(size_t maxSize); CursorWindow(){} +#ifdef USE_ECLAIR_MEMORYDEALER + bool setMemory(sp<IMemory>); +#else bool setMemory(const sp<IMemory>&); +#endif ~CursorWindow(); bool initBuffer(bool localOnly); @@ -189,6 +196,9 @@ private: size_t mSize; size_t mMaxSize; window_header_t * mHeader; +#ifdef USE_ECLAIR_MEMORYDEALER + sp<MemoryDealer> mHeap; +#endif sp<IMemory> mMemory; /** diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp index e1e9536..a528006 100644 --- a/core/jni/android/graphics/Canvas.cpp +++ b/core/jni/android/graphics/Canvas.cpp @@ -951,7 +951,7 @@ static JNINativeMethod gCanvasMethods[] = { (void*) SkCanvasGlue::drawText___CIIFFPaint}, {"native_drawText","(ILjava/lang/String;IIFFI)V", (void*) SkCanvasGlue::drawText__StringIIFFPaint}, - {"drawText","(Ljava/lang/String;FFLandroid/graphics/Paint;)V", + {"native_drawText","(Ljava/lang/String;FFLandroid/graphics/Paint;)V", (void*) SkCanvasGlue::drawString}, {"native_drawPosText","(I[CII[FI)V", (void*) SkCanvasGlue::drawPosText___CII_FPaint}, diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index b85466b..7531dee 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -476,7 +476,8 @@ static void android_hardware_Camera_setParameters(JNIEnv *env, jobject thiz, jst env->ReleaseStringCritical(params, str); } if (camera->setParameters(params8) != NO_ERROR) { - jniThrowException(env, "java/lang/RuntimeException", "setParameters failed"); + LOGE("setParameters failed!"); + //jniThrowException(env, "java/lang/RuntimeException", "setParameters failed"); return; } } diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp index feb0dad..50df9d3 100644 --- a/core/jni/android_net_NetUtils.cpp +++ b/core/jni/android_net_NetUtils.cpp @@ -22,29 +22,8 @@ #include <utils/Log.h> #include <arpa/inet.h> -extern "C" { -int ifc_enable(const char *ifname); -int ifc_disable(const char *ifname); -int ifc_add_host_route(const char *ifname, uint32_t addr); -int ifc_remove_host_routes(const char *ifname); -int ifc_set_default_route(const char *ifname, uint32_t gateway); -int ifc_get_default_route(const char *ifname); -int ifc_remove_default_route(const char *ifname); -int ifc_reset_connections(const char *ifname); -int ifc_configure(const char *ifname, in_addr_t ipaddr, in_addr_t netmask, in_addr_t gateway, in_addr_t dns1, in_addr_t dns2); - -int dhcp_do_request(const char *ifname, - in_addr_t *ipaddr, - in_addr_t *gateway, - in_addr_t *mask, - in_addr_t *dns1, - in_addr_t *dns2, - in_addr_t *server, - uint32_t *lease); -int dhcp_stop(const char *ifname); -int dhcp_release_lease(const char *ifname); -char *dhcp_get_errmsg(); -} +#include <netutils/ifc.h> +#include <netutils/dhcp.h> #define NETUTILS_PKG_NAME "android/net/NetworkUtils" diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp index 46000c9..5903f09 100644 --- a/core/jni/android_net_wifi_Wifi.cpp +++ b/core/jni/android_net_wifi_Wifi.cpp @@ -30,23 +30,6 @@ namespace android { static jboolean sScanModeActive = false; -/* - * The following remembers the jfieldID's of the fields - * of the DhcpInfo Java object, so that we don't have - * to look them up every time. - */ -static struct fieldIds { - jclass dhcpInfoClass; - jmethodID constructorId; - jfieldID ipaddress; - jfieldID gateway; - jfieldID netmask; - jfieldID dns1; - jfieldID dns2; - jfieldID serverAddress; - jfieldID leaseDuration; -} dhcpInfoFieldIds; - static int doCommand(const char *cmd, char *replybuf, int replybuflen) { size_t reply_len = replybuflen - 1; @@ -479,28 +462,6 @@ static jboolean android_net_wifi_clearBlacklistCommand(JNIEnv* env, jobject claz return doBooleanCommand("BLACKLIST clear", "OK"); } -static jboolean android_net_wifi_doDhcpRequest(JNIEnv* env, jobject clazz, jobject info) -{ - jint ipaddr, gateway, mask, dns1, dns2, server, lease; - jboolean succeeded = ((jboolean)::do_dhcp_request(&ipaddr, &gateway, &mask, - &dns1, &dns2, &server, &lease) == 0); - if (succeeded && dhcpInfoFieldIds.dhcpInfoClass != NULL) { - env->SetIntField(info, dhcpInfoFieldIds.ipaddress, ipaddr); - env->SetIntField(info, dhcpInfoFieldIds.gateway, gateway); - env->SetIntField(info, dhcpInfoFieldIds.netmask, mask); - env->SetIntField(info, dhcpInfoFieldIds.dns1, dns1); - env->SetIntField(info, dhcpInfoFieldIds.dns2, dns2); - env->SetIntField(info, dhcpInfoFieldIds.serverAddress, server); - env->SetIntField(info, dhcpInfoFieldIds.leaseDuration, lease); - } - return succeeded; -} - -static jstring android_net_wifi_getDhcpError(JNIEnv* env, jobject clazz) -{ - return env->NewStringUTF(::get_dhcp_error_string()); -} - // ---------------------------------------------------------------------------- /* @@ -556,9 +517,6 @@ static JNINativeMethod gWifiMethods[] = { { "setScanResultHandlingCommand", "(I)Z", (void*) android_net_wifi_setScanResultHandlingCommand }, { "addToBlacklistCommand", "(Ljava/lang/String;)Z", (void*) android_net_wifi_addToBlacklistCommand }, { "clearBlacklistCommand", "()Z", (void*) android_net_wifi_clearBlacklistCommand }, - - { "doDhcpRequest", "(Landroid/net/DhcpInfo;)Z", (void*) android_net_wifi_doDhcpRequest }, - { "getDhcpError", "()Ljava/lang/String;", (void*) android_net_wifi_getDhcpError }, }; int register_android_net_wifi_WifiManager(JNIEnv* env) @@ -566,18 +524,6 @@ int register_android_net_wifi_WifiManager(JNIEnv* env) jclass wifi = env->FindClass(WIFI_PKG_NAME); LOG_FATAL_IF(wifi == NULL, "Unable to find class " WIFI_PKG_NAME); - dhcpInfoFieldIds.dhcpInfoClass = env->FindClass("android/net/DhcpInfo"); - if (dhcpInfoFieldIds.dhcpInfoClass != NULL) { - dhcpInfoFieldIds.constructorId = env->GetMethodID(dhcpInfoFieldIds.dhcpInfoClass, "<init>", "()V"); - dhcpInfoFieldIds.ipaddress = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "ipAddress", "I"); - dhcpInfoFieldIds.gateway = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "gateway", "I"); - dhcpInfoFieldIds.netmask = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "netmask", "I"); - dhcpInfoFieldIds.dns1 = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "dns1", "I"); - dhcpInfoFieldIds.dns2 = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "dns2", "I"); - dhcpInfoFieldIds.serverAddress = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "serverAddress", "I"); - dhcpInfoFieldIds.leaseDuration = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "leaseDuration", "I"); - } - return AndroidRuntime::registerNativeMethods(env, WIFI_PKG_NAME, gWifiMethods, NELEM(gWifiMethods)); } diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp index 259cc01..7c35ff4 100644 --- a/core/jni/android_server_BluetoothEventLoop.cpp +++ b/core/jni/android_server_BluetoothEventLoop.cpp @@ -729,6 +729,7 @@ static jboolean isEventLoopRunningNative(JNIEnv *env, jobject object) { #ifdef HAVE_BLUETOOTH extern DBusHandlerResult a2dp_event_filter(DBusMessage *msg, JNIEnv *env); +extern DBusHandlerResult hid_event_filter(DBusMessage *msg, JNIEnv *env); // Called by dbus during WaitForAndDispatchEventNative() static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg, @@ -853,9 +854,21 @@ static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg, method_onDeviceDisconnectRequested, env->NewStringUTF(remote_device_path)); goto success; + } else if (dbus_message_is_signal(msg, + "org.bluez.Input", + "PropertyChanged")) { + LOGD("Input device receive propertychnaged!!!"); } + if (a2dp_event_filter(msg, env) == DBUS_HANDLER_RESULT_HANDLED) { + ret = DBUS_HANDLER_RESULT_HANDLED; + } else if (hid_event_filter(msg, env) == DBUS_HANDLER_RESULT_HANDLED) { + ret = DBUS_HANDLER_RESULT_HANDLED; + } else { + ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + - ret = a2dp_event_filter(msg, env); + //ret = a2dp_event_filter(msg, env); env->PopLocalFrame(NULL); return ret; diff --git a/core/jni/android_server_BluetoothHidService.cpp b/core/jni/android_server_BluetoothHidService.cpp new file mode 100644 index 0000000..1e54a02 --- /dev/null +++ b/core/jni/android_server_BluetoothHidService.cpp @@ -0,0 +1,335 @@ +/* +** Copyright 2009 ISB Corporation +** Copyright (C) 2010 0xlab +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT 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 "BluetoothHidService.cpp" + +#include "android_bluetooth_common.h" +#include "android_runtime/AndroidRuntime.h" +#include "JNIHelp.h" +#include "jni.h" +#include "utils/Log.h" +#include "utils/misc.h" + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#ifdef HAVE_BLUETOOTH +#include <dbus/dbus.h> +#endif + +namespace android { + +#ifdef HAVE_BLUETOOTH +static jmethodID method_onPropertyChanged; +static jmethodID method_onHidDeviceConnected; +static jmethodID method_onHidDeviceDisconnected; + +typedef struct { + JavaVM *vm; + int envVer; + DBusConnection *conn; + jobject me; // for callbacks to java +} native_data_t; + +static native_data_t *nat = NULL; // global native data +static Properties hid_properties[] = { + {"Connected", DBUS_TYPE_BOOLEAN}, + }; +static void onConnectHidDeviceResult(DBusMessage *msg, void *user, void *nat); +static void onDisconnectHidDeviceResult(DBusMessage *msg, void *user, void *nat); +#endif + + +/* Returns true on success (even if adapter is present but disabled). + * Return false if dbus is down, or another serious error (out of memory) +*/ +static bool initNative(JNIEnv* env, jobject object) { + LOGD(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + nat = (native_data_t *)calloc(1, sizeof(native_data_t)); + if (NULL == nat) { + LOGE("%s: out of memory!", __FUNCTION__); + return false; + } + env->GetJavaVM( &(nat->vm) ); + nat->envVer = env->GetVersion(); + nat->me = env->NewGlobalRef(object); + + DBusError err; + dbus_error_init(&err); + dbus_threads_init_default(); + nat->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err); + if (dbus_error_is_set(&err)) { + LOGE("Could not get onto the system bus: %s", err.message); + dbus_error_free(&err); + return false; + } +#endif /*HAVE_BLUETOOTH*/ + return true; +} + +static void cleanupNative(JNIEnv* env, jobject object) { +#ifdef HAVE_BLUETOOTH + LOGD(__FUNCTION__); + if (nat) { + dbus_connection_close(nat->conn); + env->DeleteGlobalRef(nat->me); + free(nat); + nat = NULL; + } +#endif +} + +static jobjectArray getHidPropertiesNative(JNIEnv *env, jobject object, + jstring path) { +#ifdef HAVE_BLUETOOTH + LOGD(__FUNCTION__); + if (nat) { + DBusMessage *msg, *reply; + DBusError err; + dbus_error_init(&err); + + const char *c_path = env->GetStringUTFChars(path, NULL); + reply = dbus_func_args_timeout(env, + nat->conn, -1, c_path, + "org.bluez.Input", "GetProperties", + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(path, c_path); + if (!reply && dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply); + return NULL; + } else if (!reply) { + LOGE("DBus reply is NULL in function %s", __FUNCTION__); + return NULL; + } + DBusMessageIter iter; + if (dbus_message_iter_init(reply, &iter)) + return parse_properties(env, &iter, (Properties *)&hid_properties, + sizeof(hid_properties) / sizeof(Properties)); + } +#endif + return NULL; +} + +static jboolean connectHidDeviceNative(JNIEnv *env, jobject object, jstring path) { +#ifdef HAVE_BLUETOOTH + LOGD(__FUNCTION__); + if (nat) { + const char *c_path = env->GetStringUTFChars(path, NULL); + size_t path_sz = env->GetStringUTFLength(path) + 1; + char *c_path_copy = (char *)malloc(path_sz); // callback data + strncpy(c_path_copy, c_path, path_sz); + + bool ret = + dbus_func_args_async(env, nat->conn, -1, onConnectHidDeviceResult, + (void *)c_path_copy, nat, + c_path, "org.bluez.Input", + "Connect", DBUS_TYPE_INVALID); + + env->ReleaseStringUTFChars(path, c_path); + if (!ret) { + free(c_path_copy); + return JNI_FALSE; + } + return JNI_TRUE; + } +#endif + return JNI_FALSE; +} + +static jboolean disconnectHidDeviceNative(JNIEnv *env, jobject object, + jstring path) { +#ifdef HAVE_BLUETOOTH + LOGD(__FUNCTION__); + if (nat) { + const char *c_path = env->GetStringUTFChars(path, NULL); + + bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat, + c_path, "org.bluez.Input", "Disconnect", + DBUS_TYPE_INVALID); + LOGD("... path = %s", c_path); + env->ReleaseStringUTFChars(path, c_path); + return ret ? JNI_TRUE : JNI_FALSE; + } +#endif + return JNI_FALSE; +} + + +#ifdef HAVE_BLUETOOTH + +static void onConnectHidDeviceResult(DBusMessage *msg, void *user, void *natData) { + LOGD(__FUNCTION__); + + char *c_path = (char *)user; + DBusError err; + JNIEnv *env; + + if (nat->vm->GetEnv((void**)&env, nat->envVer) < 0) { + LOGE("%s: error finding Env for our VM\n", __FUNCTION__); + return; + } + + dbus_error_init(&err); + + LOGD("... path = %s", c_path); + if (dbus_set_error_from_message(&err, msg)) { + LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message); + dbus_error_free(&err); + env->CallVoidMethod(nat->me, + method_onHidDeviceDisconnected, + env->NewStringUTF(c_path)); + if (env->ExceptionCheck()) { + LOGE("VM Exception occurred in native function %s (%s:%d)", + __FUNCTION__, __FILE__, __LINE__); + } + } else { // else Java callback is triggered by signal in hid_event_filter? + env->CallVoidMethod(nat->me, + method_onHidDeviceConnected, + env->NewStringUTF(c_path)); + } + + free(c_path); +} + +static void onDisconnectHidDeviceResult(DBusMessage *msg, void *user, void *natData) { + LOGD(__FUNCTION__); + + char *c_path = (char *)user; + DBusError err; + JNIEnv *env; + + if (nat->vm->GetEnv((void**)&env, nat->envVer) < 0) { + LOGE("%s: error finding Env for our VM\n", __FUNCTION__); + return; + } + + dbus_error_init(&err); + + LOGD("... path = %s", c_path); + if (dbus_set_error_from_message(&err, msg)) { + LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message); + if (strcmp(err.name, "org.bluez.Error.NotConnected") == 0) { + // we were already disconnected, so report disconnect + env->CallVoidMethod(nat->me, + method_onHidDeviceDisconnected, + env->NewStringUTF(c_path)); + } else { + // Assume it is still connected + env->CallVoidMethod(nat->me, + method_onHidDeviceConnected, + env->NewStringUTF(c_path)); + } + dbus_error_free(&err); + if (env->ExceptionCheck()) { + LOGE("VM Exception occurred in native function %s (%s:%d)", + __FUNCTION__, __FILE__, __LINE__); + } + } else { // else Java callback is triggered by signal in hid_event_filter? + env->CallVoidMethod(nat->me, + method_onHidDeviceDisconnected, + env->NewStringUTF(c_path)); + } + free(c_path); +} + +DBusHandlerResult hid_event_filter(DBusMessage *msg, JNIEnv *env) { + DBusError err; + LOGD("hid_event_filter..."); + if (!nat) { + LOGE("... skipping %s\n", __FUNCTION__); + LOGE("... ignored\n"); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + dbus_error_init(&err); + + if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL) { + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + DBusHandlerResult result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + if (dbus_message_is_signal(msg, "org.bluez.Input", + "PropertyChanged")) { + jobjectArray str_array = + parse_property_change(env, msg, (Properties *)&hid_properties, + sizeof(hid_properties) / sizeof(Properties)); + const char *c_path = dbus_message_get_path(msg); + LOGD("received org.bluez.Input PropertyChanged..."); + jstring path = env->NewStringUTF(c_path); + + env->CallVoidMethod(nat->me, + method_onPropertyChanged, + path, + str_array); + + env->DeleteLocalRef(path); + + result = DBUS_HANDLER_RESULT_HANDLED; + return result; + } else { + LOGV("... ignored"); + } + if (env->ExceptionCheck()) { + LOGE("VM Exception occurred while handling %s.%s (%s) in %s," + " leaving for VM", + dbus_message_get_interface(msg), dbus_message_get_member(msg), + dbus_message_get_path(msg), __FUNCTION__); + } + + return result; +} +#endif + + +static JNINativeMethod sMethods[] = { + {"initNative", "()Z", (void *)initNative}, + {"cleanupNative", "()V", (void *)cleanupNative}, + + + {"connectHidDeviceNative", "(Ljava/lang/String;)Z", (void*)connectHidDeviceNative}, + {"disconnectHidDeviceNative", "(Ljava/lang/String;)Z", (void*)disconnectHidDeviceNative}, + {"getHidPropertiesNative", "(Ljava/lang/String;)[Ljava/lang/Object;", + (void *)getHidPropertiesNative}, +}; + + +int register_android_server_BluetoothHidService(JNIEnv *env) { + jclass clazz = env->FindClass("android/server/BluetoothHidService"); + if (clazz == NULL) { + LOGE("Can't find android/server/BluetoothHidService"); + return -1; + } + +#ifdef HAVE_BLUETOOTH + method_onHidDeviceConnected = env->GetMethodID(clazz, "onHidDeviceConnected", "(Ljava/lang/String;)V"); + method_onHidDeviceDisconnected = env->GetMethodID(clazz, "onHidDeviceDisconnected", "(Ljava/lang/String;)V"); + method_onPropertyChanged = env->GetMethodID(clazz, "onPropertyChanged", + "(Ljava/lang/String;[Ljava/lang/String;)V"); +#endif + + return AndroidRuntime::registerNativeMethods(env, + "android/server/BluetoothHidService", sMethods, NELEM(sMethods)); +} + + +} /* namespace android */ diff --git a/core/res/res/layout/keyguard_screen_tab_unlock.xml b/core/res/res/layout/keyguard_screen_tab_unlock.xml index 200a1b2..70375f9 100644 --- a/core/res/res/layout/keyguard_screen_tab_unlock.xml +++ b/core/res/res/layout/keyguard_screen_tab_unlock.xml @@ -131,12 +131,23 @@ android:textAppearance="?android:attr/textAppearanceMedium" android:drawablePadding="4dip" /> + + <TextView + android:id="@+id/customMsg" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="4.0dip" + android:layout_marginLeft="24dip" + android:layout_marginRight="24dip" + android:layout_below="@id/status2" + android:textAppearance="?android:attr/textAppearanceSmall" + /> <TextView android:id="@+id/screenLocked" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_below="@id/status2" + android:layout_below="@id/customMsg" android:layout_marginLeft="24dip" android:textAppearance="?android:attr/textAppearanceMedium" android:layout_marginTop="12dip" @@ -167,5 +178,56 @@ android:visibility="gone" /> + <!-- music control buttons --> + <ImageButton + android:id="@+id/musicControlPlay" + android:src="@drawable/ic_media_play" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:layout_alignParentBottom="true" + android:layout_marginBottom="20dip" + android:padding="10px" + android:background="@color/transparent" + android:visibility="gone" + /> + <ImageButton + android:id="@+id/musicControlPause" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/ic_media_pause" + android:layout_centerInParent="true" + android:layout_alignParentBottom="true" + android:layout_marginBottom="20dip" + android:padding="10px" + android:background="@color/transparent" + android:visibility="gone" + /> + <ImageButton + android:id="@+id/musicControlPrevious" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/ic_media_previous" + android:layout_alignParentBottom="true" + android:layout_marginBottom="20dip" + android:padding="10px" + android:layout_toLeftOf="@+id/musicControlPause" + android:background="@color/transparent" + android:visibility="gone" + /> + <ImageButton + android:id="@+id/musicControlNext" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/ic_media_next" + android:layout_alignParentBottom="true" + android:layout_marginBottom="20dip" + android:padding="10px" + android:layout_toRightOf="@+id/musicControlPause" + android:background="@color/transparent" + android:visibility="gone" + /> + + </RelativeLayout> diff --git a/core/res/res/layout/keyguard_screen_tab_unlock_land.xml b/core/res/res/layout/keyguard_screen_tab_unlock_land.xml index 23505c2..1e5884d 100644 --- a/core/res/res/layout/keyguard_screen_tab_unlock_land.xml +++ b/core/res/res/layout/keyguard_screen_tab_unlock_land.xml @@ -130,17 +130,75 @@ android:textAppearance="?android:attr/textAppearanceMedium" android:drawablePadding="4dip" /> + + <TextView + android:id="@+id/customMsg" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="4.0dip" + android:layout_below="@id/status2" + android:textAppearance="?android:attr/textAppearanceSmall" + /> <TextView android:id="@+id/screenLocked" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_below="@id/status2" + android:layout_below="@id/customMsg" android:textAppearance="?android:attr/textAppearanceMedium" android:gravity="center" android:layout_marginTop="12dip" android:drawablePadding="4dip" /> + <!-- music control buttons --> + <ImageButton + android:id="@+id/musicControlPlay" + android:src="@drawable/ic_media_play" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:layout_alignParentBottom="true" + android:layout_marginBottom="20dip" + android:padding="10px" + android:background="@color/transparent" + android:visibility="gone" + /> + <ImageButton + android:id="@+id/musicControlPause" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/ic_media_pause" + android:layout_centerInParent="true" + android:layout_alignParentBottom="true" + android:layout_marginBottom="20dip" + android:padding="10px" + android:background="@color/transparent" + android:visibility="gone" + /> + <ImageButton + android:id="@+id/musicControlPrevious" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/ic_media_previous" + android:layout_alignParentBottom="true" + android:layout_marginBottom="20dip" + android:padding="10px" + android:layout_toLeftOf="@+id/musicControlPause" + android:background="@color/transparent" + android:visibility="gone" + /> + <ImageButton + android:id="@+id/musicControlNext" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/ic_media_next" + android:layout_alignParentBottom="true" + android:layout_marginBottom="20dip" + android:padding="10px" + android:layout_toRightOf="@+id/musicControlPause" + android:background="@color/transparent" + android:visibility="gone" + /> </RelativeLayout> diff --git a/core/res/res/layout/keyguard_screen_unlock_landscape.xml b/core/res/res/layout/keyguard_screen_unlock_landscape.xml index b5cd442..f512378 100644 --- a/core/res/res/layout/keyguard_screen_unlock_landscape.xml +++ b/core/res/res/layout/keyguard_screen_unlock_landscape.xml @@ -137,6 +137,13 @@ android:drawablePadding="4dip" /> </LinearLayout> + + <TextView + android:id="@+id/customMsg" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceSmall" + /> <!-- fill space between header and button below --> <View diff --git a/core/res/res/layout/keyguard_screen_unlock_portrait.xml b/core/res/res/layout/keyguard_screen_unlock_portrait.xml index 9ac0a47..9f99fec 100644 --- a/core/res/res/layout/keyguard_screen_unlock_portrait.xml +++ b/core/res/res/layout/keyguard_screen_unlock_portrait.xml @@ -100,6 +100,17 @@ /> </RelativeLayout> + + <TextView + android:id="@+id/customMsg" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="4dip" + android:layout_marginLeft="24dip" + android:layout_marginRight="24dip" + android:layout_below="@id/status2" + android:textAppearance="?android:attr/textAppearanceSmall" + /> <View android:id="@+id/divider" diff --git a/core/res/res/layout/recent_apps_dialog.xml b/core/res/res/layout/recent_apps_dialog.xml index e3492f6..dece5ec 100644 --- a/core/res/res/layout/recent_apps_dialog.xml +++ b/core/res/res/layout/recent_apps_dialog.xml @@ -28,6 +28,7 @@ <!-- Title --> <TextView + android:id="@+id/recent_title" android:layout_width="wrap_content" android:layout_height="40dip" android:gravity="center" diff --git a/core/res/res/layout/recent_apps_dialog_12.xml b/core/res/res/layout/recent_apps_dialog_12.xml new file mode 100644 index 0000000..66db0b8 --- /dev/null +++ b/core/res/res/layout/recent_apps_dialog_12.xml @@ -0,0 +1,129 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<com.android.internal.policy.impl.RecentApplicationsBackground + xmlns:android="http://schemas.android.com/apk/res/android" + android:background="@drawable/recent_dialog_background" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center" + android:padding="4dip" + android:orientation="vertical"> + + <!-- Title --> + <TextView + android:id="@+id/recent_title" + android:layout_width="wrap_content" + android:layout_height="20dip" + android:gravity="center" + android:textAppearance="?android:attr/textAppearanceSmall" + android:textColor="#80FFFFFF" + android:textStyle="bold" + android:singleLine="true" + android:text="@android:string/recent_tasks_title" /> + + <!-- This is only intended to be visible when all buttons (below) are invisible --> + <TextView + android:id="@+id/no_applications_message" + android:layout_width="320dip" + android:layout_height="80dip" + android:gravity="center" + android:textAppearance="?android:attr/textAppearanceSmall" + android:text="@android:string/no_recent_tasks" /> + + <!-- The first row has a fixed-width because the UI spec requires the box + to display with full-width no matter how many icons are visible, but to + adjust height based on number of rows. --> + <!-- TODO Adjust all sizes, padding, etc. to meet pixel-perfect specs --> + <LinearLayout + android:layout_width="320dip" + android:layout_height="wrap_content" + android:orientation="horizontal" + > + + <include + layout="@android:layout/recent_apps_icon_12" + android:id="@+id/button0" /> + + <include + layout="@android:layout/recent_apps_icon_12" + android:id="@+id/button1" /> + + <include + layout="@android:layout/recent_apps_icon_12" + android:id="@+id/button2" /> + + <include + layout="@android:layout/recent_apps_icon_12" + android:id="@+id/button3" /> + + </LinearLayout> + + <LinearLayout + android:layout_width="320dp" + android:layout_height="wrap_content" + android:orientation="horizontal" > + + <include + layout="@android:layout/recent_apps_icon_12" + android:id="@+id/button4" /> + + <include + layout="@android:layout/recent_apps_icon_12" + android:id="@+id/button5" /> + + <include + layout="@android:layout/recent_apps_icon_12" + android:id="@+id/button6" /> + + <include + layout="@android:layout/recent_apps_icon_12" + android:id="@+id/button7" /> + + </LinearLayout> + + <LinearLayout + android:layout_width="320dp" + android:layout_height="wrap_content" + android:orientation="horizontal" > + + <include + layout="@android:layout/recent_apps_icon_12" + android:id="@+id/button8" /> + + <include + layout="@android:layout/recent_apps_icon_12" + android:id="@+id/button9" /> + + <include + layout="@android:layout/recent_apps_icon_12" + android:id="@+id/button10" /> + + <include + layout="@android:layout/recent_apps_icon_12" + android:id="@+id/button11" /> + + </LinearLayout> + + <!-- spacer to balance out the title above --> + <FrameLayout + android:layout_height="20dip" + android:layout_width="match_parent" + /> +</com.android.internal.policy.impl.RecentApplicationsBackground> diff --git a/core/res/res/layout/recent_apps_dialog_15.xml b/core/res/res/layout/recent_apps_dialog_15.xml new file mode 100644 index 0000000..ee0f33e --- /dev/null +++ b/core/res/res/layout/recent_apps_dialog_15.xml @@ -0,0 +1,141 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<com.android.internal.policy.impl.RecentApplicationsBackground + xmlns:android="http://schemas.android.com/apk/res/android" + android:background="@drawable/recent_dialog_background" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center" + android:padding="4dip" + android:orientation="vertical"> + + <!-- Title --> + <TextView + android:id="@+id/recent_title" + android:layout_width="wrap_content" + android:layout_height="20dip" + android:gravity="center" + android:textAppearance="?android:attr/textAppearanceSmall" + android:textColor="#80FFFFFF" + android:textStyle="bold" + android:singleLine="true" + android:text="@android:string/recent_tasks_title" /> + + <!-- This is only intended to be visible when all buttons (below) are invisible --> + <TextView + android:id="@+id/no_applications_message" + android:layout_width="320dip" + android:layout_height="80dip" + android:gravity="center" + android:textAppearance="?android:attr/textAppearanceSmall" + android:text="@android:string/no_recent_tasks" /> + + <!-- The first row has a fixed-width because the UI spec requires the box + to display with full-width no matter how many icons are visible, but to + adjust height based on number of rows. --> + <!-- TODO Adjust all sizes, padding, etc. to meet pixel-perfect specs --> + <LinearLayout + android:layout_width="320dip" + android:layout_height="wrap_content" + android:orientation="horizontal" + > + + <include + layout="@android:layout/recent_apps_icon_15" + android:id="@+id/button0" /> + + <include + layout="@android:layout/recent_apps_icon_15" + android:id="@+id/button1" /> + + <include + layout="@android:layout/recent_apps_icon_15" + android:id="@+id/button2" /> + + <include + layout="@android:layout/recent_apps_icon_15" + android:id="@+id/button3" /> + + <include + layout="@android:layout/recent_apps_icon_15" + android:id="@+id/button4" /> + + </LinearLayout> + + <LinearLayout + android:layout_width="320dp" + android:layout_height="wrap_content" + android:orientation="horizontal" > + + <include + layout="@android:layout/recent_apps_icon_15" + android:id="@+id/button5" /> + + <include + layout="@android:layout/recent_apps_icon_15" + android:id="@+id/button6" /> + + <include + layout="@android:layout/recent_apps_icon_15" + android:id="@+id/button7" /> + + <include + layout="@android:layout/recent_apps_icon_15" + android:id="@+id/button8" /> + + <include + layout="@android:layout/recent_apps_icon_15" + android:id="@+id/button9" /> + + </LinearLayout> + + <LinearLayout + android:layout_width="320dp" + android:layout_height="wrap_content" + android:orientation="horizontal" > + + <include + layout="@android:layout/recent_apps_icon_15" + android:id="@+id/button10" /> + + <include + layout="@android:layout/recent_apps_icon_15" + android:id="@+id/button11" /> + + <include + layout="@android:layout/recent_apps_icon_15" + android:id="@+id/button12" /> + + <include + layout="@android:layout/recent_apps_icon_15" + android:id="@+id/button13" /> + + <include + layout="@android:layout/recent_apps_icon_15" + android:id="@+id/button14" /> + + </LinearLayout> + + <!-- spacer to balance out the title above --> + <FrameLayout + android:layout_height="20dip" + android:layout_width="match_parent" + /> +</com.android.internal.policy.impl.RecentApplicationsBackground> diff --git a/core/res/res/layout/recent_apps_icon_12.xml b/core/res/res/layout/recent_apps_icon_12.xml new file mode 100644 index 0000000..c9501e1 --- /dev/null +++ b/core/res/res/layout/recent_apps_icon_12.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<!-- This is not a standalone element - it is imported into recent_apps_dialog.xml --> +<TextView + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/label" + style="?android:attr/buttonStyle" + android:background="#00000000" + android:layout_width="80dip" + android:layout_height="wrap_content" + android:layout_marginTop="3dip" + android:layout_marginBottom="3dip" + android:textColor="@color/bright_foreground_dark" + + android:paddingTop="0dip" + android:paddingBottom="0dip" + android:drawablePadding="4dip" + + android:textSize="11dip" + android:singleLine="true" + android:ellipsize="marquee" + android:fadingEdge="horizontal" + android:gravity="top|center_horizontal" /> diff --git a/core/res/res/layout/recent_apps_icon_15.xml b/core/res/res/layout/recent_apps_icon_15.xml new file mode 100644 index 0000000..8ec38f5 --- /dev/null +++ b/core/res/res/layout/recent_apps_icon_15.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<!-- This is not a standalone element - it is imported into recent_apps_dialog.xml --> +<TextView + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/label" + style="?android:attr/buttonStyle" + android:background="#00000000" + android:layout_width="64dip" + android:layout_height="wrap_content" + android:layout_marginTop="3dip" + android:layout_marginBottom="3dip" + android:textColor="@color/bright_foreground_dark" + + android:paddingTop="0dip" + android:paddingBottom="0dip" + android:drawablePadding="4dip" + + android:textSize="11dip" + android:singleLine="true" + android:ellipsize="marquee" + android:fadingEdge="horizontal" + android:gravity="top|center_horizontal" /> diff --git a/core/res/res/layout/status_bar_expanded.xml b/core/res/res/layout/status_bar_expanded.xml index 30138a7..b20492a 100644 --- a/core/res/res/layout/status_bar_expanded.xml +++ b/core/res/res/layout/status_bar_expanded.xml @@ -24,7 +24,7 @@ android:descendantFocusability="afterDescendants" > - <LinearLayout + <LinearLayout android:id="@+id/exp_view_lin_layout" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" diff --git a/core/res/res/layout/status_bar_tracking.xml b/core/res/res/layout/status_bar_tracking.xml index c0a7a97..b15ee39 100644 --- a/core/res/res/layout/status_bar_tracking.xml +++ b/core/res/res/layout/status_bar_tracking.xml @@ -36,7 +36,7 @@ android:layout_height="wrap_content" android:orientation="vertical" > - <ImageView + <ImageView android:id="@+id/close_image" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 11095c0..da06ba5 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -134,11 +134,23 @@ <string name="power_off" msgid="4266614107412865048">"Ausschalten"</string> <string name="shutdown_progress" msgid="2281079257329981203">"Wird heruntergefahren..."</string> <string name="shutdown_confirm" msgid="649792175242821353">"Ihr Telefon wird heruntergefahren."</string> + + <!-- Button to reboot the phone, within the Phone Options dialog --> + <string name="reboot_system">Neu starten</string> + <!-- Reboot Progress Dialog. This is shown if the user chooses to reboot the phone. --> + <string name="reboot_progress">Neu starten...</string> + <!-- Reboot Confirmation Dialog. When the user chooses to reboot the phone, there will be a confirmation dialog. This is the message. --> + <string name="reboot_confirm">Ihr Telefon wird neu gestartet.</string> + <string name="recent_tasks_title" msgid="3691764623638127888">"Zuletzt verwendet"</string> <string name="no_recent_tasks" msgid="279702952298056674">"Keine zuletzt verwendeten Anwendungen"</string> <string name="global_actions" msgid="2406416831541615258">"Telefonoptionen"</string> <string name="global_action_lock" msgid="2844945191792119712">"Display-Sperre"</string> <string name="global_action_power_off" msgid="4471879440839879722">"Ausschalten"</string> + + <!-- label for item that reboots the phone in phone options dialog --> + <string name="global_action_reboot">Neu starten</string> + <string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"Lautlos"</string> <string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"Ton ist AUS"</string> <string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"Ton ist AN"</string> @@ -861,4 +873,7 @@ <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Weitere Informationen über die Mobildatennutzung durch Berühren aufrufen"</string> <string name="throttled_notification_title" msgid="6269541897729781332">"Mobildatenlimit überschritten"</string> <string name="throttled_notification_message" msgid="4712369856601275146">"Weitere Informationen über die Mobildatennutzung durch Berühren aufrufen"</string> + + <!-- Reboot recommended after applying color changes (CM) --> + <string name="toast_reboot_recommend">Achtung: Neustart wird empfohlen nach Beendigung der Anpassungen!</string> </resources> diff --git a/core/res/res/values-he-rIL/donottranslate-cldr.xml b/core/res/res/values-he-rIL/donottranslate-cldr.xml new file mode 100644 index 0000000..89cbf42 --- /dev/null +++ b/core/res/res/values-he-rIL/donottranslate-cldr.xml @@ -0,0 +1,149 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="month_long_standalone_january">ינואר</string> + <string name="month_long_standalone_february">פברואר</string> + <string name="month_long_standalone_march">מרץ</string> + <string name="month_long_standalone_april">אפריל</string> + <string name="month_long_standalone_may">מאי</string> + <string name="month_long_standalone_june">יוני</string> + <string name="month_long_standalone_july">יולי</string> + <string name="month_long_standalone_august">אוגוסט</string> + <string name="month_long_standalone_september">ספטמבר</string> + <string name="month_long_standalone_october">אוקטובר</string> + <string name="month_long_standalone_november">נובמבר</string> + <string name="month_long_standalone_december">דצמבר</string> + + <string name="month_long_january">ינואר</string> + <string name="month_long_february">פברואר</string> + <string name="month_long_march">מרץ</string> + <string name="month_long_april">אפריל</string> + <string name="month_long_may">מאי</string> + <string name="month_long_june">יוני</string> + <string name="month_long_july">יולי</string> + <string name="month_long_august">אוגוסט</string> + <string name="month_long_september">ספטמבר</string> + <string name="month_long_october">אוקטובר</string> + <string name="month_long_november">נובמבר</string> + <string name="month_long_december">דצמבר</string> + + <string name="month_medium_january">ינו</string> + <string name="month_medium_february">פבר</string> + <string name="month_medium_march">מרץ</string> + <string name="month_medium_april">אפר</string> + <string name="month_medium_may">מאי</string> + <string name="month_medium_june">יונ</string> + <string name="month_medium_july">יול</string> + <string name="month_medium_august">אוג</string> + <string name="month_medium_september">ספט</string> + <string name="month_medium_october">אוק</string> + <string name="month_medium_november">נוב</string> + <string name="month_medium_december">דצמ</string> + + <string name="month_shortest_january">י</string> + <string name="month_shortest_february">פ</string> + <string name="month_shortest_march">מ</string> + <string name="month_shortest_april">א</string> + <string name="month_shortest_may">מ</string> + <string name="month_shortest_june">י</string> + <string name="month_shortest_july">י</string> + <string name="month_shortest_august">א</string> + <string name="month_shortest_september">ס</string> + <string name="month_shortest_october">א</string> + <string name="month_shortest_november">נ</string> + <string name="month_shortest_december">ד</string> + + <string name="day_of_week_long_sunday">יום ראשון</string> + <string name="day_of_week_long_monday">יום שני</string> + <string name="day_of_week_long_tuesday">יום שלישי</string> + <string name="day_of_week_long_wednesday">יום רביעי</string> + <string name="day_of_week_long_thursday">יום חמישי</string> + <string name="day_of_week_long_friday">יום שישי</string> + <string name="day_of_week_long_saturday">יום שבת</string> + + <string name="day_of_week_medium_sunday">ראשון</string> + <string name="day_of_week_medium_monday">שני</string> + <string name="day_of_week_medium_tuesday">שלי</string> + <string name="day_of_week_medium_wednesday">רבי</string> + <string name="day_of_week_medium_thursday">חמי</string> + <string name="day_of_week_medium_friday">שיש</string> + <string name="day_of_week_medium_saturday">שבת</string> + + <string name="day_of_week_short_sunday">Su</string> + <string name="day_of_week_short_monday">Mo</string> + <string name="day_of_week_short_tuesday">Tu</string> + <string name="day_of_week_short_wednesday">We</string> + <string name="day_of_week_short_thursday">Th</string> + <string name="day_of_week_short_friday">Fr</string> + <string name="day_of_week_short_saturday">Sa</string> + + <string name="day_of_week_shortest_sunday">S</string> + <string name="day_of_week_shortest_monday">M</string> + <string name="day_of_week_shortest_tuesday">T</string> + <string name="day_of_week_shortest_wednesday">W</string> + <string name="day_of_week_shortest_thursday">T</string> + <string name="day_of_week_shortest_friday">F</string> + <string name="day_of_week_shortest_saturday">S</string> + + <string name="am">am</string> + <string name="pm">pm</string> + <string name="yesterday">אתמול</string> + <string name="today">היום</string> + <string name="tomorrow">מחר</string> + + <string name="hour_minute_24">%H:%M</string> + <string name="hour_minute_ampm">%-l:%M%p</string> + <string name="hour_minute_cap_ampm">%-l:%M%^p</string> + <string name="twelve_hour_time_format">h:mm a</string> + <string name="twenty_four_hour_time_format">HH:mm</string> + <string name="numeric_date">%-e/%-m/%Y</string> + <string name="numeric_date_format">d/M/yyyy</string> + <string name="numeric_date_template">"%s/%s/%s"</string> + <string name="month_day_year">%B %-e, %Y</string> + <string name="time_of_day">%-l:%M:%S %p</string> + <string name="date_and_time">%b %-e, %Y, %-l:%M:%S %p</string> + <string name="date_time">%1$s, %2$s</string> + <string name="time_date">%1$s, %3$s</string> + <string name="abbrev_month_day_year">%b %-e, %Y</string> + <string name="month_day">%B %-e</string> + <string name="month">%-B</string> + <string name="month_year">%B %Y</string> + <string name="abbrev_month_day">%b %-e</string> + <string name="abbrev_month">%-b</string> + <string name="abbrev_month_year">%b %Y</string> + <string name="time1_time2">%1$s – %2$s</string> + <string name="date1_date2">%2$s – %5$s</string> + <string name="numeric_md1_md2">%2$s/%3$s – %7$s/%8$s</string> + <string name="numeric_wday1_md1_wday2_md2">%1$s, %2$s/%3$s – %6$s, %7$s/%8$s</string> + <string name="numeric_mdy1_mdy2">%2$s/%3$s/%4$s – %7$s/%8$s/%9$s</string> + <string name="numeric_wday1_mdy1_wday2_mdy2">%1$s, %2$s/%3$s/%4$s – %6$s, %7$s/%8$s/%9$s</string> + <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%1$s, %2$s/%3$s/%4$s, %5$s – %6$s, %7$s/%8$s/%9$s, %10$s</string> + <string name="numeric_md1_time1_md2_time2">%2$s/%3$s, %5$s – %7$s/%8$s, %10$s</string> + <string name="numeric_wday1_md1_time1_wday2_md2_time2">%1$s, %2$s/%3$s, %5$s – %6$s, %7$s/%8$s, %10$s</string> + <string name="numeric_mdy1_time1_mdy2_time2">%2$s/%3$s/%4$s, %5$s – %7$s/%8$s/%9$s, %10$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%1$s, %2$s, %3$s – %4$s, %5$s, %6$s</string> + <string name="wday1_date1_wday2_date2">%1$s, %2$s – %4$s, %5$s</string> + <string name="date1_time1_date2_time2">%2$s, %3$s – %5$s, %6$s</string> + <string name="time_wday_date">%1$s, %2$s, %3$s</string> + <string name="wday_date">%2$s, %3$s</string> + <string name="time_wday">%1$s, %2$s</string> + <string name="same_year_md1_md2">%2$s %3$s – %7$s %8$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s, %2$s %3$s – %6$s, %7$s %8$s</string> + <string name="same_year_md1_time1_md2_time2">%2$s %3$s, %5$s – %7$s %8$s, %10$s</string> + <string name="same_month_md1_time1_md2_time2">%2$s %3$s, %5$s – %7$s %8$s, %10$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%1$s, %2$s %3$s, %5$s – %6$s, %7$s %8$s, %10$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%1$s, %2$s %3$s, %5$s – %6$s, %7$s %8$s, %10$s</string> + <string name="same_year_mdy1_time1_mdy2_time2">%2$s %3$s, %4$s, %5$s – %7$s %8$s, %9$s, %10$s</string> + <string name="same_month_mdy1_time1_mdy2_time2">%2$s %3$s, %4$s, %5$s – %7$s %8$s, %9$s, %10$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%1$s, %2$s %3$s, %4$s, %5$s – %6$s, %7$s %8$s, %9$s, %10$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%1$s, %2$s %3$s, %4$s, %5$s – %6$s, %7$s %8$s, %9$s, %10$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %2$s %3$s, %4$s – %6$s, %7$s %8$s, %9$s</string> + <string name="same_month_md1_md2">%2$s %3$s – %8$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s, %2$s %3$s – %6$s, %7$s %8$s</string> + <string name="same_year_mdy1_mdy2">%2$s %3$s – %7$s %8$s, %9$s</string> + <string name="same_month_mdy1_mdy2">%2$s %3$s – %8$s, %9$s</string> + <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %2$s %3$s – %6$s, %7$s %8$s, %9$s</string> + <string name="short_format_month">%b</string> + <string name="full_wday_month_day_no_year">d, MMMM, EEEE</string> + <string name="abbrev_wday_month_day_year">d, MMM, EEE, yyyy</string> +</resources> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index d0b3b8a..2dd5a37 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -134,11 +134,15 @@ <string name="power_off" msgid="4266614107412865048">"Spegni"</string> <string name="shutdown_progress" msgid="2281079257329981203">"Spegnimento..."</string> <string name="shutdown_confirm" msgid="649792175242821353">"Il telefono verrà spento."</string> + <string name="reboot_system">Riavvia</string> + <string name="reboot_progress">Riavvio in corso\u2026</string> + <string name="reboot_confirm">Il telefono verrà riavviato.</string> <string name="recent_tasks_title" msgid="3691764623638127888">"Recenti"</string> <string name="no_recent_tasks" msgid="279702952298056674">"Nessuna applicazione recente."</string> <string name="global_actions" msgid="2406416831541615258">"Opzioni telefono"</string> <string name="global_action_lock" msgid="2844945191792119712">"Blocco schermo"</string> <string name="global_action_power_off" msgid="4471879440839879722">"Spegni"</string> + <string name="global_action_reboot">Riavvia</string> <string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"Modalità silenziosa"</string> <string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"Audio non attivo"</string> <string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"Audio attivo"</string> @@ -861,4 +865,7 @@ <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Tocca per informazioni sull\'utilizzo dati cell."</string> <string name="throttled_notification_title" msgid="6269541897729781332">"Limite dati cell. superato"</string> <string name="throttled_notification_message" msgid="4712369856601275146">"Tocca per informazioni sull\'utilizzo dati cell."</string> + + <!-- Reboot recommended after applying color changes (CM) --> + <string name="toast_reboot_recommend">Attenzione: Riavvio richiesto dopo aver finito la personalizzazione!</string> </resources> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 5816ef7..62cefcc 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -132,13 +132,17 @@ <string name="turn_off_radio" msgid="8198784949987062346">"ワイヤレス接続をOFFにする"</string> <string name="screen_lock" msgid="799094655496098153">"画面をロック"</string> <string name="power_off" msgid="4266614107412865048">"電源を切る"</string> + <string name="reboot_system">"再起動する"</string> <string name="shutdown_progress" msgid="2281079257329981203">"シャットダウン中..."</string> <string name="shutdown_confirm" msgid="649792175242821353">"携帯電話の電源を切ります。"</string> + <string name="reboot_progress">"再起動中..."</string> + <string name="reboot_confirm">"携帯電話を再起動します。"</string> <string name="recent_tasks_title" msgid="3691764623638127888">"新着"</string> <string name="no_recent_tasks" msgid="279702952298056674">"最近使ったアプリケーションはありません。"</string> <string name="global_actions" msgid="2406416831541615258">"携帯電話オプション"</string> <string name="global_action_lock" msgid="2844945191792119712">"画面ロック"</string> <string name="global_action_power_off" msgid="4471879440839879722">"電源を切る"</string> + <string name="global_action_reboot">"再起動する"</string> <string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"マナーモード"</string> <string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"サウンドOFF"</string> <string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"サウンドON"</string> diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml index 4672c0e..4ec1f40 100644 --- a/core/res/res/values/arrays.xml +++ b/core/res/res/values/arrays.xml @@ -126,13 +126,14 @@ <item><xliff:g id="id">battery</xliff:g></item> <item><xliff:g id="id">phone_signal</xliff:g></item> <item><xliff:g id="id">phone_evdo_signal</xliff:g></item> + <item><xliff:g id="id">phone_dbm_signal</xliff:g></item> <item><xliff:g id="id">data_connection</xliff:g></item> <item><xliff:g id="id">cdma_eri</xliff:g></item> + <item><xliff:g id="id">wifi</xliff:g></item> <item><xliff:g id="id">tty</xliff:g></item> <item><xliff:g id="id">volume</xliff:g></item> <item><xliff:g id="id">mute</xliff:g></item> <item><xliff:g id="id">speakerphone</xliff:g></item> - <item><xliff:g id="id">wifi</xliff:g></item> <item><xliff:g id="id">tty</xliff:g></item> <item><xliff:g id="id">bluetooth</xliff:g></item> <item><xliff:g id="id">gps</xliff:g></item> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 6d6c47f..f69e0e7 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -916,6 +916,15 @@ <enum name="KEYCODE_MEDIA_REWIND" value="89" /> <enum name="KEYCODE_MEDIA_FAST_FORWARD" value="90" /> <enum name="KEYCODE_MUTE" value="91" /> + <enum name="KEYCODE_FUNC_1" value="92" /> + <enum name="KEYCODE_FUNC_2" value="93" /> + <enum name="KEYCODE_FUNC_3" value="94" /> + <enum name="KEYCODE_FUNC_4" value="95" /> + <enum name="KEYCODE_FUNC_5" value="96" /> + <enum name="KEYCODE_FUNC_6" value="97" /> + <enum name="KEYCODE_FUNC_7" value="98" /> + <enum name="KEYCODE_FUNC_8" value="99" /> + <enum name="KEYCODE_QUECHAR" value="100" /> </attr> <!-- ***************************************************************** --> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 088ab44..922defd 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -210,6 +210,12 @@ <item>10</item> </integer-array> + <!-- Vibrator pattern for feedback about on a key release --> + <integer-array name="config_virtualKeyUpPattern"> + <item>5</item> + <item>18</item> + </integer-array> + <bool name="config_use_strict_phone_number_comparation">false</bool> <!-- Display low battery warning when battery level dips to this value --> diff --git a/core/res/res/values/donottranslate-cldr.xml b/core/res/res/values/donottranslate-cldr.xml index a94fb58..6131fbe 100644 --- a/core/res/res/values/donottranslate-cldr.xml +++ b/core/res/res/values/donottranslate-cldr.xml @@ -95,6 +95,7 @@ <string name="hour_minute_ampm">%-l:%M%p</string> <string name="hour_minute_cap_ampm">%-l:%M%^p</string> <string name="twelve_hour_time_format">h:mm a</string> + <string name="twelve_hour_time_format_no_period">h:mm</string> <string name="twenty_four_hour_time_format">HH:mm</string> <string name="numeric_date">%-m/%-e/%Y</string> <string name="numeric_date_format">M/d/yyyy</string> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 86bfe94..776255c 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -272,13 +272,21 @@ <string name="screen_lock">Screen lock</string> <!-- Button to turn off the phone, within the Phone Options dialog --> <string name="power_off">Power off</string> + <!-- Button to reboot the phone, within the Phone Options dialog --> + <string name="reboot_system">Reboot phone</string> <!-- Shutdown Progress Dialog. This is shown if the user chooses to power off the phone. --> <string name="shutdown_progress">Shutting down\u2026</string> + <!-- Reboot Progress Dialog. This is shown if the user chooses to reboot the phone. --> + <string name="reboot_progress">Rebooting\u2026</string> + <!-- Shutdown Confirmation Dialog. When the user chooses to power off the phone, there will be a confirmation dialog. This is the message. --> <string name="shutdown_confirm">Your phone will shut down.</string> + <!-- Reboot Confirmation Dialog. When the user chooses to reboot the phone, there will be a confirmation dialog. This is the message. --> + <string name="reboot_confirm">Your phone will reboot.</string> + <!-- Recent Tasks dialog: title --> <string name="recent_tasks_title">Recent</string> <!-- Recent Tasks dialog: message when there are no recent applications --> @@ -293,6 +301,9 @@ <!-- label for item that turns off power in phone options dialog --> <string name="global_action_power_off">Power off</string> + <!-- label for item that reboots the phone in phone options dialog --> + <string name="global_action_reboot">Reboot</string> + <!-- label for item that enables silent mode in phone options dialog --> <string name="global_action_toggle_silent_mode">Silent mode</string> @@ -2235,6 +2246,7 @@ <string name="l2tp_vpn_description">Layer 2 Tunneling Protocol</string> <string name="l2tp_ipsec_psk_vpn_description">Pre-shared key based L2TP/IPSec VPN</string> <string name="l2tp_ipsec_crt_vpn_description">Certificate based L2TP/IPSec VPN</string> + <string name="openvpn_vpn_description">OpenVPN SSL VPN</string> <!-- Localized strings for WebView --> <!-- Label for button in a WebView that will open a chooser to choose a file to upload --> @@ -2269,4 +2281,7 @@ <!-- Shown when the users bandwidth is reduced because of excessive data use --> <string name="throttled_notification_title">Mobile data limit exceeded</string> <string name="throttled_notification_message">Touch to learn more about mobile data use</string> + + <!-- Reboot recommended after applying color changes (CM) --> + <string name="toast_reboot_recommend">Caution: Reboot recommended after finishing customization!</string> </resources> diff --git a/core/tests/coretests/src/android/preference/ListPreferenceTest.java b/core/tests/coretests/src/android/preference/ListPreferenceTest.java new file mode 100644 index 0000000..41f8e03 --- /dev/null +++ b/core/tests/coretests/src/android/preference/ListPreferenceTest.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.preference; + +import android.preference.ListPreference; +import android.test.AndroidTestCase; + +public class ListPreferenceTest extends AndroidTestCase { + public void testListPreferenceSummaryFromEntries() { + String[] entries = { "one", "two", "three" }; + String[] entryValues = { "1" , "2", "3" }; + ListPreference lp = new ListPreference(getContext()); + lp.setEntries(entries); + lp.setEntryValues(entryValues); + + lp.setValue(entryValues[1]); + assertTrue(lp.getSummary() == null); + + lp.setSummary("%1$s"); + assertEquals(entries[1], lp.getSummary()); + + lp.setValue(entryValues[2]); + assertEquals(entries[2], lp.getSummary()); + + lp.setSummary(null); + assertTrue(lp.getSummary() == null); + + lp.setSummary("The color is %1$s"); + assertEquals("The color is " + entries[2], lp.getSummary()); + } +} diff --git a/docs/html/resources/articles/painless-threading.jd b/docs/html/resources/articles/painless-threading.jd index 921f4df..17cec35 100644 --- a/docs/html/resources/articles/painless-threading.jd +++ b/docs/html/resources/articles/painless-threading.jd @@ -108,7 +108,7 @@ you. Our previous example can easily be rewritten with new DownloadImageTask().execute("http://example.com/image.png"); } -private class DownloadImageTask extends AsyncTask<string, void,="" bitmap=""> { +private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> { protected Bitmap doInBackground(String... urls) { return loadImageFromNetwork(urls[0]); } diff --git a/graphics/java/android/graphics/ArabicReshape.java b/graphics/java/android/graphics/ArabicReshape.java new file mode 100644 index 0000000..d89985c --- /dev/null +++ b/graphics/java/android/graphics/ArabicReshape.java @@ -0,0 +1,448 @@ +/* @hide */ +package android.graphics; + +/* +This code is for Arabic Reshaping. +Writtien by Abdulaziz Alhussien. +azizanroid@gmail.com + +This code is used in Mirsal, Ibrahim Keyboard, Arabic Contact, Arabic notepad applications + + +@hide +*/ +class ArabicReshape { + + + static final char RIGHT_LEFT_CHAR= 0x0001; + static final char RIGHT_NOLEFT_CHAR_ALEF=0x0006; + static final char RIGHT_NOLEFT_CHAR=0x0004; + static final char RIGHT_LEFT_CHAR_LAM=0x0003; + static final char TANWEEN=0x000C; + static final char TASHKEEL=0x000A; + static final char TATWEEL_CHAR= 0x0008; + static final char NORIGHT_NOLEFT_CHAR= 0x0007; + static final char NOTUSED_CHAR= 0x000F; + static final char NOTARABIC_CHAR= 0x0000; + + static final char RIGHT_LEFT_CHAR_MASK= 0x0880; + static final char RIGHT_NOLEFT_CHAR_MASK= 0x0800; + static final char LEFT_CHAR_MASK= 0x0080; + + + + + + private static final char allchar[][] = { + {0x0621, 0x0007, 0xFE80, 0xFE80, 0xFE80, 0xFE80}, + {0x0622, 0x0806, 0xFE81, 0xFE82, 0xFEF5, 0xFEF6}, + {0x0623, 0x0806, 0xFE83, 0xFE84, 0xFEF7, 0xFEF8}, + {0x0624, 0x0804, 0xFE85, 0xFE86, 0xFE86, 0xFE86}, + {0x0625, 0x0806, 0xFE87, 0xFE88, 0xFEF9, 0xFEFA}, + {0x0626, 0x0881, 0xFE89, 0xFE8A, 0xFE8B, 0xFE8C}, + {0x0627, 0x0806, 0xFE8D, 0xFE8E, 0xFEFB, 0xFEFC}, + {0x0628, 0x0881, 0xFE8F, 0xFE90, 0xFE91, 0xFE92}, + {0x0629, 0x0804, 0xFE93, 0xFE94, 0xFE94, 0xFE94}, + {0x062A, 0x0881, 0xFE95, 0xFE96, 0xFE97, 0xFE98}, + {0x062B, 0x0881, 0xFE99, 0xFE9A, 0xFE9B, 0xFE9C}, + {0x062C, 0x0881, 0xFE9D, 0xFE9E, 0xFE9F, 0xFEA0}, + {0x062D, 0x0881, 0xFEA1, 0xFEA2, 0xFEA3, 0xFEA4}, + {0x062E, 0x0881, 0xFEA5, 0xFEA6, 0xFEA7, 0xFEA8}, + {0x062F, 0x0804, 0xFEA9, 0xFEAA, 0xFEAA, 0xFEAA}, + {0x0630, 0x0804, 0xFEAB, 0xFEAC, 0xFEAC, 0xFEAC}, + {0x0631, 0x0804, 0xFEAD, 0xFEAE, 0xFEAE, 0xFEAE}, + {0x0632, 0x0804, 0xFEAF, 0xFEB0, 0xFEB0, 0xFEB0}, + {0x0633, 0x0881, 0xFEB1, 0xFEB2, 0xFEB3, 0xFEB4}, + {0x0634, 0x0881, 0xFEB5, 0xFEB6, 0xFEB7, 0xFEB8}, + {0x0635, 0x0881, 0xFEB9, 0xFEBA, 0xFEBB, 0xFEBC}, + {0x0636, 0x0881, 0xFEBD, 0xFEBE, 0xFEBF, 0xFEC0}, + {0x0637, 0x0881, 0xFEC1, 0xFEC2, 0xFEC3, 0xFEC4}, + {0x0638, 0x0881, 0xFEC5, 0xFEC6, 0xFEC7, 0xFEC8}, + {0x0639, 0x0881, 0xFEC9, 0xFECA, 0xFECB, 0xFECC}, + {0x063A, 0x0881, 0xFECD, 0xFECE, 0xFECF, 0xFED0}, + {0x063B, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x063C, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x063D, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x063E, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x063F, 0x000F, 0x0, 0x0, 0x0, 0x0}, + + {0x0640, 0x0888, 0x0640, 0x0640, 0x0640, 0x0640}, + {0x0641, 0x0881, 0xFED1, 0xFED2, 0xFED3, 0xFED4}, + {0x0642, 0x0881, 0xFED5, 0xFED6, 0xFED7, 0xFED8}, + {0x0643, 0x0881, 0xFED9, 0xFEDA, 0xFEDB, 0xFEDC}, + {0x0644, 0x0883, 0xFEDD, 0xFEDE, 0xFEDF, 0xFEE0}, + {0x0645, 0x0881, 0xFEE1, 0xFEE2, 0xFEE3, 0xFEE4}, + {0x0646, 0x0881, 0xFEE5, 0xFEE6, 0xFEE7, 0xFEE8}, + {0x0647, 0x0881, 0xFEE9, 0xFEEA, 0xFEEB, 0xFEEC}, + {0x0648, 0x0804, 0xFEED, 0xFEEE, 0xFEEE, 0xFEEE}, + {0x0649, 0x0804, 0xFEEF, 0xFEF0, 0xFEF0, 0xFEF0}, + {0x064A, 0x0881, 0xFEF1, 0xFEF2, 0xFEF3, 0xFEF4}, + {0x064B, 0x000C, 0x064B, 0xFE70, 0xFE71, 0xFE70}, + {0x064C, 0x000C, 0x064C, 0xFE72, 0xFE72, 0xFE72}, + {0x064D, 0x000C, 0x064D, 0xFE74, 0xFE74, 0xFE74}, + {0x064E, 0x000A, 0x064E, 0xFE76, 0xFE77, 0xFE76}, + {0x064F, 0x000A, 0x064F, 0xFE78, 0xFE79, 0xFE78}, + {0x0650, 0x000A, 0x0650, 0xFE7A, 0xFE7B, 0xFE7A}, + {0x0651, 0x000A, 0x0651, 0xFE7C, 0xFE7D, 0xFE7C}, + {0x0652, 0x000A, 0x0652, 0xFE7E, 0xFE7F, 0xFE7E}, + + {0x0653, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x0654, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x0655, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x0656, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x0657, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x0658, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x0659, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x065A, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x065B, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x065C, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x065D, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x065E, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x065F, 0x000F, 0x0, 0x0, 0x0, 0x0}, + + {0x0660, 0x000B, 0x0660, 0x0660, 0x0660, 0x0660}, + {0x0661, 0x000B, 0x0661, 0x0661, 0x0661, 0x0661}, + {0x0662, 0x000B, 0x0662, 0x0662, 0x0662, 0x0662}, + {0x0663, 0x000B, 0x0663, 0x0663, 0x0663, 0x0663}, + {0x0664, 0x000B, 0x0665, 0x0664, 0x0664, 0x0664}, + {0x0665, 0x000B, 0x0665, 0x0665, 0x0665, 0x0665}, + {0x0666, 0x000B, 0x0666, 0x0666, 0x0666, 0x0666}, + {0x0667, 0x000B, 0x0667, 0x0667, 0x0667, 0x0667}, + {0x0668, 0x000B, 0x0668, 0x0668, 0x0668, 0x0668}, + {0x0669, 0x000B, 0x0669, 0x0669, 0x0669, 0x0669}, + + {0x066A, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x066B, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x066C, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x066D, 0x000F, 0x0, 0x0, 0x0, 0x0}, + + {0x066E, 0x000E, 0x065E, 0x065E, 0x065E, 0x065E}, + {0x066F, 0x000E, 0x065F, 0x065F, 0x065F, 0x065F}, + + {0x0670, 0x000F, 0x0, 0x0, 0x0, 0x0}, + + {0x0671, 0x0804, 0xFB50, 0xFB51, 0xFB51, 0xFB51}, + {0x0672, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x0673, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x0674, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x0675, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x0676, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x0677, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x0678, 0x000F, 0x0, 0x0, 0x0, 0x0}, + + + {0x0679, 0x0881, 0xFB66, 0xFB67, 0xFB68, 0xFB69}, + {0x067A, 0x0881, 0xFB5E, 0xFB5F, 0xFB60, 0xFB61}, + {0x067B, 0x0881, 0xFB52, 0xFB53, 0xFB54, 0xFB55}, + {0x067C, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x067D, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x067E, 0x0881, 0xFB56, 0xFB57, 0xFB58, 0xFB59}, + {0x067F, 0x0881, 0xFB62, 0xFB63, 0xFB64, 0xFB65}, + {0x0680, 0x0881, 0xFB5A, 0xFB5B, 0xFB5C, 0xFB5D}, + + {0x0681, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x0682, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x0683, 0x0881, 0xFB76, 0xFB77, 0xFB78, 0xFB79}, + {0x0684, 0x0881, 0xFB72, 0xFB73, 0xFB74, 0xFB75}, + {0x0685, 0x000F, 0x0, 0x0, 0x0, 0x0}, + + {0x0686, 0x0881, 0xFB7A, 0xFB7B, 0xFB7C, 0xFB7D}, + {0x0687, 0x0881, 0xFB7E, 0xFB7F, 0xFB80, 0xFB81}, + {0x0688, 0x0804, 0xFB88, 0xFB89, 0xFB89, 0xFB89}, + {0x0689, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x068A, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x068B, 0x000F, 0x0, 0x0, 0x0, 0x0}, + + {0x068C, 0x0804, 0xFB84, 0xFB85, 0xFB85, 0xFB85}, + {0x068D, 0x0804, 0xFB82, 0xFB83, 0xFB83, 0xFB83}, + {0x068E, 0x0804, 0xFB86, 0xFB87, 0xFB83, 0xFB83}, + {0x068F, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x0690, 0x000F, 0x0, 0x0, 0x0, 0x0}, + + {0x0691, 0x0804, 0xFB8C, 0xFB8D, 0xFB8D, 0xFB8D}, + {0x0692, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x0693, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x0694, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x0695, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x0696, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x0697, 0x000F, 0x0, 0x0, 0x0, 0x0}, + + {0x0698, 0x0804, 0xFB8A, 0xFB8B, 0xFB8B, 0xFB8B}, + {0x0699, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x069A, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x069B, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x069C, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x069D, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x069E, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x069F, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x06A0, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x06A1, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x06A2, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x06A3, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x06A4, 0x0881, 0xFB6A, 0xFB6B, 0xFB6C, 0xFB6D}, + {0x06A5, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x06A6, 0x0881, 0xFB6E, 0xFB6F, 0xFB70, 0xFB71}, + {0x06A7, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x06A8, 0x000F, 0x0, 0x0, 0x0, 0x0}, + + {0x06A9, 0x0881, 0xFB8E, 0xFB8F, 0xFB90, 0xFB91}, + {0x06AA, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x06AB, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x06AC, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x06AD, 0x0881, 0xFBD3, 0xFBD4, 0xFBD5, 0xFBD6}, + {0x06AE, 0x000F, 0x0, 0x0, 0x0, 0x0}, + + {0x06AF, 0x0881, 0xFB92, 0xFB93, 0xFB94, 0xFB95}, + {0x06B0, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x06B1, 0x0881, 0xFB9A, 0xFB9B, 0xFB9C, 0xFB9D}, + {0x06B2, 0x000F, 0x0, 0x0, 0x0, 0x0}, + + {0x06B3, 0x0881, 0xFB96, 0xFB97, 0xFB98, 0xFB99}, + {0x06B4, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x06B5, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x06B6, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x06B7, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x06B8, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x06B9, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x06BA, 0x0804, 0xFB9E, 0xFB9F, 0xFB9F, 0xFB9F}, + {0x06BB, 0x0881, 0xFBA0, 0xFBA1, 0xFBA2, 0xFBA3}, + {0x06BC, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x06BD, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x06BE, 0x0881, 0xFBAA, 0xFBAB, 0xFBAC, 0xFBAD}, + {0x06BF, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x06C0, 0x0804, 0xFBA4, 0xFBA5, 0xFBA5, 0xFBA5}, + {0x06C1, 0x0881, 0xFBA6, 0xFBA7, 0xFBA8, 0xFBA9}, + {0x06C2, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x06C3, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x06C4, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x06C5, 0x0804, 0xFBE0, 0xFBE1, 0xFBE1, 0xFBE1}, + {0x06C6, 0x0804, 0xFBD9, 0xFBDA, 0xFBDA, 0xFBDA}, + {0x06C7, 0x0804, 0xFBD7, 0xFBD8, 0xFBD8, 0xFBD8}, + {0x06C8, 0x0804, 0xFBDB, 0xFBDC, 0xFBDC, 0xFBDC}, + {0x06C9, 0x0804, 0xFBE2, 0xFBE3, 0xFBE3, 0xFBE3}, + {0x06CA, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x06CB, 0x0804, 0xFBDE, 0xFBDF, 0xFBDF, 0xFBDF}, + {0x06CC, 0x0881, 0xFBFC, 0xFBFD, 0xFBFE, 0xFBFF}, + {0x06CD, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x06CE, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x06CF, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x06D0, 0x0881, 0xFBE4, 0xFBE5, 0xFBE6, 0xFBE7}, + {0x06D1, 0x000F, 0x0, 0x0, 0x0, 0x0}, + {0x06D2, 0x0804, 0xFBAE, 0xFBAF, 0xFBAF, 0xFBAF}, + {0x06D3, 0x0804, 0xFBB0, 0xFBB1, 0xFBB1, 0xFBB1} + }; + + + /* @hide*/ + public static String reshape(String Str){ + String Temp=" "+Str+" "; + char pre,at,post; + StringBuilder reshapedString=new StringBuilder() ; + int i=0; + int len=Str.length(); + + char post_post; + char pre_pre=' '; + + while (i<len){ + pre=Temp.charAt(i+2); + at=Temp.charAt(i+1); + post=Temp.charAt(i); + + + int which_case=getCase(at); + int what_case_post=getCase(post); + int what_case_pre=getCase(pre); + int what_case_post_post; + int what_case_pre_pre; + //which_case=0x000F& + // Log.v("what case"," :" +which_case); + int pre_step=0; + if (what_case_pre==TASHKEEL){ + pre=Temp.charAt(i+3); + what_case_pre=getCase(pre); + } + if ((what_case_pre&LEFT_CHAR_MASK)==LEFT_CHAR_MASK){ + pre_step=1; + + } + + // System.out.println("##letter "+ pre); + switch (which_case&0x000F){ + + case NOTUSED_CHAR: + case NOTARABIC_CHAR: + + reshapedString.append(at); + + i++; + continue; + case NORIGHT_NOLEFT_CHAR: + case TATWEEL_CHAR: + reshapedString.append(getShape(at,0)); + + i++; + continue; + case RIGHT_NOLEFT_CHAR_ALEF: + + // System.out.println("--letter "+ pre); + + if ((what_case_pre&0x000F)==RIGHT_LEFT_CHAR_LAM){ + pre=Temp.charAt(i+3); + // System.out.println("++letter "+ pre); + what_case_pre=getCase(pre); + pre_step=0; + if ((what_case_pre&LEFT_CHAR_MASK)==LEFT_CHAR_MASK){ + pre_step=1; + + } + reshapedString.append(getShape(at,pre_step+2)); + i=i+2; + + continue; + } /*else if ((what_case_post&RIGHT_NOLEFT_CHAR_MASK)==RIGHT_NOLEFT_CHAR_MASK){ + reshapedString.append(getShape(at,2+pre_step)); + i=i+1; + + continue; + + + } else if (what_case_post==TANWEEN){ + reshapedString.append(getShape(at,pre_step)); + i=i+1; + continue; + + + } else if (what_case_post==TASHKEEL){ + post_post=Temp.charAt(i+3); + what_case_post_post=getCase(post_post); + if ((what_case_post_post&RIGHT_NOLEFT_CHAR_MASK)==RIGHT_NOLEFT_CHAR_MASK){ + reshapedString.append(getShape(at,2+pre_step)); + i=i+1; + + continue; + + } else { + reshapedString.append(getShape(at,pre_step)); + i=i+1; + continue; + + } + + + + + + } */else { + reshapedString.append(getShape(at,pre_step)); + i=i+1; + continue; + + } + case RIGHT_LEFT_CHAR_LAM: + case RIGHT_LEFT_CHAR: + if ((what_case_post&RIGHT_NOLEFT_CHAR_MASK)==RIGHT_NOLEFT_CHAR_MASK){ + reshapedString.append(getShape(at,2+pre_step)); + i=i+1; + continue; + + + } else if (what_case_post==TANWEEN){ + reshapedString.append(getShape(at,pre_step)); + i=i+1; + continue; + + + } else if (what_case_post==TASHKEEL){ + post_post=Temp.charAt(i+3); + what_case_post_post=getCase(post_post); + if ((what_case_post_post&RIGHT_NOLEFT_CHAR_MASK)==RIGHT_NOLEFT_CHAR_MASK){ + reshapedString.append(getShape(at,2+pre_step)); + i=i+1; + continue; + + } else { + reshapedString.append(getShape(at,pre_step)); + i=i+1; + continue; + + } + + + + + + } else { + reshapedString.append(getShape(at,pre_step)); + i=i+1; + continue; + + } + + case RIGHT_NOLEFT_CHAR: + reshapedString.append(getShape(at,pre_step)); + i=i+1; + continue; + case TASHKEEL: + reshapedString.append(getShape(at,0)); + i++; + pre_pre=pre; + continue; + case TANWEEN: + reshapedString.append(getShape(at,0)); + i++; + pre_pre=pre; + continue; + + + + default: + reshapedString.append(getShape(at,0)); + i++; + + + } + + + + } + + return reshapedString.toString(); + } + + + + + + + + + + + + static int getCase(char ch){ + if (ch<0x0621 || ch>0x06d2 ){ + return 0; + + + } + else { + + return allchar[(int)ch-0x0621][1]; + + } + + + } + + + static char getShape(char ch, int which_shape){ + + return allchar[(int)ch-0x0621][2+which_shape]; + + } + + + + +} diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java index 345f810..266dd58 100644 --- a/graphics/java/android/graphics/Canvas.java +++ b/graphics/java/android/graphics/Canvas.java @@ -34,7 +34,9 @@ import javax.microedition.khronos.opengles.GL; public class Canvas { // assigned in constructors, freed in finalizer final int mNativeCanvas; - + private static final char FIRST_RIGHT_TO_LEFT = '\u0590'; + private static final char LAST_RIGHT_TO_LEFT = '\u07b1'; + /* Our native canvas can be either a raster, gl, or picture canvas. If we are raster, then mGL will be null, and mBitmap may or may not be present (our default constructor creates a raster canvas but no @@ -44,7 +46,7 @@ public class Canvas { */ 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; @@ -53,7 +55,7 @@ public class Canvas { // Used to determine when compatibility scaling is in effect. private int mScreenDensity = Bitmap.DENSITY_NONE; - + // Used by native code @SuppressWarnings({"UnusedDeclaration"}) private int mSurfaceFormat; @@ -72,7 +74,7 @@ public class Canvas { /** * Construct a canvas with the specified bitmap to draw into. The bitmap * must be mutable. - * + * * <p>The initial target density of the canvas is the same as the given * bitmap's density. * @@ -88,7 +90,7 @@ public class Canvas { mBitmap = bitmap; mDensity = bitmap.mDensity; } - + /*package*/ Canvas(int nativeCanvas) { if (nativeCanvas == 0) { throw new IllegalStateException(); @@ -96,14 +98,14 @@ public class Canvas { 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. - * + * * <p>The initial target density of the canvas is the same as the initial * density of bitmaps as per {@link Bitmap#getDensity() Bitmap.getDensity()}. */ @@ -112,7 +114,7 @@ public class Canvas { mGL = gl; mDensity = Bitmap.getDefaultDensity(); } - + /** * Return the GL object associated with this canvas, or null if it is not * backed by GL. @@ -120,7 +122,7 @@ public class Canvas { 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 @@ -129,13 +131,13 @@ public class Canvas { public static void freeGlCaches() { freeCaches(); } - + /** * 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. * * @param bitmap Specifies a mutable bitmap for the canvas to draw into. - * + * * @see #setDensity(int) * @see #getDensity() */ @@ -152,7 +154,7 @@ public class Canvas { mBitmap = bitmap; mDensity = bitmap.mDensity; } - + /** * Set the viewport dimensions if this canvas is GL based. If it is not, * this method is ignored and no exception is thrown. @@ -197,7 +199,7 @@ public class Canvas { * to determine the scaling factor when drawing a bitmap into it. * * @see #setDensity(int) - * @see Bitmap#getDensity() + * @see Bitmap#getDensity() */ public int getDensity() { return mDensity; @@ -213,7 +215,7 @@ public class Canvas { * {@link Bitmap#DENSITY_NONE} to disable bitmap scaling. * * @see #getDensity() - * @see Bitmap#setDensity(int) + * @see Bitmap#setDensity(int) */ public void setDensity(int density) { if (mBitmap != null) { @@ -226,7 +228,7 @@ public class Canvas { public void setScreenDensity(int density) { mScreenDensity = density; } - + // the SAVE_FLAG constants must match their native equivalents /** restore the current matrix when restore() is called */ @@ -240,8 +242,8 @@ public class Canvas { /** clip against the layer's bounds */ public static final int CLIP_TO_LAYER_SAVE_FLAG = 0x10; /** restore everything when restore() is called */ - public static final int ALL_SAVE_FLAG = 0x1F; - + public static final int ALL_SAVE_FLAG = 0x1F; + /** * Saves the current matrix and clip onto a private stack. Subsequent * calls to translate,scale,rotate,skew,concat or clipRect,clipPath @@ -252,7 +254,7 @@ public class Canvas { * @return The value to pass to restoreToCount() to balance this save() */ public native int save(); - + /** * Based on saveFlags, can save the current matrix and clip onto a private * stack. Subsequent calls to translate,scale,rotate,skew,concat or @@ -287,7 +289,7 @@ public class Canvas { paint != null ? paint.mNativePaint : 0, saveFlags); } - + /** * Helper version of saveLayer() that takes 4 values rather than a RectF. */ @@ -318,7 +320,7 @@ public class Canvas { alpha = Math.min(255, Math.max(0, alpha)); return native_saveLayerAlpha(mNativeCanvas, bounds, alpha, saveFlags); } - + /** * Helper for saveLayerAlpha() that takes 4 values instead of a RectF. */ @@ -422,7 +424,7 @@ public class Canvas { public void concat(Matrix matrix) { native_concat(mNativeCanvas, matrix.native_instance); } - + /** * Completely replace the current matrix with the specified matrix. If the * matrix parameter is null, then the current matrix is reset to identity. @@ -434,7 +436,7 @@ public class Canvas { native_setMatrix(mNativeCanvas, matrix == null ? 0 : matrix.native_instance); } - + /** * Return, in ctm, the current transformation matrix. This does not alter * the matrix in the canvas, but just returns a copy of it. @@ -442,7 +444,7 @@ public class Canvas { public void getMatrix(Matrix ctm) { native_getCTM(mNativeCanvas, ctm.native_instance); } - + /** * Return a new matrix with a copy of the canvas' current transformation * matrix. @@ -452,7 +454,7 @@ public class Canvas { getMatrix(m); return m; } - + /** * Modify the current clip with the specified rectangle. * @@ -488,7 +490,7 @@ public class Canvas { * @return true if the resulting clip is non-empty */ public native boolean clipRect(RectF rect); - + /** * Intersect the current clip with the specified rectangle, which is * expressed in local coordinates. @@ -497,7 +499,7 @@ public class Canvas { * @return true if the resulting clip is non-empty */ public native boolean clipRect(Rect rect); - + /** * Modify the current clip with the specified rectangle, which is * expressed in local coordinates. @@ -534,7 +536,7 @@ public class Canvas { */ public native boolean clipRect(float left, float top, float right, float bottom); - + /** * Intersect the current clip with the specified rectangle, which is * expressed in local coordinates. @@ -550,7 +552,7 @@ public class Canvas { */ public native boolean clipRect(int left, int top, int right, int bottom); - + /** * Modify the current clip with the specified path. * @@ -561,7 +563,7 @@ public class Canvas { public boolean clipPath(Path path, Region.Op op) { return native_clipPath(mNativeCanvas, path.ni(), op.nativeInt); } - + /** * Intersect the current clip with the specified path. * @@ -571,7 +573,7 @@ public class Canvas { public boolean clipPath(Path path) { return clipPath(path, Region.Op.INTERSECT); } - + /** * Modify the current clip with the specified region. Note that unlike * clipRect() and clipPath() which transform their arguments by the @@ -600,11 +602,11 @@ public class Canvas { public boolean clipRegion(Region region) { return clipRegion(region, Region.Op.INTERSECT); } - + public DrawFilter getDrawFilter() { return mDrawFilter; } - + public void setDrawFilter(DrawFilter filter) { int nativeFilter = 0; if (filter != null) { @@ -617,7 +619,7 @@ public class Canvas { public enum EdgeType { BW(0), //!< treat edges by just rounding to nearest pixel boundary AA(1); //!< treat edges by rounding-out, since they may be antialiased - + EdgeType(int nativeInt) { this.nativeInt = nativeInt; } @@ -695,7 +697,7 @@ public class Canvas { public boolean getClipBounds(Rect bounds) { return native_getClipBounds(mNativeCanvas, bounds); } - + /** * Retrieve the clip bounds. * @@ -706,7 +708,7 @@ public class Canvas { getClipBounds(r); return r; } - + /** * Fill the entire canvas' bitmap (restricted to the current clip) with the * specified RGB color, using srcover porterduff mode. @@ -763,7 +765,7 @@ public class Canvas { public void drawPaint(Paint paint) { native_drawPaint(mNativeCanvas, paint.mNativePaint); } - + /** * Draw a series of points. Each point is centered at the coordinate * specified by pts[], and its diameter is specified by the paint's stroke @@ -853,7 +855,7 @@ public class Canvas { public void drawRect(Rect r, Paint paint) { drawRect(r.left, r.top, r.right, r.bottom, paint); } - + /** * Draw the specified Rect using the specified paint. The rectangle will @@ -949,7 +951,7 @@ public class Canvas { public void drawPath(Path path, Paint paint) { native_drawPath(mNativeCanvas, path.ni(), paint.mNativePaint); } - + private static void throwIfRecycled(Bitmap bitmap) { if (bitmap.isRecycled()) { throw new RuntimeException( @@ -960,7 +962,7 @@ public class Canvas { /** * Draw the specified bitmap, with its top/left corner at (x,y), using * the specified paint, transformed by the current matrix. - * + * * <p>Note: if the paint contains a maskfilter that generates a mask which * extends beyond the bitmap's original width/height (e.g. BlurMaskFilter), * then the bitmap will be drawn as if it were in a Shader with CLAMP mode. @@ -970,7 +972,7 @@ public class Canvas { * <p>If the bitmap and canvas have different densities, this function * will take care of automatically scaling the bitmap to draw at the * same density as the canvas. - * + * * @param bitmap The bitmap to be drawn * @param left The position of the left side of the bitmap being drawn * @param top The position of the top side of the bitmap being drawn @@ -987,7 +989,7 @@ public class Canvas { * Draw the specified bitmap, scaling/translating automatically to fill * the destination rectangle. If the source rectangle is not null, it * specifies the subset of the bitmap to draw. - * + * * <p>Note: if the paint contains a maskfilter that generates a mask which * extends beyond the bitmap's original width/height (e.g. BlurMaskFilter), * then the bitmap will be drawn as if it were in a Shader with CLAMP mode. @@ -998,7 +1000,7 @@ public class Canvas { * This is because the source and destination rectangle coordinate * spaces are in their respective densities, so must already have the * appropriate scaling factor applied. - * + * * @param bitmap The bitmap to be drawn * @param src May be null. The subset of the bitmap to be drawn * @param dst The rectangle that the bitmap will be scaled/translated @@ -1019,7 +1021,7 @@ public class Canvas { * Draw the specified bitmap, scaling/translating automatically to fill * the destination rectangle. If the source rectangle is not null, it * specifies the subset of the bitmap to draw. - * + * * <p>Note: if the paint contains a maskfilter that generates a mask which * extends beyond the bitmap's original width/height (e.g. BlurMaskFilter), * then the bitmap will be drawn as if it were in a Shader with CLAMP mode. @@ -1030,7 +1032,7 @@ public class Canvas { * This is because the source and destination rectangle coordinate * spaces are in their respective densities, so must already have the * appropriate scaling factor applied. - * + * * @param bitmap The bitmap to be drawn * @param src May be null. The subset of the bitmap to be drawn * @param dst The rectangle that the bitmap will be scaled/translated @@ -1046,7 +1048,7 @@ public class Canvas { paint != null ? paint.mNativePaint : 0, mScreenDensity, bitmap.mDensity); } - + /** * Treat the specified array of colors as a bitmap, and draw it. This gives * the same result as first creating a bitmap from the array, and then @@ -1093,7 +1095,7 @@ public class Canvas { native_drawBitmap(mNativeCanvas, colors, offset, stride, x, y, width, height, hasAlpha, paint != null ? paint.mNativePaint : 0); } - + /** Legacy version of drawBitmap(int[] colors, ...) that took ints for x,y */ public void drawBitmap(int[] colors, int offset, int stride, int x, int y, @@ -1103,7 +1105,7 @@ public class Canvas { drawBitmap(colors, offset, stride, (float)x, (float)y, width, height, hasAlpha, paint); } - + /** * Draw the bitmap using the specified matrix. * @@ -1115,13 +1117,13 @@ public class Canvas { nativeDrawBitmapMatrix(mNativeCanvas, bitmap.ni(), matrix.ni(), paint != null ? paint.mNativePaint : 0); } - + private static void checkRange(int length, int offset, int count) { if ((offset | count) < 0 || offset + count > length) { throw new ArrayIndexOutOfBoundsException(); } } - + /** * Draw the bitmap through the mesh, where mesh vertices are evenly * distributed across the bitmap. There are meshWidth+1 vertices across, and @@ -1168,18 +1170,18 @@ public class Canvas { verts, vertOffset, colors, colorOffset, paint != null ? paint.mNativePaint : 0); } - + public enum VertexMode { TRIANGLES(0), TRIANGLE_STRIP(1), TRIANGLE_FAN(2); - + VertexMode(int nativeInt) { this.nativeInt = nativeInt; } final int nativeInt; } - + /** * Draw the array of vertices, interpreted as triangles (based on mode). The * verts array is required, and specifies the x,y pairs for each vertex. If @@ -1208,7 +1210,7 @@ public class Canvas { * @param indices If not null, array of indices to reference into the * vertex (texs, colors) array. * @param indexCount number of entries in the indices array (if not null). - * @param paint Specifies the shader to use if the texs array is non-null. + * @param paint Specifies the shader to use if the texs array is non-null. */ public void drawVertices(VertexMode mode, int vertexCount, float[] verts, int vertOffset, @@ -1230,7 +1232,169 @@ public class Canvas { vertOffset, texs, texOffset, colors, colorOffset, indices, indexOffset, indexCount, paint.mNativePaint); } - + + /** + * Since the reshaping algorithm does not test for arabic prior to starting, this is made to + * @hide + **/ + public static boolean bidiTest(char[] text,int start,int srcCount) { + + boolean hasBidi=false; + + // Check if there are BiDi characters in the string, of so, we need to work. + for (int i=start;i<(srcCount+start);i++){ + if (text[i]>=FIRST_RIGHT_TO_LEFT&&text[i]<=LAST_RIGHT_TO_LEFT){ + hasBidi=true; + break; + } + } + return hasBidi; + } + /** + * Since the reshaping algorithm does not test for arabic prior to starting, this is made to + * @hide + **/ + public static boolean bidiTest(String text,int start,int srcCount) { + + boolean hasBidi=false; + + // Check if there are BiDi characters in the string, of so, we need to work. + for (int i=start;i<(srcCount+start);i++){ + if (text.charAt(i)>=FIRST_RIGHT_TO_LEFT&&text.charAt(i)<=LAST_RIGHT_TO_LEFT){ + hasBidi=true; + break; + } + } + return hasBidi; + } + /** + * A lightweight BiDi processing to make all draw text work with RTL languages. + * written from scratch by David Kohen (kohen dot d at gmail dot com) - 2010 + * @hide + **/ + public static char[] bidiProcess(char[] text,int start,int srcCount) { + + boolean hasBidi=false; + char[] destCharArray=new char[srcCount]; + + char[] buf = TemporaryBuffer.obtain(srcCount); + System.arraycopy(text,start, buf, 0, srcCount); + + // I'm doing the processing from the end of the string, since it worked well this way. + int count=0,srcIndex=0; + boolean rtlMode=true; + for (int i=0;i<srcCount;i++){ + srcIndex=srcCount-1-i; + if (buf[srcIndex]>=FIRST_RIGHT_TO_LEFT&&buf[srcIndex]<=LAST_RIGHT_TO_LEFT){ + destCharArray[i]=buf[srcIndex]; + // In rtl mode I'm mirroring glyphs. + rtlMode=true; + } + else { + srcIndex=srcCount-1-i; + if (count==0) { + // Direction neutral characters + if (buf[srcIndex]<='\u002f' || + (buf[srcIndex]>'\u0039' && buf[srcIndex]<='\u0040') || + (buf[srcIndex]>'\u005a' && buf[srcIndex]<='\u0060')|| + (buf[srcIndex]>'\u007a' && buf[srcIndex]<='\u00BF')) { + + if (rtlMode){ + switch (buf[srcIndex]) { + case '[': + destCharArray[i]=']'; + break; + case ']': + destCharArray[i]='['; + break; + case '}': + destCharArray[i]='{'; + break; + case '{': + destCharArray[i]='}'; + break; + case '(': + destCharArray[i]=')'; + break; + case ')': + destCharArray[i]='('; + break; + case '>': + destCharArray[i]='<'; + break; + case '<': + destCharArray[i]='>'; + break; + default: + destCharArray[i]=buf[srcIndex]; + break; + } + } else destCharArray[i]=buf[srcIndex]; + } else { + // Handling LTR embedded strings. + while (((srcIndex-count)>=0)&&((buf[srcIndex-count]<FIRST_RIGHT_TO_LEFT)||(buf[srcIndex-count]>LAST_RIGHT_TO_LEFT))){ + count++; + } + int index=0; + int punctuationMarks=0; + + // Handling direction neutral characters in the middle of LTR + while (count>0 && (srcIndex-(count)>=0) && + (buf[srcIndex-(count-1)]<='\u002f' || + (buf[srcIndex-(count-1)]>'\u0039' && buf[srcIndex-(count-1)]<='\u0040') || + (buf[srcIndex-(count-1)]>'\u005a' && buf[srcIndex-(count-1)]<='\u0060')|| + (buf[srcIndex-(count-1)]>'\u007a' && buf[srcIndex-(count-1)]<='\u00BF'))){ + destCharArray[i+(count-1)]=buf[srcIndex-(count-1)]; + count--; + punctuationMarks++; + } + + while (count>0){ + destCharArray[i+index]=buf[srcIndex-(count-1)]; + count--; + index++; + } + count=index+punctuationMarks-1; + } + } + else { + // Avoiding spaghetti code and mangling of loop counter + count--; + } + rtlMode=false; + } + } + return destCharArray; + } + + /** @hide **/ + public void drawText(char[] text, int index, int count, float x, float y, + Paint paint,boolean bidi) { + if (((index | count | (index + count)) < 0) || + (index + count) > text.length) { + throw new IndexOutOfBoundsException(); + } + boolean hasBidi=bidiTest(text,index,count); + if (hasBidi) { + if (bidi) { + char[] bidiText=bidiProcess(text,index,count); + String reshapedText=ArabicReshape.reshape(new String(bidiText)); + /* The reshaping may make the string smaller */ + native_drawText(mNativeCanvas, reshapedText.toCharArray(), 0, count - ((count - reshapedText.length())>0 ? (count - reshapedText.length()) : 0), x, y, + paint.mNativePaint); + } else { + String reshapedText=ArabicReshape.reshape(new String(text)); + /* The reshaping may make the string smaller */ + native_drawText(mNativeCanvas, reshapedText.toCharArray(), index, + count - ((text.length - reshapedText.length())>0 ? (text.length - reshapedText.length()) : 0), x, y, + paint.mNativePaint); + } + } else { + native_drawText(mNativeCanvas, text, index, count, x, y, + paint.mNativePaint); + } + } + /** * Draw the text, with origin at (x,y), using the specified paint. The * origin is interpreted based on the Align setting in the paint. @@ -1246,8 +1410,14 @@ public class Canvas { (text.length - index - count)) < 0) { throw new IndexOutOfBoundsException(); } - native_drawText(mNativeCanvas, text, index, count, x, y, - paint.mNativePaint); + + boolean hasBidi=bidiTest(text,index,count); + if (hasBidi) { + drawText(text,index,count,x,y,paint,true); + } else { + native_drawText(mNativeCanvas, text, index, count, x, y, + paint.mNativePaint); + } } /** @@ -1259,7 +1429,28 @@ public class Canvas { * @param y The y-coordinate of the origin of the text being drawn * @param paint The paint used for the text (e.g. color, size, style) */ - public native void drawText(String text, float x, float y, Paint paint); + private native void native_drawText(String text, float x, float y, Paint paint); + + /** @hide */ + public void drawText(String text, float x, float y, Paint paint,boolean bidi){ + boolean hasBidi=bidiTest(text,0,text.length()); + if (hasBidi) { + if (!bidi) { + native_drawText(ArabicReshape.reshape(text),x,y,paint); + } else { + if (text.length() > 0) { + String bidiText; + bidiText=new String(bidiProcess(text.toCharArray(),0,text.length())); + native_drawText(ArabicReshape.reshape(bidiText),x,y,paint); + } + } + } else { + native_drawText(text,x,y,paint); + } + } + public void drawText(String text, float x, float y, Paint paint){ + drawText(text,x,y,paint,true); + } /** * Draw the text, with origin at (x,y), using the specified paint. @@ -1277,8 +1468,16 @@ public class Canvas { if ((start | end | (end - start) | (text.length() - end)) < 0) { throw new IndexOutOfBoundsException(); } - native_drawText(mNativeCanvas, text, start, end, x, y, - paint.mNativePaint); + boolean hasBidi=bidiTest(text,start,end-start); + if (hasBidi) { + String reshapedText=ArabicReshape.reshape(new String(bidiProcess(text.toCharArray(),start,end-start))); + /* The reshaping may make the string smaller */ + native_drawText(mNativeCanvas, reshapedText, 0, end-start - ((end-start - reshapedText.length())>0?(end-start - reshapedText.length()):0), x, y, + paint.mNativePaint); + } else { + native_drawText(mNativeCanvas, text, start, end, x, y, + paint.mNativePaint); + } } /** @@ -1296,21 +1495,49 @@ public class Canvas { */ public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) { + drawText(text,start,end,x,y,paint,true); + } + + /** @hide */ + public void drawText(CharSequence text, int start, int end, float x, + float y, Paint paint,boolean bidi) { + boolean hasBidi=bidiTest(text.toString(),start,end-start); if (text instanceof String || text instanceof SpannedString || - text instanceof SpannableString) { - native_drawText(mNativeCanvas, text.toString(), start, end, x, y, + text instanceof SpannableString) { + if (hasBidi) { + if (bidi) { + String bidiText=new String(bidiProcess(text.toString().toCharArray(),start,end-start)); + String reshapedText=ArabicReshape.reshape(bidiText); + /* The reshaping may make the string smaller */ + native_drawText(mNativeCanvas, reshapedText, 0, (end-start) - ((end-start - reshapedText.length())>0 ? (end-start - reshapedText.length()) : 0), x, y, + paint.mNativePaint); + } else { + String reshapedText=ArabicReshape.reshape(text.toString()); + /* The reshaping may make the string smaller */ + native_drawText(mNativeCanvas, reshapedText, 0, (end-start) - ((end-start - reshapedText.length())>0 ? (end-start - reshapedText.length()) : 0), x, y, + paint.mNativePaint); + } + } else { + native_drawText(mNativeCanvas, text.toString() , start, end, x, y, 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); - drawText(buf, 0, end - start, x, y, paint); - TemporaryBuffer.recycle(buf); - } + paint); + } + else { + char[] buf = TemporaryBuffer.obtain(end - start); + TextUtils.getChars(text, start, end, buf, 0); + if (hasBidi) { + String reshapedText=ArabicReshape.reshape(new String(buf)); + /* The reshaping may make the string smaller */ + drawText(reshapedText.toCharArray(), 0, (end - start) - (((end - start) - reshapedText.length())>0?((end - start) - reshapedText.length()):0), x, y, paint,false); + } else { + drawText(buf, 0, end - start, x, y, paint,false); + } + TemporaryBuffer.recycle(buf); + } } /** @@ -1326,11 +1553,24 @@ public class Canvas { */ public void drawPosText(char[] text, int index, int count, float[] pos, Paint paint) { - if (index < 0 || index + count > text.length || count*2 > pos.length) { + if (index < 0 || index + count > text.length || (index+count)*2 > pos.length) { throw new IndexOutOfBoundsException(); } - native_drawPosText(mNativeCanvas, text, index, count, pos, - paint.mNativePaint); + + boolean hasBidi=bidiTest(text,index,count); + if (hasBidi) { + float[] relativePos = new float[count*2]; + System.arraycopy(pos , index*2 , relativePos , 0, count*2); + char[] bidiText; + bidiText=bidiProcess(text,index,count); + String reshapedText=ArabicReshape.reshape(new String(bidiText)); + /* The reshaping may make the string smaller */ + native_drawPosText(mNativeCanvas, reshapedText.toCharArray(), 0, count - ((count - reshapedText.length())>0 ? (count - reshapedText.length()) : 0), relativePos, + paint.mNativePaint); + } else { + native_drawPosText(mNativeCanvas, text, index, count, pos, + paint.mNativePaint); + } } /** @@ -1345,7 +1585,16 @@ public class Canvas { if (text.length()*2 > pos.length) { throw new ArrayIndexOutOfBoundsException(); } - native_drawPosText(mNativeCanvas, text, pos, paint.mNativePaint); + + boolean hasBidi=bidiTest(text,0,text.length()); + if (hasBidi) { + String bidiText; + bidiText=new String(bidiProcess(text.toCharArray(),0,text.length())); + native_drawPosText(mNativeCanvas, ArabicReshape.reshape(bidiText), pos, paint.mNativePaint); + } else { + native_drawPosText(mNativeCanvas, text, pos, + paint.mNativePaint); + } } /** @@ -1366,9 +1615,21 @@ public class Canvas { if (index < 0 || index + count > text.length) { throw new ArrayIndexOutOfBoundsException(); } - native_drawTextOnPath(mNativeCanvas, text, index, count, - path.ni(), hOffset, vOffset, - paint.mNativePaint); + boolean hasBidi=bidiTest(text,index,count); + if (hasBidi) { + char[] bidiText; + bidiText=bidiProcess(text,index,count); + String reshapedText=ArabicReshape.reshape(new String(bidiText)); + /* The reshaping may make the string smaller */ + native_drawTextOnPath(mNativeCanvas, reshapedText.toCharArray(), 0, count - ((count - reshapedText.length())>0 ? (count - reshapedText.length()) : 0), + path.ni(), hOffset, vOffset, + paint.mNativePaint); + } else { + native_drawTextOnPath(mNativeCanvas, text, index, count, + path.ni(), hOffset, vOffset, + paint.mNativePaint); + } + // TODO: Handle index>0 } /** @@ -1387,8 +1648,17 @@ public class Canvas { public void drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) { if (text.length() > 0) { - native_drawTextOnPath(mNativeCanvas, text, path.ni(), - hOffset, vOffset, paint.mNativePaint); + boolean hasBidi=bidiTest(text,0,text.length()); + if (hasBidi) { + String bidiText; + bidiText=new String(bidiProcess(text.toCharArray(),0,text.length())); + native_drawTextOnPath(mNativeCanvas, ArabicReshape.reshape(bidiText), path.ni(), + hOffset, vOffset, paint.mNativePaint); + } else { + native_drawTextOnPath(mNativeCanvas, text, + path.ni(), hOffset, vOffset, + paint.mNativePaint); + } } } @@ -1396,14 +1666,14 @@ public class Canvas { * Save the canvas state, draw the picture, and restore the canvas state. * This differs from picture.draw(canvas), which does not perform any * save/restore. - * + * * @param picture The picture to be drawn */ public void drawPicture(Picture picture) { picture.endRecording(); native_drawPicture(mNativeCanvas, picture.ni()); } - + /** * Draw the picture, stretched to fit into the dst rectangle. */ @@ -1417,7 +1687,7 @@ public class Canvas { drawPicture(picture); restore(); } - + /** * Draw the picture, stretched to fit into the dst rectangle. */ @@ -1431,7 +1701,7 @@ public class Canvas { drawPicture(picture); restore(); } - + protected void finalize() throws Throwable { super.finalize(); // If the constructor threw an exception before setting mNativeCanvas, the native finalizer @@ -1552,7 +1822,7 @@ public class Canvas { float[] verts, int vertOffset, float[] texs, int texOffset, int[] colors, int colorOffset, short[] indices, int indexOffset, int indexCount, int nPaint); - + private static native void native_drawText(int nativeCanvas, char[] text, int index, int count, float x, float y, int paint); diff --git a/graphics/java/android/graphics/Color.java b/graphics/java/android/graphics/Color.java index 5cefaa3..26aecba 100644 --- a/graphics/java/android/graphics/Color.java +++ b/graphics/java/android/graphics/Color.java @@ -202,7 +202,7 @@ public class Color { * 'yellow', 'lightgray', 'darkgray' */ public static int parseColor(String colorString) { - if (colorString.charAt(0) == '#') { + if (colorString.startsWith("#")) { // Use a long to avoid rollovers on #ffXXXXXX long color = Long.parseLong(colorString.substring(1), 16); if (colorString.length() == 7) { diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 3e3f87b..5fce6de 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -1299,7 +1299,18 @@ public class Paint { if ((index | count) < 0 || index + count > text.length) { throw new ArrayIndexOutOfBoundsException(); } - native_getTextPath(mNativePaint, text, index, count, x, y, path.ni()); + boolean hasBidi=Canvas.bidiTest(text,index,count); + if (hasBidi) { + char[] bidiText; + bidiText=Canvas.bidiProcess(text,index,count); + String reshapedText=ArabicReshape.reshape(new String(bidiText)); + /* The reshaping may make the string smaller */ + native_getTextPath(mNativePaint, reshapedText.toCharArray(), 0, + count - ((count-reshapedText.length())>0 ? (count-reshapedText.length()) : 0), + x, y, path.ni()); + } else { + native_getTextPath(mNativePaint, text, index, count, x, y, path.ni()); + } } /** @@ -1320,7 +1331,17 @@ public class Paint { if ((start | end | (end - start) | (text.length() - end)) < 0) { throw new IndexOutOfBoundsException(); } - native_getTextPath(mNativePaint, text, start, end, x, y, path.ni()); + boolean hasBidi=Canvas.bidiTest(text,start,start+end); + if (hasBidi) { + char[] bidiText; + bidiText=Canvas.bidiProcess(text.toCharArray(),start,start+end); + String reshapedText=ArabicReshape.reshape(String.valueOf(bidiText)); + /* The reshaping may make the string smaller */ + native_getTextPath(mNativePaint, reshapedText, 0, end-start - ((end-start - reshapedText.length())>0 ? (end-start - reshapedText.length()) : 0), + x, y, path.ni()); + } else { + native_getTextPath(mNativePaint, text, start, end, x, y, path.ni()); + } } /** diff --git a/graphics/jni/Android.mk b/graphics/jni/Android.mk index 8476be1..13ee169 100644 --- a/graphics/jni/Android.mk +++ b/graphics/jni/Android.mk @@ -19,8 +19,12 @@ LOCAL_SHARED_LIBRARIES := \ libcutils \ libskia \ libutils \ - libui \ - libsurfaceflinger_client + libui + +ifneq ($(BOARD_USES_ECLAIR_LIBCAMERA),true) + LOCAL_SHARED_LIBRARIES += \ + libsurfaceflinger_client +endif LOCAL_STATIC_LIBRARIES := diff --git a/include/binder/MemoryDealer.h b/include/binder/MemoryDealer.h index 170f20d..2512c27 100644 --- a/include/binder/MemoryDealer.h +++ b/include/binder/MemoryDealer.h @@ -14,6 +14,10 @@ * limitations under the License. */ +#ifdef USE_ECLAIR_MEMORYDEALER +#include <binder/MemoryDealerEclair.h> +#else + #ifndef ANDROID_MEMORY_DEALER_H #define ANDROID_MEMORY_DEALER_H @@ -58,3 +62,4 @@ private: }; // namespace android #endif // ANDROID_MEMORY_DEALER_H +#endif // USE_ECLAIR_MEMORYDEALER diff --git a/include/binder/MemoryDealerEclair.h b/include/binder/MemoryDealerEclair.h new file mode 100644 index 0000000..03ac70a --- /dev/null +++ b/include/binder/MemoryDealerEclair.h @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_MEMORY_DEALER_H +#define ANDROID_MEMORY_DEALER_H + + +#include <stdint.h> +#include <sys/types.h> + +#include <binder/IMemory.h> +#include <utils/threads.h> +#include <binder/MemoryHeapBase.h> + +namespace android { +// ---------------------------------------------------------------------------- +class String8; + +/* + * interface for implementing a "heap". A heap basically provides + * the IMemoryHeap interface for cross-process sharing and the + * ability to map/unmap pages within the heap. + */ +class HeapInterface : public virtual BnMemoryHeap +{ +public: + // all values must be page-aligned + virtual sp<IMemory> mapMemory(size_t offset, size_t size) = 0; + + HeapInterface(); +protected: + virtual ~HeapInterface(); +}; + +// ---------------------------------------------------------------------------- + +/* + * interface for implementing an allocator. An allocator provides + * methods for allocating and freeing memory blocks and dumping + * its state. + */ +class AllocatorInterface : public RefBase +{ +public: + enum { + PAGE_ALIGNED = 0x00000001 + }; + + virtual size_t allocate(size_t size, uint32_t flags = 0) = 0; + virtual status_t deallocate(size_t offset) = 0; + virtual size_t size() const = 0; + virtual void dump(const char* what, uint32_t flags = 0) const = 0; + virtual void dump(String8& res, + const char* what, uint32_t flags = 0) const = 0; + + AllocatorInterface(); +protected: + virtual ~AllocatorInterface(); +}; + +// ---------------------------------------------------------------------------- + +/* + * concrete implementation of HeapInterface on top of mmap() + */ +class SharedHeap : public HeapInterface, public MemoryHeapBase +{ +public: + SharedHeap(); + SharedHeap(size_t size, uint32_t flags = 0, char const * name = NULL); + virtual ~SharedHeap(); + virtual sp<IMemory> mapMemory(size_t offset, size_t size); +}; + +// ---------------------------------------------------------------------------- + +/* + * A simple templatized doubly linked-list implementation + */ + +template <typename NODE> +class LinkedList +{ + NODE* mFirst; + NODE* mLast; + +public: + LinkedList() : mFirst(0), mLast(0) { } + bool isEmpty() const { return mFirst == 0; } + NODE const* head() const { return mFirst; } + NODE* head() { return mFirst; } + NODE const* tail() const { return mLast; } + NODE* tail() { return mLast; } + + void insertAfter(NODE* node, NODE* newNode) { + newNode->prev = node; + newNode->next = node->next; + if (node->next == 0) mLast = newNode; + else node->next->prev = newNode; + node->next = newNode; + } + + void insertBefore(NODE* node, NODE* newNode) { + newNode->prev = node->prev; + newNode->next = node; + if (node->prev == 0) mFirst = newNode; + else node->prev->next = newNode; + node->prev = newNode; + } + + void insertHead(NODE* newNode) { + if (mFirst == 0) { + mFirst = mLast = newNode; + newNode->prev = newNode->next = 0; + } else { + newNode->prev = 0; + newNode->next = mFirst; + mFirst->prev = newNode; + mFirst = newNode; + } + } + + void insertTail(NODE* newNode) { + if (mLast == 0) { + insertHead(newNode); + } else { + newNode->prev = mLast; + newNode->next = 0; + mLast->next = newNode; + mLast = newNode; + } + } + + NODE* remove(NODE* node) { + if (node->prev == 0) mFirst = node->next; + else node->prev->next = node->next; + if (node->next == 0) mLast = node->prev; + else node->next->prev = node->prev; + return node; + } +}; + + +/* + * concrete implementation of AllocatorInterface using a simple + * best-fit allocation scheme + */ +class SimpleBestFitAllocator : public AllocatorInterface +{ +public: + + SimpleBestFitAllocator(size_t size); + virtual ~SimpleBestFitAllocator(); + + virtual size_t allocate(size_t size, uint32_t flags = 0); + virtual status_t deallocate(size_t offset); + virtual size_t size() const; + virtual void dump(const char* what, uint32_t flags = 0) const; + virtual void dump(String8& res, + const char* what, uint32_t flags = 0) const; + +private: + + struct chunk_t { + chunk_t(size_t start, size_t size) + : start(start), size(size), free(1), prev(0), next(0) { + } + size_t start; + size_t size : 28; + int free : 4; + mutable chunk_t* prev; + mutable chunk_t* next; + }; + + ssize_t alloc(size_t size, uint32_t flags); + chunk_t* dealloc(size_t start); + void dump_l(const char* what, uint32_t flags = 0) const; + void dump_l(String8& res, const char* what, uint32_t flags = 0) const; + + static const int kMemoryAlign; + mutable Mutex mLock; + LinkedList<chunk_t> mList; + size_t mHeapSize; +}; + +// ---------------------------------------------------------------------------- + +class MemoryDealer : public RefBase +{ +public: + + enum { + READ_ONLY = MemoryHeapBase::READ_ONLY, + PAGE_ALIGNED = AllocatorInterface::PAGE_ALIGNED + }; + + // creates a memory dealer with the SharedHeap and SimpleBestFitAllocator + MemoryDealer(size_t size, uint32_t flags = 0, const char* name = 0); + + // provide a custom heap but use the SimpleBestFitAllocator + MemoryDealer(const sp<HeapInterface>& heap); + + // provide both custom heap and allocotar + MemoryDealer( + const sp<HeapInterface>& heap, + const sp<AllocatorInterface>& allocator); + + virtual sp<IMemory> allocate(size_t size, uint32_t flags = 0); + virtual void deallocate(size_t offset); + virtual void dump(const char* what, uint32_t flags = 0) const; + + + sp<IMemoryHeap> getMemoryHeap() const { return heap(); } + sp<AllocatorInterface> getAllocator() const { return allocator(); } + +protected: + virtual ~MemoryDealer(); + +private: + const sp<HeapInterface>& heap() const; + const sp<AllocatorInterface>& allocator() const; + + class Allocation : public BnMemory { + public: + Allocation(const sp<MemoryDealer>& dealer, + ssize_t offset, size_t size, const sp<IMemory>& memory); + virtual ~Allocation(); + virtual sp<IMemoryHeap> getMemory(ssize_t* offset, size_t* size) const; + private: + sp<MemoryDealer> mDealer; + ssize_t mOffset; + size_t mSize; + sp<IMemory> mMemory; + }; + + sp<HeapInterface> mHeap; + sp<AllocatorInterface> mAllocator; +}; + + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_MEMORY_DEALER_H diff --git a/include/binder/MemoryHeapPmem.h b/include/binder/MemoryHeapPmem.h index e1660c4..02e83f8 100644 --- a/include/binder/MemoryHeapPmem.h +++ b/include/binder/MemoryHeapPmem.h @@ -20,18 +20,26 @@ #include <stdlib.h> #include <stdint.h> +#ifdef USE_ECLAIR_MEMORYDEALER +#include <binder/MemoryDealer.h> +#endif #include <binder/MemoryHeapBase.h> #include <binder/IMemory.h> #include <utils/SortedVector.h> +#ifndef USE_ECLAIR_MEMORYDEALER #include <utils/threads.h> +#endif namespace android { class MemoryHeapBase; // --------------------------------------------------------------------------- - +#ifdef USE_ECLAIR_MEMORYDEALER +class MemoryHeapPmem : public HeapInterface, public MemoryHeapBase +#else class MemoryHeapPmem : public MemoryHeapBase +#endif { public: class MemoryPmem : public BnMemory { diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h index 5ea83a5..dde56a9 100644 --- a/include/camera/CameraParameters.h +++ b/include/camera/CameraParameters.h @@ -25,6 +25,12 @@ namespace android { class CameraParameters { public: + enum { + CAMERA_ORIENTATION_UNKNOWN = 0, + CAMERA_ORIENTATION_PORTRAIT = 1, + CAMERA_ORIENTATION_LANDSCAPE = 2, + }; + CameraParameters(); CameraParameters(const String8 ¶ms) { unflatten(params); } ~CameraParameters(); @@ -52,6 +58,9 @@ public: void setPictureFormat(const char *format); const char *getPictureFormat() const; + int getOrientation() const; + void setOrientation(int orientation); + void dump() const; status_t dump(int fd, const Vector<String16>& args) const; @@ -95,6 +104,10 @@ public: // The height (in pixels) of EXIF thumbnail in Jpeg picture. // Example value: "384". Read/write. static const char KEY_JPEG_THUMBNAIL_HEIGHT[]; + + //++TODO is the following parameter is needed when jpeg thumbnail is available + static const char KEY_SUPPORTED_THUMBNAIL_SIZES[]; + // Supported EXIF thumbnail sizes (width x height). 0x0 means not thumbnail // in EXIF. // Example value: "512x384,320x240,0x0". Read only. @@ -128,6 +141,33 @@ public: // GPS altitude. This will be stored in JPEG EXIF header. // Example value: "21.0". Write only. static const char KEY_GPS_ALTITUDE[]; + + static const char KEY_GPS_LATITUDE_REF[]; + static const char KEY_GPS_LONGITUDE_REF[]; + static const char KEY_GPS_ALTITUDE_REF[]; + static const char KEY_GPS_STATUS[]; + static const char KEY_EXIF_DATETIME[]; + + static const char KEY_AUTO_EXPOSURE[]; + static const char KEY_SUPPORTED_AUTO_EXPOSURE[]; + static const char KEY_ISO_MODE[]; + static const char KEY_SUPPORTED_ISO_MODES[]; + static const char KEY_LENSSHADE[] ; + static const char KEY_SUPPORTED_LENSSHADE_MODES[] ; + static const char KEY_SHARPNESS[]; + static const char KEY_MAX_SHARPNESS[]; + static const char KEY_CONTRAST[]; + static const char KEY_MAX_CONTRAST[]; + static const char KEY_SATURATION[]; + static const char KEY_MAX_SATURATION[]; + + // Values for auto exposure settings. + static const char AUTO_EXPOSURE_FRAME_AVG[]; + static const char AUTO_EXPOSURE_CENTER_WEIGHTED[]; + static const char AUTO_EXPOSURE_SPOT_METERING[]; + + + // GPS timestamp (UTC in seconds since January 1, 1970). This should be // stored in JPEG EXIF header. // Example value: "1251192757". Write only. @@ -292,6 +332,7 @@ public: static const char PIXEL_FORMAT_YUV422I[]; // YUY2 static const char PIXEL_FORMAT_RGB565[]; static const char PIXEL_FORMAT_JPEG[]; + static const char PIXEL_FORMAT_RAW[]; // Values for focus mode settings. // Auto-focus mode. @@ -299,6 +340,7 @@ public: // Focus is set at infinity. Applications should not call // CameraHardwareInterface.autoFocus in this mode. static const char FOCUS_MODE_INFINITY[]; + static const char FOCUS_MODE_NORMAL[]; static const char FOCUS_MODE_MACRO[]; // Focus is fixed. The camera is always in this mode if the focus is not // adjustable. If the camera has auto-focus, this mode can fix the @@ -310,6 +352,16 @@ public: // CameraHardwareInterface.autoFocus in this mode. static const char FOCUS_MODE_EDOF[]; + static const char ISO_AUTO[]; + static const char ISO_HJR[] ; + static const char ISO_100[]; + static const char ISO_200[] ; + static const char ISO_400[]; + static const char ISO_800[]; + static const char ISO_1600[]; + // Values for Lens Shading + static const char LENSSHADE_ENABLE[] ; + static const char LENSSHADE_DISABLE[] ; private: DefaultKeyedVector<String8,String8> mMap; }; @@ -317,3 +369,4 @@ private: }; // namespace android #endif + diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h index 92bc126..18347d5 100644 --- a/include/media/AudioRecord.h +++ b/include/media/AudioRecord.h @@ -129,9 +129,9 @@ public: */ enum record_flags { - RECORD_AGC_ENABLE = AudioSystem::AGC_ENABLE, - RECORD_NS_ENABLE = AudioSystem::NS_ENABLE, - RECORD_IIR_ENABLE = AudioSystem::TX_IIR_ENABLE + RECORD_AGC_ENABLE = 0x0001, // AudioSystem::AGC_ENABLE, + RECORD_NS_ENABLE = 0x0002, // AudioSystem::NS_ENABLE, + RECORD_IIR_ENABLE = 0x0004, // AudioSystem::TX_IIR_ENABLE }; AudioRecord(int inputSource, diff --git a/include/media/IMediaRecorder.h b/include/media/IMediaRecorder.h index 73bf2ee..769d914 100644 --- a/include/media/IMediaRecorder.h +++ b/include/media/IMediaRecorder.h @@ -43,6 +43,7 @@ public: virtual status_t setVideoSize(int width, int height) = 0; virtual status_t setVideoFrameRate(int frames_per_second) = 0; virtual status_t setParameters(const String8& params) = 0; + virtual status_t setCameraParameters(const String8& params) = 0; virtual status_t setListener(const sp<IMediaPlayerClient>& listener) = 0; virtual status_t prepare() = 0; virtual status_t getMaxAmplitude(int* max) = 0; diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h index 9e606d9..a50ce35 100644 --- a/include/media/MediaPlayerInterface.h +++ b/include/media/MediaPlayerInterface.h @@ -45,6 +45,7 @@ enum player_type { // The shared library with the test player is passed passed as an // argument to the 'test:' url in the setDataSource call. TEST_PLAYER = 5, + FLAC_PLAYER = 6, }; diff --git a/include/media/MediaProfiles.h b/include/media/MediaProfiles.h index a4eea2a..5354937 100644 --- a/include/media/MediaProfiles.h +++ b/include/media/MediaProfiles.h @@ -24,8 +24,9 @@ namespace android { enum camcorder_quality { - CAMCORDER_QUALITY_LOW = 0, - CAMCORDER_QUALITY_HIGH = 1 + CAMCORDER_QUALITY_LOW = 0, + CAMCORDER_QUALITY_HIGH = 1, + CAMCORDER_QUALITY_FRONT = 2 }; enum video_decoder { diff --git a/include/media/MediaRecorderBase.h b/include/media/MediaRecorderBase.h index 5b787a7..c16daa8 100644 --- a/include/media/MediaRecorderBase.h +++ b/include/media/MediaRecorderBase.h @@ -41,6 +41,7 @@ struct MediaRecorderBase { virtual status_t setOutputFile(const char *path) = 0; virtual status_t setOutputFile(int fd, int64_t offset, int64_t length) = 0; virtual status_t setParameters(const String8& params) = 0; + virtual status_t setCameraParameters(const String8& params) = 0; virtual status_t setListener(const sp<IMediaPlayerClient>& listener) = 0; virtual status_t prepare() = 0; virtual status_t start() = 0; diff --git a/include/media/PVMediaRecorder.h b/include/media/PVMediaRecorder.h index 2a1298a..4b6f19d 100644 --- a/include/media/PVMediaRecorder.h +++ b/include/media/PVMediaRecorder.h @@ -45,6 +45,7 @@ public: virtual status_t setOutputFile(const char *path); virtual status_t setOutputFile(int fd, int64_t offset, int64_t length); virtual status_t setParameters(const String8& params); + virtual status_t setCameraParameters(const String8& params); virtual status_t setListener(const sp<IMediaPlayerClient>& listener); virtual status_t prepare(); virtual status_t start(); diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h index 9ea6c7b..0f5aed1 100644 --- a/include/media/mediarecorder.h +++ b/include/media/mediarecorder.h @@ -167,6 +167,7 @@ public: status_t setVideoSize(int width, int height); status_t setVideoFrameRate(int frames_per_second); status_t setParameters(const String8& params); + status_t setCameraParameters(const String8& params); status_t setListener(const sp<MediaRecorderListener>& listener); status_t prepare(); status_t getMaxAmplitude(int* max); diff --git a/include/ui/EventHub.h b/include/ui/EventHub.h index 3b18c77..3896485 100644 --- a/include/ui/EventHub.h +++ b/include/ui/EventHub.h @@ -57,7 +57,8 @@ public: CLASS_TOUCHSCREEN = 0x00000004, CLASS_TRACKBALL = 0x00000008, CLASS_TOUCHSCREEN_MT= 0x00000010, - CLASS_DPAD = 0x00000020 + CLASS_DPAD = 0x00000020, + CLASS_MOUSE = 0x00000040 }; uint32_t getDeviceClasses(int32_t deviceId) const; diff --git a/include/ui/KeycodeLabels.h b/include/ui/KeycodeLabels.h index 571e47b..f9f86234 100644 --- a/include/ui/KeycodeLabels.h +++ b/include/ui/KeycodeLabels.h @@ -114,6 +114,15 @@ static const KeycodeLabel KEYCODES[] = { { "MEDIA_REWIND", 89 }, { "MEDIA_FAST_FORWARD", 90 }, { "MUTE", 91 }, + { "FUNC_1", 92 }, + { "FUNC_2", 93 }, + { "FUNC_3", 94 }, + { "FUNC_4", 95 }, + { "FUNC_5", 96 }, + { "FUNC_6", 97 }, + { "FUNC_7", 98 }, + { "FUNC_8", 99 }, + { "QUECHAR", 100 }, // NOTE: If you add a new keycode here you must also add it to: // (enum KeyCode, in this file) @@ -218,7 +227,16 @@ typedef enum KeyCode { kKeyCodePreviousSong = 88, kKeyCodeRewind = 89, kKeyCodeForward = 90, - kKeyCodeMute = 91 + kKeyCodeMute = 91, + kKeyCodeFunc1 = 92, + kKeyCodeFunc2 = 93, + kKeyCodeFunc3 = 94, + kKeyCodeFunc4 = 95, + kKeyCodeFunc5 = 96, + kKeyCodeFunc6 = 97, + kKeyCodeFunc7 = 98, + kKeyCodeFunc8 = 99, + kKeyCodeQuechar = 100 } KeyCode; static const KeycodeLabel FLAGS[] = { diff --git a/include/ui/Overlay.h b/include/ui/Overlay.h index a9ae1c4..9156e80 100644 --- a/include/ui/Overlay.h +++ b/include/ui/Overlay.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2007 The Android Open Source Project + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -91,6 +92,7 @@ public: /* set the buffer attributes */ status_t setParameter(int param, int value); + status_t setFd(int fd); /* returns the address of a given buffer if supported, NULL otherwise. */ void* getBufferAddress(overlay_buffer_t buffer); diff --git a/include/utils/Asset.h b/include/utils/Asset.h index 5908bcc..b2697d4 100644 --- a/include/utils/Asset.h +++ b/include/utils/Asset.h @@ -63,11 +63,7 @@ public: enum { /* data larger than this does not get uncompressed into a buffer */ -#ifdef HAVE_ANDROID_OS - UNCOMPRESS_DATA_MAX = 1 * 1024 * 1024 -#else - UNCOMPRESS_DATA_MAX = 2 * 1024 * 1024 -#endif + UNCOMPRESS_DATA_MAX = 3 * 1024 * 1024 }; /* diff --git a/libs/audioflinger/Android.mk b/libs/audioflinger/Android.mk index 870c0b8..3082813 100644 --- a/libs/audioflinger/Android.mk +++ b/libs/audioflinger/Android.mk @@ -75,6 +75,7 @@ include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ + AudioDSP.cpp.arm \ AudioFlinger.cpp \ AudioMixer.cpp.arm \ AudioResampler.cpp.arm \ diff --git a/libs/audioflinger/AudioDSP.cpp b/libs/audioflinger/AudioDSP.cpp new file mode 100644 index 0000000..c56773b --- /dev/null +++ b/libs/audioflinger/AudioDSP.cpp @@ -0,0 +1,651 @@ +/* //device/include/server/AudioFlinger/AudioDSP.cpp +** +** Copyright 2010, Antti S. Lankila +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT 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 <math.h> +#include <stdio.h> + +#include "AudioDSP.h" + +namespace android { + +/* Keep this in sync with AudioMixer's FP decimal count. We + * use this count to generate the dither for ditherAndClamp(), + * among other things. */ +static const int32_t fixedPointDecimals = 12; +static const int32_t fixedPointBits = (1 << fixedPointDecimals) - 1; + +static int16_t toFixedPoint(float x) +{ + return int16_t(x * (1 << fixedPointDecimals) + 0.5f); +} + + +/*************************************************************************** + * Delay * + ***************************************************************************/ +Delay::Delay() + : mState(0), mIndex(0), mLength(0) +{ +} + +Delay::~Delay() +{ + if (mState != 0) { + delete[] mState; + mState = 0; + } +} + +void Delay::setParameters(float samplingFrequency, float time) +{ + mLength = int32_t(time * samplingFrequency + 0.5f); + if (mState != 0) { + delete[] mState; + } + mState = new int32_t[mLength]; + memset(mState, 0, mLength * sizeof(int32_t)); + mIndex = 0; +} + +inline int32_t Delay::process(int32_t x0) +{ + int32_t y0 = mState[mIndex]; + mState[mIndex] = x0; + mIndex = (mIndex + 1) % mLength; + return y0; +} + + +/*************************************************************************** + * Allpass * + ***************************************************************************/ +Allpass::Allpass() + : mK(0), mState(0), mIndex(0), mLength(0) +{ +} + +Allpass::~Allpass() +{ + if (mState != 0) { + delete[] mState; + mState = 0; + } +} + +void Allpass::setParameters(float samplingFrequency, float k, float time) +{ + mK = toFixedPoint(k); + mLength = int32_t(time * samplingFrequency + 0.5f); + if (mState != 0) { + delete[] mState; + } + mState = new int32_t[mLength]; + memset(mState, 0, mLength * sizeof(int32_t)); + mIndex = 0; +} + +inline int32_t Allpass::process(int32_t x0) +{ + int32_t tmp = x0 - mK * (mState[mIndex] >> fixedPointDecimals); + int32_t y0 = mState[mIndex] + mK * (tmp >> fixedPointDecimals); + mState[mIndex] = tmp; + mIndex = (mIndex + 1) % mLength; + return y0; +} + + +/*************************************************************************** + * Biquad * + ***************************************************************************/ +Biquad::Biquad() + : mB0(0), mY0(0) +{ + state.i32.mA = 0; + state.i32.mB = 0; + state.i32.mX = 0; + state.i32.mY = 0; +} + +void Biquad::setCoefficients(float a0, float a1, float a2, float b0, float b1, float b2) +{ + state.i16.mA1 = -toFixedPoint(a1/a0); + state.i16.mA2 = -toFixedPoint(a2/a0); + mB0 = toFixedPoint(b0/a0); + state.i16.mB1 = toFixedPoint(b1/a0); + state.i16.mB2 = toFixedPoint(b2/a0); +} + +void Biquad::setRC(float center_frequency, float sampling_frequency) +{ + float DT_div_RC = 2 * (float) M_PI * center_frequency / sampling_frequency; + float b0 = DT_div_RC / (1 + DT_div_RC); + float a1 = -1 + b0; + + setCoefficients(1, a1, 0, b0, 0, 0); +} + +void Biquad::reset() +{ + mY0 = 0; + state.i32.mX = 0; + state.i32.mY = 0; +} + +/* + * Peaking equalizer, low shelf and high shelf are taken from + * the good old Audio EQ Cookbook by Robert Bristow-Johnson. + */ +void Biquad::setPeakingEqualizer(float center_frequency, float sampling_frequency, float db_gain, float bandwidth) +{ + float w0 = 2 * (float) M_PI * center_frequency / sampling_frequency; + float A = powf(10, db_gain/40); + + float alpha = sinf(w0)/2 * sinhf( logf(2)/2 * bandwidth * w0/sinf(w0) ); + float b0 = 1 + alpha*A; + float b1 = -2*cosf(w0); + float b2 = 1 - alpha*A; + float a0 = 1 + alpha/A; + float a1 = -2*cosf(w0); + float a2 = 1 - alpha/A; + + setCoefficients(a0, a1, a2, b0, b1, b2); +} + +void Biquad::setLowShelf(float center_frequency, float sampling_frequency, float db_gain, float slope) +{ + float w0 = 2 * (float) M_PI * center_frequency / sampling_frequency; + float A = powf(10, db_gain/40); + float alpha = sinf(w0)/2 * sqrtf( (A + 1/A)*(1/slope - 1) + 2 ); + + float b0 = A*( (A+1) - (A-1)*cosf(w0) + 2*sqrtf(A)*alpha ); + float b1 = 2*A*( (A-1) - (A+1)*cosf(w0) ); + float b2 = A*( (A+1) - (A-1)*cosf(w0) - 2*sqrtf(A)*alpha ); + float a0 = (A+1) + (A-1)*cosf(w0) + 2*sqrtf(A)*alpha ; + float a1 = -2*( (A-1) + (A+1)*cosf(w0) ); + float a2 = (A+1) + (A-1)*cosf(w0) - 2*sqrtf(A)*alpha ; + + setCoefficients(a0, a1, a2, b0, b1, b2); +} + +void Biquad::setHighShelf(float center_frequency, float sampling_frequency, float db_gain, float slope) +{ + float w0 = 2 * (float) M_PI * center_frequency / sampling_frequency; + float A = powf(10, db_gain/40); + float alpha = sinf(w0)/2 * sqrtf( (A + 1/A)*(1/slope - 1) + 2 ); + + float b0 = A*( (A+1) + (A-1)*cosf(w0) + 2*sqrtf(A)*alpha ); + float b1 = -2*A*( (A-1) + (A+1)*cosf(w0) ); + float b2 = A*( (A+1) + (A-1)*cosf(w0) - 2*sqrtf(A)*alpha ); + float a0 = (A+1) - (A-1)*cosf(w0) + 2*sqrtf(A)*alpha ; + float a1 = 2*( (A-1) - (A+1)*cosf(w0) ); + float a2 = (A+1) - (A-1)*cosf(w0) - 2*sqrtf(A)*alpha ; + + setCoefficients(a0, a1, a2, b0, b1, b2); +} + +void Biquad::setBandPass(float center_frequency, float sampling_frequency, float resonance) +{ + float w0 = 2 * (float) M_PI * center_frequency / sampling_frequency; + float alpha = sinf(w0) / (2*resonance); + + float b0 = sinf(w0)/2; + float b1 = 0; + float b2 = -sinf(w0)/2; + float a0 = 1 + alpha; + float a1 = -2*cosf(w0); + float a2 = 1 - alpha; + + setCoefficients(a0, a1, a2, b0, b1, b2); +} + +/* returns output scaled by fixedPoint factor */ +inline int32_t Biquad::process(int16_t x0) +{ + /* mY0 holds error from previous integer truncation. */ + int32_t y0 = mY0 + mB0 * x0; + +#if defined(__arm__) && !defined(__thumb__) + asm( + "smlatt %[y0], %[i], %[j], %[y0]\n" + "smlabb %[y0], %[i], %[j], %[y0]\n" + "smlatt %[y0], %[k], %[l], %[y0]\n" + "smlabb %[y0], %[k], %[l], %[y0]\n" + : [y0]"+r"(y0) + : [i]"r"(state.i32.mA), [j]"r"(state.i32.mY), + [k]"r"(state.i32.mB), [l]"r"(state.i32.mX) + : ); + + /* GCC is going to issue loads for the state.i16, so I do it + * like this because the state.i32 is already in registers. + * ARM appears to have instructions that can handle these + * bit manipulations well, such as "orr r0, r0, r1, lsl #16". + */ + state.i32.mY = (state.i32.mY << 16) | ((y0 >> fixedPointDecimals) & 0xffff); + state.i32.mX = (state.i32.mX << 16) | (x0 & 0xffff); +#else + y0 += state.i16.mB1 * state.i16.mX1 + + state.i16.mB2 * state.i16.mX2 + + state.i16.mY1 * state.i16.mA1 + + state.i16.mY2 * state.i16.mA2; + + state.i16.mY2 = state.i16.mY1; + state.i16.mY1 = y0 >> fixedPointDecimals; + + state.i16.mX2 = state.i16.mX1; + state.i16.mX1 = x0; +#endif + + mY0 = y0 & fixedPointBits; + return y0; +} + + +/*************************************************************************** + * Effect * + ***************************************************************************/ +Effect::Effect() +{ + configure(44100); +} + +Effect::~Effect() { +} + +void Effect::configure(const float samplingFrequency) { + mSamplingFrequency = samplingFrequency; +} + + +EffectCompression::EffectCompression() + : mCompressionRatio(2.0) +{ +} + +EffectCompression::~EffectCompression() +{ +} + +void EffectCompression::configure(const float samplingFrequency) +{ + Effect::configure(samplingFrequency); + mWeighter.setBandPass(1000, samplingFrequency, sqrtf(2)/2); +} + +void EffectCompression::setRatio(float compressionRatio) +{ + mCompressionRatio = compressionRatio; +} + +void EffectCompression::process(int32_t* inout, int32_t frames) +{ +} + +float EffectCompression::estimateLevel(const int16_t* audioData, int32_t frames, int32_t samplesPerFrame) +{ + mWeighter.reset(); + uint32_t power = 0; + uint32_t powerFraction = 0; + for (int32_t i = 0; i < frames; i ++) { + int16_t tmp = *audioData; + audioData += samplesPerFrame; + + int32_t out = mWeighter.process(tmp) >> 12; + powerFraction += out * out; + power += powerFraction >> 16; + powerFraction &= 0xffff; + } + + /* peak-to-peak is -32768 to 32767, but we are squared here. */ + return (65536.0f * power + powerFraction) / (32768.0f * 32768.0f) / frames; +} + + +EffectTone::EffectTone() +{ + for (int32_t i = 0; i < 5; i ++) { + mBand[i] = 0; + } + for (int32_t i = 0; i < 5; i ++) { + setBand(i, 0); + } +} + +EffectTone::~EffectTone() { + for (int32_t i = 0; i < 4; i ++) { + delete &mFilterL[i]; + delete &mFilterR[i]; + } +} + +void EffectTone::configure(const float samplingFrequency) { + Effect::configure(samplingFrequency); + refreshBands(); +} + +void EffectTone::setBand(int32_t band, float dB) +{ + mBand[band] = dB; + refreshBands(); +} + +void EffectTone::refreshBands() { + mGain = toFixedPoint(powf(10, mBand[0] / 20)); + + for (int32_t band = 0; band < 3; band ++) { + float dB = mBand[band + 1] - mBand[0]; + float centerFrequency = 250.0f * powf(4, band); + + mFilterL[band].setPeakingEqualizer(centerFrequency, mSamplingFrequency, dB, 3.0f); + mFilterR[band].setPeakingEqualizer(centerFrequency, mSamplingFrequency, dB, 3.0f); + } + + { + int32_t band = 3; + + float dB = mBand[band + 1] - mBand[0]; + float centerFrequency = 250.0f * powf(4, band); + + mFilterL[band].setHighShelf(centerFrequency * 0.5f, mSamplingFrequency, dB, 1.0f); + mFilterR[band].setHighShelf(centerFrequency * 0.5f, mSamplingFrequency, dB, 1.0f); + } +} + +void EffectTone::process(int32_t* inout, int32_t frames) +{ + for (int32_t i = 0; i < frames; i ++) { + int32_t tmpL = inout[0] >> fixedPointDecimals; + int32_t tmpR = inout[1] >> fixedPointDecimals; + /* 16 bits */ + + /* bass control is really a global gain compensated by other + * controls */ + tmpL = tmpL * mGain; + tmpR = tmpR * mGain; + /* 28 bits */ + + /* evaluate the other filters. + * I'm ignoring the integer truncation problem here, but in reality + * it should be accounted for. */ + for (int32_t j = 0; j < 4; j ++) { + tmpL = mFilterL[j].process(tmpL >> fixedPointDecimals); + tmpR = mFilterR[j].process(tmpR >> fixedPointDecimals); + } + /* 28 bits */ + + inout[0] = tmpL; + inout[1] = tmpR; + inout += 2; + } +} + +EffectHeadphone::EffectHeadphone() + : mDeep(true), mWide(true), + mDelayDataL(0), mDelayDataR(0) +{ + setLevel(0); +} + +EffectHeadphone::~EffectHeadphone() +{ + delete &mReverbDelayL; + delete &mReverbDelayR; + delete &mDelayL; + delete &mDelayR; + for (int32_t i = 0; i < 3; i ++) { + delete &mAllpassL[i]; + delete &mAllpassR[i]; + } + delete &mLowpassL; + delete &mLowpassR; +} + +void EffectHeadphone::configure(const float samplingFrequency) { + Effect::configure(samplingFrequency); + + mReverbDelayL.setParameters(mSamplingFrequency, 0.030f); + mReverbDelayR.setParameters(mSamplingFrequency, 0.030f); + mDelayL.setParameters(mSamplingFrequency, 0.00045f); + mDelayR.setParameters(mSamplingFrequency, 0.00045f); + mAllpassL[0].setParameters(mSamplingFrequency, 0.4f, 0.00031f); + mAllpassR[0].setParameters(mSamplingFrequency, 0.4f, 0.00031f); + mAllpassL[1].setParameters(mSamplingFrequency, 0.4f, 0.00021f); + mAllpassR[1].setParameters(mSamplingFrequency, 0.4f, 0.00021f); + mAllpassL[2].setParameters(mSamplingFrequency, 0.4f, 0.00011f); + mAllpassR[2].setParameters(mSamplingFrequency, 0.4f, 0.00011f); + mLowpassL.setRC(4000.0f, mSamplingFrequency); + mLowpassR.setRC(4000.0f, mSamplingFrequency); +} + +void EffectHeadphone::setDeep(bool deep) +{ + mDeep = deep; +} + +void EffectHeadphone::setWide(bool wide) +{ + mWide = wide; +} + +void EffectHeadphone::setLevel(float level) +{ + mLevel = toFixedPoint(powf(10, (level - 15.0f) / 20.0f)); +} + +void EffectHeadphone::process(int32_t* inout, int32_t frames) +{ + for (int32_t i = 0; i < frames; i ++) { + /* calculate reverb wet into dataL, dataR */ + int32_t dryL = inout[0]; + int32_t dryR = inout[1]; + int32_t dataL = dryL; + int32_t dataR = dryR; + /* 28 bits */ + + if (mDeep) { + dataL += mDelayDataR; + dataR += mDelayDataL; + } + + dataL = mReverbDelayL.process(dataL); + dataR = mReverbDelayR.process(dataR); + /* 28 bits */ + + if (mWide) { + dataR = -dataR; + } + + dataL = (dataL >> fixedPointDecimals) * mLevel; + dataR = (dataR >> fixedPointDecimals) * mLevel; + /* 28 bits */ + + mDelayDataL = dataL; + mDelayDataR = dataR; + + /* Reverb wet done; mix with dry and do headphone virtualization */ + dataL += dryL; + dataR += dryR; + + /* Add fixed ear-to-ear propagation delay of about 10 cm, based + * on the idea that ear-to-ear distance is 30 cm and the speakers + * are placed in front of the listener, which means that the actual + * time delay will be somewhat less than the maximum. */ + dataL = mDelayL.process(dataL); + dataR = mDelayR.process(dataR); + for (int32_t j = 0; j < 3; j ++) { + /* Confuse phase, simulating shoulder echoes and whatnot. */ + dataL = mAllpassL[j].process(dataL); + dataR = mAllpassR[j].process(dataR); + } + + /* Lowpass filter to estimate head shadow. */ + dataL = mLowpassL.process(dataL >> fixedPointDecimals); + dataR = mLowpassR.process(dataR >> fixedPointDecimals); + /* 28 bits */ + + /* Mix right-to-left and vice versa. */ + inout[0] += dataR; + inout[1] += dataL; + inout += 2; + } +} + + +/*************************************************************************** + * AudioDSP * + ***************************************************************************/ +const String8 AudioDSP::keyCompressionEnable = String8("dsp.compression.enable"); +const String8 AudioDSP::keyCompressionRatio = String8("dsp.compression.ratio"); + +const String8 AudioDSP::keyToneEnable = String8("dsp.tone.enable"); +const String8 AudioDSP::keyToneEq1 = String8("dsp.tone.eq1"); +const String8 AudioDSP::keyToneEq2 = String8("dsp.tone.eq2"); +const String8 AudioDSP::keyToneEq3 = String8("dsp.tone.eq3"); +const String8 AudioDSP::keyToneEq4 = String8("dsp.tone.eq4"); +const String8 AudioDSP::keyToneEq5 = String8("dsp.tone.eq5"); + +const String8 AudioDSP::keyHeadphoneEnable = String8("dsp.headphone.enable"); +const String8 AudioDSP::keyHeadphoneDeep = String8("dsp.headphone.deep"); +const String8 AudioDSP::keyHeadphoneWide = String8("dsp.headphone.wide"); +const String8 AudioDSP::keyHeadphoneLevel = String8("dsp.headphone.level"); + +AudioDSP::AudioDSP() + : mCompressionEnable(false), mToneEnable(false), mHeadphoneEnable(false) +{ +} + +AudioDSP::~AudioDSP() +{ + delete &mCompression; + delete &mTone; + delete &mHeadphone; +} + +void AudioDSP::configure(const float samplingRate) +{ + mCompression.configure(samplingRate); + mTone.configure(samplingRate); + mHeadphone.configure(samplingRate); +} + +void AudioDSP::setParameters(const String8& keyValuePairs) +{ + int intValue; + float floatValue; + status_t result; + AudioParameter param = AudioParameter(keyValuePairs); + + result = param.getInt(keyCompressionEnable, intValue); + if (result == NO_ERROR) { + mCompressionEnable = intValue != 0; + } + result = param.getFloat(keyCompressionRatio, floatValue); + if (result == NO_ERROR) { + mCompression.setRatio(floatValue); + } + + result = param.getInt(keyToneEnable, intValue); + if (result == NO_ERROR) { + mToneEnable = intValue != 0; + } + result = param.getFloat(keyToneEq1, floatValue); + if (result == NO_ERROR) { + mTone.setBand(0, floatValue); + } + result = param.getFloat(keyToneEq2, floatValue); + if (result == NO_ERROR) { + mTone.setBand(1, floatValue); + } + result = param.getFloat(keyToneEq3, floatValue); + if (result == NO_ERROR) { + mTone.setBand(2, floatValue); + } + result = param.getFloat(keyToneEq4, floatValue); + if (result == NO_ERROR) { + mTone.setBand(3, floatValue); + } + result = param.getFloat(keyToneEq5, floatValue); + if (result == NO_ERROR) { + mTone.setBand(4, floatValue); + } + + result = param.getInt(keyHeadphoneEnable, intValue); + if (result == NO_ERROR) { + mHeadphoneEnable = intValue != 0; + } + result = param.getInt(keyHeadphoneDeep, intValue); + if (result == NO_ERROR) { + mHeadphone.setDeep(intValue != 0); + } + result = param.getInt(keyHeadphoneWide, intValue); + if (result == NO_ERROR) { + mHeadphone.setWide(intValue != 0); + } + result = param.getFloat(keyHeadphoneLevel, floatValue); + if (result == NO_ERROR) { + mHeadphone.setLevel(floatValue); + } +} + +int32_t AudioDSP::estimateLevel(const int16_t* input, int32_t frames, int32_t samplesPerFrame) +{ + if (! mCompressionEnable) { + return 65536; + } + + /* Analyze both channels separately, pick the maximum power measured. */ + float maximumPowerSquared = 0; + for (int channel = 0; channel < samplesPerFrame; channel ++) { + float candidatePowerSquared = mCompression.estimateLevel(input + channel, frames, samplesPerFrame); + if (candidatePowerSquared > maximumPowerSquared) { + maximumPowerSquared = candidatePowerSquared; + } + } + + /* -100 .. 0 dB. */ + float signalPowerDb = logf(maximumPowerSquared + 1e-10f) / logf(10.0f) * 10.0f; + + /* target 83 dB SPL, and add 6 dB to compensate for the weighter, whose + * peak is at -3 dB. */ + signalPowerDb += 96.0f - 83.0f + 6.0f; + + /* now we have an estimate of the signal power, with 0 level around 83 dB. + * we now select the level to boost to. */ + float desiredLevelDb = signalPowerDb / mCompression.mCompressionRatio; + + /* turn back to multiplier */ + float correctionDb = desiredLevelDb - signalPowerDb; + + /* Reduce extreme boost by a smooth ramp. + * New range -50 .. 0 dB */ + correctionDb -= powf(correctionDb/100, 2.0f) * (100.0f / 2.0f); + + return int32_t(65536.0f * powf(10.0f, correctionDb / 20.0f)); +} + +/* input is 28-bit interleaved stereo in integer format */ +void AudioDSP::process(int32_t* audioData, int32_t frames) +{ + if (mToneEnable) { + mTone.process(audioData, frames); + } + + if (mHeadphoneEnable) { + mHeadphone.process(audioData, frames); + } +} + +} diff --git a/libs/audioflinger/AudioDSP.h b/libs/audioflinger/AudioDSP.h new file mode 100644 index 0000000..99cc299 --- /dev/null +++ b/libs/audioflinger/AudioDSP.h @@ -0,0 +1,173 @@ +/* //device/include/server/AudioFlinger/AudioDSP.h +** +** Copyright 2010, Antti S Lankila +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT 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_AUDIODSP_H +#define ANDROID_AUDIODSP_H 1 + +#include <media/AudioSystem.h> +#include <utils/String8.h> + +namespace android { + +class Delay { + int32_t* mState; + int32_t mIndex; + int32_t mLength; + + public: + Delay(); + ~Delay(); + void setParameters(float rate, float time); + int32_t process(int32_t x0); +}; + +class Allpass { + int32_t mK; + int32_t* mState; + int32_t mIndex; + int32_t mLength; + + public: + Allpass(); + ~Allpass(); + void setParameters(float rate, float k, float time); + int32_t process(int32_t x0); +}; + +class Biquad { + union { + struct { + int32_t mA, mB, mY, mX; + } i32; + struct { + int16_t mA1, mA2, mB1, mB2, mY1, mY2, mX1, mX2; + } i16; + } state; + int16_t mB0, mY0; + + void setCoefficients(float a0, float a1, float a2, float b0, float b1, float b2); + + public: + Biquad(); + void setRC(float cf, float sf); + void setPeakingEqualizer(float cf, float sf, float gain, float bw); + void setBandPass(float cf, float sf, float resonance); + void setLowShelf(float cf, float sf, float gain, float slope); + void setHighShelf(float cf, float sf, float gain, float slope); + void reset(); + int32_t process(int16_t x0); +}; + +class Effect { + protected: + float mSamplingFrequency; + + public: + Effect(); + virtual ~Effect(); + virtual void configure(const float samplingFrequency); + virtual void process(int32_t* inout, int32_t frames) = 0; +}; + +class EffectCompression : public Effect { + private: + Biquad mWeighter; + + public: + float mCompressionRatio; + + EffectCompression(); + ~EffectCompression(); + void configure(const float samplingFrequency); + void setRatio(float compressionRatio); + void process(int32_t* inout, int32_t frames); + float estimateLevel(const int16_t* audiodata, int32_t frames, int32_t framesPerSample); +}; + +class EffectTone : public Effect { + float mBand[5]; + int32_t mGain; + Biquad mFilterL[4], mFilterR[4]; + + void refreshBands(); + + public: + EffectTone(); + ~EffectTone(); + void configure(const float samplingFrequency); + void setBand(int32_t idx, float dB); + void process(int32_t* inout, int32_t frames); +}; + +class EffectHeadphone : public Effect { + bool mDeep, mWide; + int32_t mLevel; + + Delay mReverbDelayL, mReverbDelayR; + int32_t mDelayDataL, mDelayDataR; + Delay mDelayL, mDelayR; + Allpass mAllpassL[3], mAllpassR[3]; + Biquad mLowpassL, mLowpassR; + + public: + EffectHeadphone(); + ~EffectHeadphone(); + void configure(const float samplingFrequency); + void setDeep(bool enable); + void setWide(bool enable); + void setLevel(float level); + void process(int32_t* inout, int32_t frames); +}; + +class AudioDSP { + bool mCompressionEnable; + EffectCompression mCompression; + + bool mToneEnable; + EffectTone mTone; + + bool mHeadphoneEnable; + EffectHeadphone mHeadphone; + + public: + AudioDSP(); + ~AudioDSP(); + + void configure(float samplingRate); + void setParameters(const String8& keyValuePairs); + int32_t estimateLevel(const int16_t* audiodata, int32_t frames, int32_t samplesPerFrame); + void process(int32_t* inputInterleaved, int32_t frames); + + static const String8 keyCompressionEnable; + static const String8 keyCompressionRatio; + + static const String8 keyToneEnable; + static const String8 keyToneEq1; + static const String8 keyToneEq2; + static const String8 keyToneEq3; + static const String8 keyToneEq4; + static const String8 keyToneEq5; + + static const String8 keyHeadphoneEnable; + static const String8 keyHeadphoneDeep; + static const String8 keyHeadphoneWide; + static const String8 keyHeadphoneLevel; +}; + +} + +#endif diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp index 2414e8d..890692d 100644 --- a/libs/audioflinger/AudioFlinger.cpp +++ b/libs/audioflinger/AudioFlinger.cpp @@ -605,6 +605,8 @@ status_t AudioFlinger::setParameters(int ioHandle, const String8& keyValuePairs) // ioHandle == 0 means the parameters are global to the audio hardware interface if (ioHandle == 0) { + /* Set global DSP parameters, if any. */ + mDsp.setParameters(keyValuePairs); AutoMutex lock(mHardwareLock); mHardwareStatus = AUDIO_SET_PARAMETER; result = mAudioHardware->setParameters(keyValuePairs); @@ -1287,7 +1289,7 @@ AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, Aud mAudioMixer(0) { mType = PlaybackThread::MIXER; - mAudioMixer = new AudioMixer(mFrameCount, mSampleRate); + mAudioMixer = new AudioMixer(mFrameCount, mSampleRate, audioFlinger->mDsp); // FIXME - Current mixer implementation only supports stereo output if (mChannelCount == 1) { @@ -1727,7 +1729,7 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l() if (status == NO_ERROR && reconfig) { delete mAudioMixer; readOutputParameters(); - mAudioMixer = new AudioMixer(mFrameCount, mSampleRate); + mAudioMixer = new AudioMixer(mFrameCount, mSampleRate, mAudioFlinger->mDsp); for (size_t i = 0; i < mTracks.size() ; i++) { int name = getTrackName_l(); if (name < 0) break; @@ -3067,7 +3069,11 @@ void AudioFlinger::PlaybackThread::OutputTrack::clearBufferQueue() AudioFlinger::Client::Client(const sp<AudioFlinger>& audioFlinger, pid_t pid) : RefBase(), mAudioFlinger(audioFlinger), +#ifdef USE_ECLAIR_MEMORYDEALER + mMemoryDealer(new MemoryDealer(1024*1024)), +#else mMemoryDealer(new MemoryDealer(1024*1024, "AudioFlinger::Client")), +#endif mPid(pid) { // 1 MB of address space is good for 32 tracks, 8 buffers each, 4 KB/buffer diff --git a/libs/audioflinger/AudioFlinger.h b/libs/audioflinger/AudioFlinger.h index 739ec33..05915e5 100644 --- a/libs/audioflinger/AudioFlinger.h +++ b/libs/audioflinger/AudioFlinger.h @@ -31,13 +31,18 @@ #include <utils/Atomic.h> #include <utils/Errors.h> #include <utils/threads.h> +#ifdef USE_ECLAIR_MEMORYDEALER #include <binder/MemoryDealer.h> +#else +#include <binder/MemoryDealer.h> +#endif #include <utils/SortedVector.h> #include <utils/Vector.h> #include <hardware_legacy/AudioHardwareInterface.h> #include "AudioBufferProvider.h" +#include "AudioDSP.h" namespace android { @@ -798,6 +803,8 @@ private: SortedVector< sp<IBinder> > mNotificationClients; int mNextThreadId; + + AudioDSP mDsp; }; // ---------------------------------------------------------------------------- diff --git a/libs/audioflinger/AudioMixer.cpp b/libs/audioflinger/AudioMixer.cpp index 19a442a..e5985d7 100644 --- a/libs/audioflinger/AudioMixer.cpp +++ b/libs/audioflinger/AudioMixer.cpp @@ -38,11 +38,18 @@ static inline int16_t clamp16(int32_t sample) return sample; } +static int32_t seed = 1; +inline static int32_t prng() { + seed = (seed * 12345) + 1103515245; + return int32_t(seed & 0xfff); +} + // ---------------------------------------------------------------------------- -AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate) - : mActiveTrack(0), mTrackNames(0), mSampleRate(sampleRate) +AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate, AudioDSP& dsp) + : mActiveTrack(0), mTrackNames(0), mSampleRate(sampleRate), mDsp(dsp) { + mDsp.configure(sampleRate); mState.enabledTracks= 0; mState.needsChanged = 0; mState.frameCount = frameCount; @@ -202,18 +209,10 @@ status_t AudioMixer::setParameter(int target, int name, int value) if ((uint32_t(name-VOLUME0) < MAX_NUM_CHANNELS)) { track_t& track = mState.tracks[ mActiveTrack ]; if (track.volume[name-VOLUME0] != value) { - track.prevVolume[name-VOLUME0] = track.volume[name-VOLUME0] << 16; track.volume[name-VOLUME0] = value; if (target == VOLUME) { track.prevVolume[name-VOLUME0] = value << 16; track.volumeInc[name-VOLUME0] = 0; - } else { - int32_t d = (value<<16) - track.prevVolume[name-VOLUME0]; - int32_t volInc = d / int32_t(mState.frameCount); - track.volumeInc[name-VOLUME0] = volInc; - if (volInc == 0) { - track.prevVolume[name-VOLUME0] = value << 16; - } } invalidateState(1<<mActiveTrack); } @@ -244,15 +243,34 @@ bool AudioMixer::track_t::doesResample() const return resampler != 0; } -inline -void AudioMixer::track_t::adjustVolumeRamp() +void AudioMixer::track_t::adjustVolumeRamp(AudioDSP& dsp, size_t frames) { - for (int i=0 ; i<2 ; i++) { - if (((volumeInc[i]>0) && (((prevVolume[i]+volumeInc[i])>>16) >= volume[i])) || - ((volumeInc[i]<0) && (((prevVolume[i]+volumeInc[i])>>16) <= volume[i]))) { - volumeInc[i] = 0; - prevVolume[i] = volume[i]<<16; + int32_t dynamicRangeCompressionFactor = dsp.estimateLevel( + static_cast<const int16_t*>(in), int32_t(frames), channelCount + ); + + for (int i = 0; i < 2; i ++) { + /* Ramp from current to new volume level if necessary */ + int32_t desiredVolume = volume[i] * dynamicRangeCompressionFactor; + int32_t d = desiredVolume - prevVolume[i]; + + /* limit change rate to smooth the compressor. */ + int32_t volChangeLimit = (prevVolume[i] >> 10); + + volChangeLimit += 1; + int32_t volInc = d / int32_t(frames); + if (volInc < -(volChangeLimit)) { + volInc = -(volChangeLimit); } + + /* Make ramps up slower, but ramps down fast. */ + volChangeLimit >>= 3; + volChangeLimit -= 1; + if (volInc > volChangeLimit) { + volInc = volChangeLimit; + } + + volumeInc[i] = volInc; } } @@ -267,11 +285,11 @@ status_t AudioMixer::setBufferProvider(AudioBufferProvider* buffer) void AudioMixer::process(void* output) { - mState.hook(&mState, output); + mState.hook(&mState, output, mDsp); } -void AudioMixer::process__validate(state_t* state, void* output) +void AudioMixer::process__validate(state_t* state, void* output, AudioDSP& dsp) { LOGW_IF(!state->needsChanged, "in process__validate() but nothing's invalid"); @@ -356,11 +374,6 @@ void AudioMixer::process__validate(state_t* state, void* output) state->resampleTemp = 0; } state->hook = process__genericNoResampling; - if (all16BitsStereoNoResample && !volumeRamp) { - if (countActiveTracks == 1) { - state->hook = process__OneTrack16BitsStereoNoResampling; - } - } } } @@ -369,7 +382,7 @@ void AudioMixer::process__validate(state_t* state, void* output) countActiveTracks, state->enabledTracks, all16BitsStereoNoResample, resampling, volumeRamp); - state->hook(state, output); + state->hook(state, output, dsp); // Now that the volume ramp has been done, set optimal state and // track hooks for subsequent mixer process @@ -390,10 +403,6 @@ void AudioMixer::process__validate(state_t* state, void* output) } if (allMuted) { state->hook = process__nop; - } else if (!resampling && all16BitsStereoNoResample) { - if (countActiveTracks == 1) { - state->hook = process__OneTrack16BitsStereoNoResampling; - } } } } @@ -481,7 +490,7 @@ int32_t mulRL(int left, uint32_t inRL, uint32_t vRL) } -void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp) +void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp, AudioDSP& dsp) { t->resampler->setSampleRate(t->sampleRate); @@ -490,7 +499,7 @@ void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFram t->resampler->setVolume(UNITY_GAIN, UNITY_GAIN); memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t)); t->resampler->resample(temp, outFrameCount, t->bufferProvider); - volumeRampStereo(t, out, outFrameCount, temp); + volumeRampStereo(t, out, outFrameCount, temp, dsp); } // constant gain @@ -500,12 +509,13 @@ void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFram } } -void AudioMixer::track__nop(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp) +void AudioMixer::track__nop(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp, AudioDSP& dsp) { } -void AudioMixer::volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp) +void AudioMixer::volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, AudioDSP& dsp) { + t->adjustVolumeRamp(dsp, frameCount); int32_t vl = t->prevVolume[0]; int32_t vr = t->prevVolume[1]; const int32_t vlInc = t->volumeInc[0]; @@ -525,14 +535,14 @@ void AudioMixer::volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, i t->prevVolume[0] = vl; t->prevVolume[1] = vr; - t->adjustVolumeRamp(); } -void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp) +void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, AudioDSP& dsp) { int16_t const *in = static_cast<int16_t const *>(t->in); // ramp gain + t->adjustVolumeRamp(dsp, frameCount); if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) { int32_t vl = t->prevVolume[0]; int32_t vr = t->prevVolume[1]; @@ -552,7 +562,6 @@ void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount t->prevVolume[0] = vl; t->prevVolume[1] = vr; - t->adjustVolumeRamp(); } // constant gain @@ -569,11 +578,12 @@ void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount t->in = in; } -void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, int32_t* temp) +void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, AudioDSP& dsp) { int16_t const *in = static_cast<int16_t const *>(t->in); // ramp gain + t->adjustVolumeRamp(dsp, frameCount); if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) { int32_t vl = t->prevVolume[0]; int32_t vr = t->prevVolume[1]; @@ -594,7 +604,6 @@ void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, t->prevVolume[0] = vl; t->prevVolume[1] = vr; - t->adjustVolumeRamp(); } // constant gain else { @@ -612,11 +621,20 @@ void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, void AudioMixer::ditherAndClamp(int32_t* out, int32_t const *sums, size_t c) { + int32_t oldDitherValue = prng(); for (size_t i=0 ; i<c ; i++) { int32_t l = *sums++; int32_t r = *sums++; - int32_t nl = l >> 12; - int32_t nr = r >> 12; + + /* Apply dither to output. This is the high-passed triangular + * probability density function, discussed in "A Theory of + * Nonsubtractive Dither", by Robert A. Wannamaker et al. */ + int32_t ditherValue = prng(); + int32_t dithering = oldDitherValue - ditherValue; + oldDitherValue = ditherValue; + + int32_t nl = (l + ditherValue) >> 12; + int32_t nr = (r + ditherValue) >> 12; l = clamp16(nl); r = clamp16(nr); *out++ = (r<<16) | (l & 0xFFFF); @@ -624,7 +642,7 @@ void AudioMixer::ditherAndClamp(int32_t* out, int32_t const *sums, size_t c) } // no-op case -void AudioMixer::process__nop(state_t* state, void* output) +void AudioMixer::process__nop(state_t* state, void* output, AudioDSP& dsp) { // this assumes output 16 bits stereo, no resampling memset(output, 0, state->frameCount*4); @@ -645,7 +663,7 @@ void AudioMixer::process__nop(state_t* state, void* output) } // generic code without resampling -void AudioMixer::process__genericNoResampling(state_t* state, void* output) +void AudioMixer::process__genericNoResampling(state_t* state, void* output, AudioDSP& dsp) { int32_t outTemp[BLOCKSIZE * MAX_NUM_CHANNELS] __attribute__((aligned(32))); @@ -682,7 +700,7 @@ void AudioMixer::process__genericNoResampling(state_t* state, void* output) while (outFrames) { size_t inFrames = (t.frameCount > outFrames)?outFrames:t.frameCount; if (inFrames) { - (t.hook)(&t, outTemp + (BLOCKSIZE-outFrames)*MAX_NUM_CHANNELS, inFrames, state->resampleTemp); + (t.hook)(&t, outTemp + (BLOCKSIZE-outFrames)*MAX_NUM_CHANNELS, inFrames, state->resampleTemp, dsp); t.frameCount -= inFrames; outFrames -= inFrames; } @@ -700,6 +718,7 @@ void AudioMixer::process__genericNoResampling(state_t* state, void* output) } } + dsp.process(outTemp, BLOCKSIZE); ditherAndClamp(out, outTemp, BLOCKSIZE); out += BLOCKSIZE; numFrames -= BLOCKSIZE; @@ -717,7 +736,7 @@ void AudioMixer::process__genericNoResampling(state_t* state, void* output) } // generic code with resampling -void AudioMixer::process__genericResampling(state_t* state, void* output) +void AudioMixer::process__genericResampling(state_t* state, void* output, AudioDSP& dsp) { int32_t* const outTemp = state->outputTemp; const size_t size = sizeof(int32_t) * MAX_NUM_CHANNELS * state->frameCount; @@ -736,7 +755,7 @@ void AudioMixer::process__genericResampling(state_t* state, void* output) // acquire/release the buffers because it's done by // the resampler. if ((t.needs & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) { - (t.hook)(&t, outTemp, numFrames, state->resampleTemp); + (t.hook)(&t, outTemp, numFrames, state->resampleTemp, dsp); } else { size_t outFrames = numFrames; @@ -749,167 +768,17 @@ void AudioMixer::process__genericResampling(state_t* state, void* output) // been enabled for mixing. if (t.in == NULL) break; - (t.hook)(&t, outTemp + (numFrames-outFrames)*MAX_NUM_CHANNELS, t.buffer.frameCount, state->resampleTemp); + (t.hook)(&t, outTemp + (numFrames-outFrames)*MAX_NUM_CHANNELS, t.buffer.frameCount, state->resampleTemp, dsp); outFrames -= t.buffer.frameCount; t.bufferProvider->releaseBuffer(&t.buffer); } } } + dsp.process(outTemp, numFrames); ditherAndClamp(out, outTemp, numFrames); } -// one track, 16 bits stereo without resampling is the most common case -void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state, void* output) -{ - const int i = 31 - __builtin_clz(state->enabledTracks); - const track_t& t = state->tracks[i]; - - AudioBufferProvider::Buffer& b(t.buffer); - - int32_t* out = static_cast<int32_t*>(output); - size_t numFrames = state->frameCount; - - const int16_t vl = t.volume[0]; - const int16_t vr = t.volume[1]; - const uint32_t vrl = t.volumeRL; - while (numFrames) { - b.frameCount = numFrames; - t.bufferProvider->getNextBuffer(&b); - int16_t const *in = b.i16; - - // in == NULL can happen if the track was flushed just after having - // been enabled for mixing. - if (in == NULL || ((unsigned long)in & 3)) { - memset(out, 0, numFrames*MAX_NUM_CHANNELS*sizeof(int16_t)); - LOGE_IF(((unsigned long)in & 3), "process stereo track: input buffer alignment pb: buffer %p track %d, channels %d, needs %08x", - in, i, t.channelCount, t.needs); - return; - } - size_t outFrames = b.frameCount; - - if (UNLIKELY(uint32_t(vl) > UNITY_GAIN || uint32_t(vr) > UNITY_GAIN)) { - // volume is boosted, so we might need to clamp even though - // we process only one track. - do { - uint32_t rl = *reinterpret_cast<uint32_t const *>(in); - in += 2; - int32_t l = mulRL(1, rl, vrl) >> 12; - int32_t r = mulRL(0, rl, vrl) >> 12; - // clamping... - l = clamp16(l); - r = clamp16(r); - *out++ = (r<<16) | (l & 0xFFFF); - } while (--outFrames); - } else { - do { - uint32_t rl = *reinterpret_cast<uint32_t const *>(in); - in += 2; - int32_t l = mulRL(1, rl, vrl) >> 12; - int32_t r = mulRL(0, rl, vrl) >> 12; - *out++ = (r<<16) | (l & 0xFFFF); - } while (--outFrames); - } - numFrames -= b.frameCount; - t.bufferProvider->releaseBuffer(&b); - } -} - -// 2 tracks is also a common case -void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state, void* output) -{ - int i; - uint32_t en = state->enabledTracks; - - i = 31 - __builtin_clz(en); - const track_t& t0 = state->tracks[i]; - AudioBufferProvider::Buffer& b0(t0.buffer); - - en &= ~(1<<i); - i = 31 - __builtin_clz(en); - const track_t& t1 = state->tracks[i]; - AudioBufferProvider::Buffer& b1(t1.buffer); - - int16_t const *in0; - const int16_t vl0 = t0.volume[0]; - const int16_t vr0 = t0.volume[1]; - size_t frameCount0 = 0; - - int16_t const *in1; - const int16_t vl1 = t1.volume[0]; - const int16_t vr1 = t1.volume[1]; - size_t frameCount1 = 0; - - int32_t* out = static_cast<int32_t*>(output); - size_t numFrames = state->frameCount; - int16_t const *buff = NULL; - - - while (numFrames) { - - if (frameCount0 == 0) { - b0.frameCount = numFrames; - t0.bufferProvider->getNextBuffer(&b0); - if (b0.i16 == NULL) { - if (buff == NULL) { - buff = new int16_t[MAX_NUM_CHANNELS * state->frameCount]; - } - in0 = buff; - b0.frameCount = numFrames; - } else { - in0 = b0.i16; - } - frameCount0 = b0.frameCount; - } - if (frameCount1 == 0) { - b1.frameCount = numFrames; - t1.bufferProvider->getNextBuffer(&b1); - if (b1.i16 == NULL) { - if (buff == NULL) { - buff = new int16_t[MAX_NUM_CHANNELS * state->frameCount]; - } - in1 = buff; - b1.frameCount = numFrames; - } else { - in1 = b1.i16; - } - frameCount1 = b1.frameCount; - } - - size_t outFrames = frameCount0 < frameCount1?frameCount0:frameCount1; - - numFrames -= outFrames; - frameCount0 -= outFrames; - frameCount1 -= outFrames; - - do { - int32_t l0 = *in0++; - int32_t r0 = *in0++; - l0 = mul(l0, vl0); - r0 = mul(r0, vr0); - int32_t l = *in1++; - int32_t r = *in1++; - l = mulAdd(l, vl1, l0) >> 12; - r = mulAdd(r, vr1, r0) >> 12; - // clamping... - l = clamp16(l); - r = clamp16(r); - *out++ = (r<<16) | (l & 0xFFFF); - } while (--outFrames); - - if (frameCount0 == 0) { - t0.bufferProvider->releaseBuffer(&b0); - } - if (frameCount1 == 0) { - t1.bufferProvider->releaseBuffer(&b1); - } - } - - if (buff != NULL) { - delete [] buff; - } -} - // ---------------------------------------------------------------------------- }; // namespace android diff --git a/libs/audioflinger/AudioMixer.h b/libs/audioflinger/AudioMixer.h index 15766cd..9aa92ed 100644 --- a/libs/audioflinger/AudioMixer.h +++ b/libs/audioflinger/AudioMixer.h @@ -22,6 +22,7 @@ #include <sys/types.h> #include "AudioBufferProvider.h" +#include "AudioDSP.h" #include "AudioResampler.h" namespace android { @@ -36,7 +37,7 @@ namespace android { class AudioMixer { public: - AudioMixer(size_t frameCount, uint32_t sampleRate); + AudioMixer(size_t frameCount, uint32_t sampleRate, AudioDSP& dsp); ~AudioMixer(); @@ -88,7 +89,6 @@ public: static void ditherAndClamp(int32_t* out, int32_t const *sums, size_t c); private: - enum { NEEDS_CHANNEL_COUNT__MASK = 0x00000003, NEEDS_FORMAT__MASK = 0x000000F0, @@ -116,7 +116,7 @@ private: struct state_t; - typedef void (*mix_t)(state_t* state, void* output); + typedef void (*mix_t)(state_t* state, void* output, AudioDSP& dsp); static const int BLOCKSIZE = 16; // 4 cache lines @@ -142,7 +142,7 @@ private: AudioBufferProvider* bufferProvider; mutable AudioBufferProvider::Buffer buffer; - void (*hook)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp); + void (*hook)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp, AudioDSP& dsp); void const* in; // current location in buffer AudioResampler* resampler; @@ -150,7 +150,7 @@ private: bool setResampler(uint32_t sampleRate, uint32_t devSampleRate); bool doesResample() const; - void adjustVolumeRamp(); + void adjustVolumeRamp(AudioDSP& dsp, size_t frames); }; // pad to 32-bytes to fill cache line @@ -168,23 +168,22 @@ private: int mActiveTrack; uint32_t mTrackNames; const uint32_t mSampleRate; + AudioDSP& mDsp; state_t mState __attribute__((aligned(32))); void invalidateState(uint32_t mask); - static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp); - static void track__nop(track_t* t, int32_t* out, size_t numFrames, int32_t* temp); - static void volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp); - static void track__16BitsStereo(track_t* t, int32_t* out, size_t numFrames, int32_t* temp); - static void track__16BitsMono(track_t* t, int32_t* out, size_t numFrames, int32_t* temp); - - static void process__validate(state_t* state, void* output); - static void process__nop(state_t* state, void* output); - static void process__genericNoResampling(state_t* state, void* output); - static void process__genericResampling(state_t* state, void* output); - static void process__OneTrack16BitsStereoNoResampling(state_t* state, void* output); - static void process__TwoTracks16BitsStereoNoResampling(state_t* state, void* output); + static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, AudioDSP& dsp); + static void track__nop(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, AudioDSP& dsp); + static void volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, AudioDSP& dsp); + static void track__16BitsStereo(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, AudioDSP& dsp); + static void track__16BitsMono(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, AudioDSP& dsp); + + static void process__validate(state_t* state, void* output, AudioDSP& dsp); + static void process__nop(state_t* state, void* output, AudioDSP& dsp); + static void process__genericNoResampling(state_t* state, void* output, AudioDSP& dsp); + static void process__genericResampling(state_t* state, void* output, AudioDSP& dsp); }; // ---------------------------------------------------------------------------- diff --git a/libs/binder/Android.mk b/libs/binder/Android.mk index 13dc500..85cd488 100644 --- a/libs/binder/Android.mk +++ b/libs/binder/Android.mk @@ -21,7 +21,6 @@ sources := \ IPCThreadState.cpp \ IPermissionController.cpp \ IServiceManager.cpp \ - MemoryDealer.cpp \ MemoryBase.cpp \ MemoryHeapBase.cpp \ MemoryHeapPmem.cpp \ @@ -30,6 +29,13 @@ sources := \ ProcessState.cpp \ Static.cpp +ifeq ($(BOARD_USES_ECLAIR_LIBCAMERA),true) + LOCAL_CFLAGS += -DUSE_ECLAIR_MEMORYDEALER + sources += MemoryDealerEclair.cpp +else + sources += MemoryDealer.cpp +endif + LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) diff --git a/libs/binder/MemoryDealerEclair.cpp b/libs/binder/MemoryDealerEclair.cpp new file mode 100644 index 0000000..a48a619 --- /dev/null +++ b/libs/binder/MemoryDealerEclair.cpp @@ -0,0 +1,422 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "MemoryDealer" +#define USE_ECLAIR_MEMORYDEALER + +#include <binder/MemoryDealer.h> + +#include <utils/Log.h> +#include <binder/IPCThreadState.h> +#include <utils/SortedVector.h> +#include <utils/String8.h> +#include <binder/MemoryBase.h> + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> + +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/file.h> + +namespace android { +// ---------------------------------------------------------------------------- + +HeapInterface::HeapInterface() { } +HeapInterface::~HeapInterface() { } + +// ---------------------------------------------------------------------------- + +AllocatorInterface::AllocatorInterface() { } +AllocatorInterface::~AllocatorInterface() { } + +// ---------------------------------------------------------------------------- + +class SimpleMemory : public MemoryBase { +public: + SimpleMemory(const sp<IMemoryHeap>& heap, ssize_t offset, size_t size); + virtual ~SimpleMemory(); +}; + + +// ---------------------------------------------------------------------------- + +MemoryDealer::Allocation::Allocation( + const sp<MemoryDealer>& dealer, ssize_t offset, size_t size, + const sp<IMemory>& memory) + : mDealer(dealer), mOffset(offset), mSize(size), mMemory(memory) +{ +} + +MemoryDealer::Allocation::~Allocation() +{ + if (mSize) { + /* NOTE: it's VERY important to not free allocations of size 0 because + * they're special as they don't have any record in the allocator + * and could alias some real allocation (their offset is zero). */ + mDealer->deallocate(mOffset); + } +} + +sp<IMemoryHeap> MemoryDealer::Allocation::getMemory( + ssize_t* offset, size_t* size) const +{ + return mMemory->getMemory(offset, size); +} + +// ---------------------------------------------------------------------------- + +MemoryDealer::MemoryDealer(size_t size, uint32_t flags, const char* name) + : mHeap(new SharedHeap(size, flags, name)), + mAllocator(new SimpleBestFitAllocator(size)) +{ +} + +MemoryDealer::MemoryDealer(const sp<HeapInterface>& heap) + : mHeap(heap), + mAllocator(new SimpleBestFitAllocator(heap->virtualSize())) +{ +} + +MemoryDealer::MemoryDealer( const sp<HeapInterface>& heap, + const sp<AllocatorInterface>& allocator) + : mHeap(heap), mAllocator(allocator) +{ +} + +MemoryDealer::~MemoryDealer() +{ +} + +sp<IMemory> MemoryDealer::allocate(size_t size, uint32_t flags) +{ + sp<IMemory> memory; + const ssize_t offset = allocator()->allocate(size, flags); + if (offset >= 0) { + sp<IMemory> new_memory = heap()->mapMemory(offset, size); + if (new_memory != 0) { + memory = new Allocation(this, offset, size, new_memory); + } else { + LOGE("couldn't map [%8lx, %u]", offset, size); + if (size) { + /* NOTE: it's VERY important to not free allocations of size 0 + * because they're special as they don't have any record in the + * allocator and could alias some real allocation + * (their offset is zero). */ + allocator()->deallocate(offset); + } + } + } + return memory; +} + +void MemoryDealer::deallocate(size_t offset) +{ + allocator()->deallocate(offset); +} + +void MemoryDealer::dump(const char* what, uint32_t flags) const +{ + allocator()->dump(what, flags); +} + +const sp<HeapInterface>& MemoryDealer::heap() const { + return mHeap; +} + +const sp<AllocatorInterface>& MemoryDealer::allocator() const { + return mAllocator; +} + +// ---------------------------------------------------------------------------- + +// align all the memory blocks on a cache-line boundary +const int SimpleBestFitAllocator::kMemoryAlign = 32; + +SimpleBestFitAllocator::SimpleBestFitAllocator(size_t size) +{ + size_t pagesize = getpagesize(); + mHeapSize = ((size + pagesize-1) & ~(pagesize-1)); + + chunk_t* node = new chunk_t(0, mHeapSize / kMemoryAlign); + mList.insertHead(node); +} + +SimpleBestFitAllocator::~SimpleBestFitAllocator() +{ + while(!mList.isEmpty()) { + delete mList.remove(mList.head()); + } +} + +size_t SimpleBestFitAllocator::size() const +{ + return mHeapSize; +} + +size_t SimpleBestFitAllocator::allocate(size_t size, uint32_t flags) +{ + Mutex::Autolock _l(mLock); + ssize_t offset = alloc(size, flags); + return offset; +} + +status_t SimpleBestFitAllocator::deallocate(size_t offset) +{ + Mutex::Autolock _l(mLock); + chunk_t const * const freed = dealloc(offset); + if (freed) { + return NO_ERROR; + } + return NAME_NOT_FOUND; +} + +ssize_t SimpleBestFitAllocator::alloc(size_t size, uint32_t flags) +{ + if (size == 0) { + return 0; + } + size = (size + kMemoryAlign-1) / kMemoryAlign; + chunk_t* free_chunk = 0; + chunk_t* cur = mList.head(); + + size_t pagesize = getpagesize(); + while (cur) { + int extra = 0; + if (flags & PAGE_ALIGNED) + extra = ( -cur->start & ((pagesize/kMemoryAlign)-1) ) ; + + // best fit + if (cur->free && (cur->size >= (size+extra))) { + if ((!free_chunk) || (cur->size < free_chunk->size)) { + free_chunk = cur; + } + if (cur->size == size) { + break; + } + } + cur = cur->next; + } + + if (free_chunk) { + const size_t free_size = free_chunk->size; + free_chunk->free = 0; + free_chunk->size = size; + if (free_size > size) { + int extra = 0; + if (flags & PAGE_ALIGNED) + extra = ( -free_chunk->start & ((pagesize/kMemoryAlign)-1) ) ; + if (extra) { + chunk_t* split = new chunk_t(free_chunk->start, extra); + free_chunk->start += extra; + mList.insertBefore(free_chunk, split); + } + + LOGE_IF((flags&PAGE_ALIGNED) && + ((free_chunk->start*kMemoryAlign)&(pagesize-1)), + "PAGE_ALIGNED requested, but page is not aligned!!!"); + + const ssize_t tail_free = free_size - (size+extra); + if (tail_free > 0) { + chunk_t* split = new chunk_t( + free_chunk->start + free_chunk->size, tail_free); + mList.insertAfter(free_chunk, split); + } + } + return (free_chunk->start)*kMemoryAlign; + } + return NO_MEMORY; +} + +SimpleBestFitAllocator::chunk_t* SimpleBestFitAllocator::dealloc(size_t start) +{ + start = start / kMemoryAlign; + chunk_t* cur = mList.head(); + while (cur) { + if (cur->start == start) { + LOG_FATAL_IF(cur->free, + "block at offset 0x%08lX of size 0x%08lX already freed", + cur->start*kMemoryAlign, cur->size*kMemoryAlign); + + // merge freed blocks together + chunk_t* freed = cur; + cur->free = 1; + do { + chunk_t* const p = cur->prev; + chunk_t* const n = cur->next; + if (p && (p->free || !cur->size)) { + freed = p; + p->size += cur->size; + mList.remove(cur); + delete cur; + } + cur = n; + } while (cur && cur->free); + + #ifndef NDEBUG + if (!freed->free) { + dump_l("dealloc (!freed->free)"); + } + #endif + LOG_FATAL_IF(!freed->free, + "freed block at offset 0x%08lX of size 0x%08lX is not free!", + freed->start * kMemoryAlign, freed->size * kMemoryAlign); + + return freed; + } + cur = cur->next; + } + return 0; +} + +void SimpleBestFitAllocator::dump(const char* what, uint32_t flags) const +{ + Mutex::Autolock _l(mLock); + dump_l(what, flags); +} + +void SimpleBestFitAllocator::dump_l(const char* what, uint32_t flags) const +{ + String8 result; + dump_l(result, what, flags); + LOGD("%s", result.string()); +} + +void SimpleBestFitAllocator::dump(String8& result, + const char* what, uint32_t flags) const +{ + Mutex::Autolock _l(mLock); + dump_l(result, what, flags); +} + +void SimpleBestFitAllocator::dump_l(String8& result, + const char* what, uint32_t flags) const +{ + size_t size = 0; + int32_t i = 0; + chunk_t const* cur = mList.head(); + + const size_t SIZE = 256; + char buffer[SIZE]; + snprintf(buffer, SIZE, " %s (%p, size=%u)\n", + what, this, (unsigned int)mHeapSize); + + result.append(buffer); + + while (cur) { + const char* errs[] = {"", "| link bogus NP", + "| link bogus PN", "| link bogus NP+PN" }; + int np = ((cur->next) && cur->next->prev != cur) ? 1 : 0; + int pn = ((cur->prev) && cur->prev->next != cur) ? 2 : 0; + + snprintf(buffer, SIZE, " %3u: %08x | 0x%08X | 0x%08X | %s %s\n", + i, int(cur), int(cur->start*kMemoryAlign), + int(cur->size*kMemoryAlign), + int(cur->free) ? "F" : "A", + errs[np|pn]); + + result.append(buffer); + + if (!cur->free) + size += cur->size*kMemoryAlign; + + i++; + cur = cur->next; + } + snprintf(buffer, SIZE, " size allocated: %u (%u KB)\n", int(size), int(size/1024)); + result.append(buffer); +} + +// ---------------------------------------------------------------------------- + +SharedHeap::SharedHeap() + : HeapInterface(), MemoryHeapBase() +{ +} + +SharedHeap::SharedHeap(size_t size, uint32_t flags, char const * name) + : MemoryHeapBase(size, flags, name) +{ +} + +SharedHeap::~SharedHeap() +{ +} + +sp<IMemory> SharedHeap::mapMemory(size_t offset, size_t size) +{ + return new SimpleMemory(this, offset, size); +} + + +SimpleMemory::SimpleMemory(const sp<IMemoryHeap>& heap, + ssize_t offset, size_t size) + : MemoryBase(heap, offset, size) +{ +#ifndef NDEBUG + void* const start_ptr = (void*)(intptr_t(heap->base()) + offset); + memset(start_ptr, 0xda, size); +#endif +} + +SimpleMemory::~SimpleMemory() +{ + size_t freedOffset = getOffset(); + size_t freedSize = getSize(); + + // keep the size to unmap in excess + size_t pagesize = getpagesize(); + size_t start = freedOffset; + size_t end = start + freedSize; + start &= ~(pagesize-1); + end = (end + pagesize-1) & ~(pagesize-1); + + // give back to the kernel the pages we don't need + size_t free_start = freedOffset; + size_t free_end = free_start + freedSize; + if (start < free_start) + start = free_start; + if (end > free_end) + end = free_end; + start = (start + pagesize-1) & ~(pagesize-1); + end &= ~(pagesize-1); + + if (start < end) { + void* const start_ptr = (void*)(intptr_t(getHeap()->base()) + start); + size_t size = end-start; + +#ifndef NDEBUG + memset(start_ptr, 0xdf, size); +#endif + + // MADV_REMOVE is not defined on Dapper based Goobuntu +#ifdef MADV_REMOVE + if (size) { + int err = madvise(start_ptr, size, MADV_REMOVE); + LOGW_IF(err, "madvise(%p, %u, MADV_REMOVE) returned %s", + start_ptr, size, err<0 ? strerror(errno) : "Ok"); + } +#endif + } +} + +}; // namespace android diff --git a/libs/binder/MemoryHeapPmem.cpp b/libs/binder/MemoryHeapPmem.cpp index 16e92f9..5cc60f8 100644 --- a/libs/binder/MemoryHeapPmem.cpp +++ b/libs/binder/MemoryHeapPmem.cpp @@ -127,7 +127,11 @@ void SubRegionMemory::revoke() MemoryHeapPmem::MemoryHeapPmem(const sp<MemoryHeapBase>& pmemHeap, uint32_t flags) +#ifdef USE_ECLAIR_MEMORYDEALER + : HeapInterface(), MemoryHeapBase() +#else : MemoryHeapBase() +#endif { char const * const device = pmemHeap->getDevice(); #if HAVE_ANDROID_OS diff --git a/libs/camera/Android.mk b/libs/camera/Android.mk index 03ff229..3691bde 100644 --- a/libs/camera/Android.mk +++ b/libs/camera/Android.mk @@ -8,6 +8,10 @@ LOCAL_SRC_FILES:= \ ICameraClient.cpp \ ICameraService.cpp +LOCAL_MODULE:= libcamera_client + +ifneq ($(BOARD_USES_ECLAIR_LIBCAMERA),true) + LOCAL_SHARED_LIBRARIES := \ libcutils \ libutils \ @@ -16,10 +20,14 @@ LOCAL_SHARED_LIBRARIES := \ libsurfaceflinger_client \ libui -LOCAL_MODULE:= libcamera_client - ifeq ($(TARGET_SIMULATOR),true) LOCAL_LDLIBS += -lpthread endif include $(BUILD_SHARED_LIBRARY) + +else + +include $(BUILD_STATIC_LIBRARY) + +endif diff --git a/libs/camera/CameraParameters.cpp b/libs/camera/CameraParameters.cpp index 65fd7ac..12b08f0 100644 --- a/libs/camera/CameraParameters.cpp +++ b/libs/camera/CameraParameters.cpp @@ -130,6 +130,8 @@ const char CameraParameters::PIXEL_FORMAT_YUV420SP[] = "yuv420sp"; const char CameraParameters::PIXEL_FORMAT_YUV422I[] = "yuv422i-yuyv"; const char CameraParameters::PIXEL_FORMAT_RGB565[] = "rgb565"; const char CameraParameters::PIXEL_FORMAT_JPEG[] = "jpeg"; +const char CameraParameters::PIXEL_FORMAT_RAW[] = "raw"; + // Values for focus mode settings. const char CameraParameters::FOCUS_MODE_AUTO[] = "auto"; @@ -137,6 +139,51 @@ const char CameraParameters::FOCUS_MODE_INFINITY[] = "infinity"; const char CameraParameters::FOCUS_MODE_MACRO[] = "macro"; const char CameraParameters::FOCUS_MODE_FIXED[] = "fixed"; const char CameraParameters::FOCUS_MODE_EDOF[] = "edof"; +const char CameraParameters::FOCUS_MODE_NORMAL[] = "normal"; + + +const char CameraParameters::KEY_SUPPORTED_THUMBNAIL_SIZES[] = "jpeg-thumbnail-size-values"; +const char CameraParameters::KEY_GPS_LATITUDE_REF[] = "gps-latitude-ref"; +const char CameraParameters::KEY_GPS_LONGITUDE_REF[] = "gps-longitude-ref"; +const char CameraParameters::KEY_GPS_ALTITUDE_REF[] = "gps-altitude-ref"; +const char CameraParameters::KEY_GPS_STATUS[] = "gps-status"; + +const char CameraParameters::KEY_EXIF_DATETIME[] = "exif-datetime"; +const char CameraParameters::KEY_AUTO_EXPOSURE[] = "auto-exposure"; +const char CameraParameters::KEY_SUPPORTED_AUTO_EXPOSURE[] = "auto-exposure-values"; + +const char CameraParameters::KEY_ISO_MODE[] = "iso"; +const char CameraParameters::KEY_SUPPORTED_ISO_MODES[] = "iso-values"; +const char CameraParameters::KEY_LENSSHADE[] = "lensshade"; +const char CameraParameters::KEY_SUPPORTED_LENSSHADE_MODES[] = "lensshade-values"; +const char CameraParameters::KEY_SHARPNESS[] = "sharpness"; +const char CameraParameters::KEY_MAX_SHARPNESS[] = "max-sharpness"; +const char CameraParameters::KEY_CONTRAST[] = "contrast"; +const char CameraParameters::KEY_MAX_CONTRAST[] = "max-contrast"; +const char CameraParameters::KEY_SATURATION[] = "saturation"; +const char CameraParameters::KEY_MAX_SATURATION[] = "max-saturation"; + + +// Values for auto exposure settings. +const char CameraParameters::AUTO_EXPOSURE_FRAME_AVG[] = "frame-average"; +const char CameraParameters::AUTO_EXPOSURE_CENTER_WEIGHTED[] = "center-weighted"; +const char CameraParameters::AUTO_EXPOSURE_SPOT_METERING[] = "spot-metering"; + + // Values for ISO Settings +const char CameraParameters::ISO_AUTO[] = "auto"; +const char CameraParameters::ISO_HJR[] = "ISO_HJR"; +const char CameraParameters::ISO_100[] = "ISO100"; +const char CameraParameters::ISO_200[] = "ISO200"; +const char CameraParameters::ISO_400[] = "ISO400"; +const char CameraParameters::ISO_800[] = "ISO800"; +const char CameraParameters::ISO_1600[] = "ISO1600"; + + //Values for Lens Shading +const char CameraParameters::LENSSHADE_ENABLE[] = "enable"; +const char CameraParameters::LENSSHADE_DISABLE[] = "disable"; + +static const char* portrait = "portrait"; +static const char* landscape = "landscape"; CameraParameters::CameraParameters() : mMap() @@ -315,6 +362,23 @@ void CameraParameters::setPreviewFormat(const char *format) set(KEY_PREVIEW_FORMAT, format); } +int CameraParameters::getOrientation() const +{ + const char* orientation = get("orientation"); + if (orientation && !strcmp(orientation, portrait)) + return CAMERA_ORIENTATION_PORTRAIT; + return CAMERA_ORIENTATION_LANDSCAPE; +} + +void CameraParameters::setOrientation(int orientation) +{ + if (orientation == CAMERA_ORIENTATION_PORTRAIT) { + set("orientation", portrait); + } else { + set("orientation", landscape); + } +} + const char *CameraParameters::getPreviewFormat() const { return get(KEY_PREVIEW_FORMAT); diff --git a/libs/surfaceflinger/Android.mk b/libs/surfaceflinger/Android.mk index 86eb78d..bf74089 100644 --- a/libs/surfaceflinger/Android.mk +++ b/libs/surfaceflinger/Android.mk @@ -38,8 +38,12 @@ LOCAL_SHARED_LIBRARIES := \ libEGL \ libGLESv1_CM \ libbinder \ - libui \ - libsurfaceflinger_client + libui + +ifneq ($(BOARD_USES_ECLAIR_LIBCAMERA),true) + LOCAL_SHARED_LIBRARIES += \ + libsurfaceflinger_client +endif LOCAL_C_INCLUDES := \ $(call include-path-for, corecg graphics) diff --git a/libs/surfaceflinger/Layer.cpp b/libs/surfaceflinger/Layer.cpp index ce7e9aa..2c7b4af 100644 --- a/libs/surfaceflinger/Layer.cpp +++ b/libs/surfaceflinger/Layer.cpp @@ -133,10 +133,14 @@ status_t Layer::setBuffers( uint32_t w, uint32_t h, mNeedsBlending = (info.h_alpha - info.l_alpha) > 0; mNoEGLImageForSwBuffers = !(hwFlags & DisplayHardware::CACHED_BUFFERS); - // we use the red index - int displayRedSize = displayInfo.getSize(PixelFormatInfo::INDEX_RED); - int layerRedsize = info.getSize(PixelFormatInfo::INDEX_RED); - mNeedsDithering = layerRedsize > displayRedSize; + if (mFlinger->getUseDithering()) { + // we use the red index + int displayRedSize = displayInfo.getSize(PixelFormatInfo::INDEX_RED); + int layerRedsize = info.getSize(PixelFormatInfo::INDEX_RED); + mNeedsDithering = layerRedsize > displayRedSize; + } else { + mNeedsDithering = false; + } for (size_t i=0 ; i<NUM_BUFFERS ; i++) { mBuffers[i] = new GraphicBuffer(); diff --git a/libs/surfaceflinger/LayerBase.cpp b/libs/surfaceflinger/LayerBase.cpp index a8b735e..046c7c3 100644 --- a/libs/surfaceflinger/LayerBase.cpp +++ b/libs/surfaceflinger/LayerBase.cpp @@ -33,6 +33,15 @@ #include "SurfaceFlinger.h" #include "DisplayHardware/DisplayHardware.h" +#define RENDER_EFFECT_NIGHT 1 +#define RENDER_EFFECT_TERMINAL 2 +#define RENDER_EFFECT_BLUE 3 +#define RENDER_EFFECT_AMBER 4 +#define RENDER_EFFECT_SALMON 5 +#define RENDER_EFFECT_FUSCIA 6 +#define RENDER_EFFECT_N1_CALIBRATED_N 7 +#define RENDER_EFFECT_N1_CALIBRATED_R 8 +#define RENDER_EFFECT_N1_CALIBRATED_C 9 namespace android { @@ -401,7 +410,14 @@ void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const glEnable(GL_TEXTURE_2D); - if (UNLIKELY(s.alpha < 0xFF)) { + int renderEffect = mFlinger->getRenderEffect(); + int renderColorR = mFlinger->getRenderColorR(); + int renderColorG = mFlinger->getRenderColorG(); + int renderColorB = mFlinger->getRenderColorB(); + + bool noEffect = renderEffect == 0; + + if (UNLIKELY(s.alpha < 0xFF) && noEffect) { // We have an alpha-modulation. We need to modulate all // texture components by alpha because we're always using // premultiplied alpha. @@ -423,7 +439,7 @@ void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const glEnable(GL_BLEND); glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA); glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, env); - } else { + } else if (noEffect) { glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glColor4x(0x10000, 0x10000, 0x10000, 0x10000); if (needsBlending()) { @@ -433,6 +449,44 @@ void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const } else { glDisable(GL_BLEND); } + } else { + // Apply a render effect, which is simple color masks for now. + GLenum env, src; + env = GL_MODULATE; + src = mPremultipliedAlpha ? GL_ONE : GL_SRC_ALPHA; + const GGLfixed alpha = (s.alpha << 16)/255; + switch (renderEffect) { + case RENDER_EFFECT_NIGHT: + glColor4x(alpha, 0, 0, alpha); + break; + case RENDER_EFFECT_TERMINAL: + glColor4x(0, alpha, 0, alpha); + break; + case RENDER_EFFECT_BLUE: + glColor4x(0, 0, alpha, alpha); + break; + case RENDER_EFFECT_AMBER: + glColor4x(alpha, alpha*0.75, 0, alpha); + break; + case RENDER_EFFECT_SALMON: + glColor4x(alpha, alpha*0.5, alpha*0.5, alpha); + break; + case RENDER_EFFECT_FUSCIA: + glColor4x(alpha, 0, alpha*0.5, alpha); + break; + case RENDER_EFFECT_N1_CALIBRATED_N: + glColor4x(alpha*renderColorR/1000, alpha*renderColorG/1000, alpha*renderColorB/1000, alpha); + break; + case RENDER_EFFECT_N1_CALIBRATED_R: + glColor4x(alpha*(renderColorR-50)/1000, alpha*renderColorG/1000, alpha*(renderColorB-30)/1000, alpha); + break; + case RENDER_EFFECT_N1_CALIBRATED_C: + glColor4x(alpha*renderColorR/1000, alpha*renderColorG/1000, alpha*(renderColorB+30)/1000, alpha); + break; + } + glEnable(GL_BLEND); + glBlendFunc(src, GL_ONE_MINUS_SRC_ALPHA); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, env); } Region::const_iterator it = clip.begin(); @@ -826,4 +880,4 @@ sp<OverlayRef> LayerBaseClient::Surface::createOverlay( // --------------------------------------------------------------------------- -}; // namespace android +}; // namespace android
\ No newline at end of file diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp index 0722fda..9e327ec 100644 --- a/libs/surfaceflinger/SurfaceFlinger.cpp +++ b/libs/surfaceflinger/SurfaceFlinger.cpp @@ -184,13 +184,18 @@ SurfaceFlinger::SurfaceFlinger() mFreezeDisplayTime(0), mDebugRegion(0), mDebugBackground(0), + mRenderEffect(0), + mRenderColorR(0), + mRenderColorG(0), + mRenderColorB(0), mDebugInSwapBuffers(0), mLastSwapBufferTime(0), mDebugInTransaction(0), mLastTransactionTime(0), mBootFinished(false), mConsoleSignals(0), - mSecureFrameBuffer(0) + mSecureFrameBuffer(0), + mUseDithering(true) { init(); } @@ -205,9 +210,23 @@ void SurfaceFlinger::init() mDebugRegion = atoi(value); property_get("debug.sf.showbackground", value, "0"); mDebugBackground = atoi(value); + property_get("debug.sf.render_effect", value, "0"); + mRenderEffect = atoi(value); + + // default calibration color set (disabled by default) + property_get("debug.sf.render_color_red", value, "975"); + mRenderColorR = atoi(value); + property_get("debug.sf.render_color_green", value, "937"); + mRenderColorG = atoi(value); + property_get("debug.sf.render_color_blue", value, "824"); + mRenderColorB = atoi(value); + + property_get("persist.sys.use_dithering", value, "1"); + mUseDithering = atoi(value) == 1; LOGI_IF(mDebugRegion, "showupdates enabled"); LOGI_IF(mDebugBackground, "showbackground enabled"); + LOGI_IF(mUseDithering, "dithering enabled"); } SurfaceFlinger::~SurfaceFlinger() @@ -1692,12 +1711,30 @@ status_t SurfaceFlinger::onTransact( reply->writeInt32(0); reply->writeInt32(mDebugRegion); reply->writeInt32(mDebugBackground); + reply->writeInt32(mRenderEffect); return NO_ERROR; case 1013: { Mutex::Autolock _l(mStateLock); const DisplayHardware& hw(graphicPlane(0).displayHardware()); reply->writeInt32(hw.getPageFlipCount()); } + case 1014: { // RENDER_EFFECT + // TODO: filter to only allow valid effects + mRenderEffect = data.readInt32(); + return NO_ERROR; + } + case 1015: { // RENDER_COLOR_RED + mRenderColorR = data.readInt32(); + return NO_ERROR; + } + case 1016: { // RENDER_COLOR_GREEN + mRenderColorG = data.readInt32(); + return NO_ERROR; + } + case 1017: { // RENDER_COLOR_BLUE + mRenderColorB = data.readInt32(); + return NO_ERROR; + } return NO_ERROR; } } diff --git a/libs/surfaceflinger/SurfaceFlinger.h b/libs/surfaceflinger/SurfaceFlinger.h index d75dc15..1bfdb1b 100644 --- a/libs/surfaceflinger/SurfaceFlinger.h +++ b/libs/surfaceflinger/SurfaceFlinger.h @@ -174,6 +174,12 @@ public: overlay_control_device_t* getOverlayEngine() const; + inline int getRenderEffect() const { return mRenderEffect; } + inline int getRenderColorR() const { return mRenderColorR; } + inline int getRenderColorG() const { return mRenderColorG; } + inline int getRenderColorB() const { return mRenderColorB; } + inline int getUseDithering() const { return mUseDithering; } + status_t removeLayer(const sp<LayerBase>& layer); status_t addLayer(const sp<LayerBase>& layer); @@ -354,6 +360,10 @@ private: // don't use a lock for these, we don't care int mDebugRegion; int mDebugBackground; + int mRenderEffect; + int mRenderColorR; + int mRenderColorG; + int mRenderColorB; volatile nsecs_t mDebugInSwapBuffers; nsecs_t mLastSwapBufferTime; volatile nsecs_t mDebugInTransaction; @@ -372,6 +382,7 @@ private: // only written in the main thread, only read in other threads volatile int32_t mSecureFrameBuffer; + bool mUseDithering; }; // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger_client/Android.mk b/libs/surfaceflinger_client/Android.mk index fe85b34..5368280 100644 --- a/libs/surfaceflinger_client/Android.mk +++ b/libs/surfaceflinger_client/Android.mk @@ -10,6 +10,10 @@ LOCAL_SRC_FILES:= \ Surface.cpp \ SurfaceComposerClient.cpp +LOCAL_MODULE:= libsurfaceflinger_client + +ifneq ($(BOARD_USES_ECLAIR_LIBCAMERA),true) + LOCAL_SHARED_LIBRARIES := \ libcutils \ libutils \ @@ -17,10 +21,15 @@ LOCAL_SHARED_LIBRARIES := \ libhardware \ libui -LOCAL_MODULE:= libsurfaceflinger_client ifeq ($(TARGET_SIMULATOR),true) LOCAL_LDLIBS += -lpthread endif include $(BUILD_SHARED_LIBRARY) + +else + +include $(BUILD_STATIC_LIBRARY) + +endif diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk index f7acd97..c5bc8bd 100644 --- a/libs/ui/Android.mk +++ b/libs/ui/Android.mk @@ -26,6 +26,27 @@ LOCAL_SHARED_LIBRARIES := \ libhardware \ libhardware_legacy + +ifeq ($(BOARD_USES_ECLAIR_LIBCAMERA),true) + +LOCAL_SRC_FILES+= \ + ../camera/Camera.cpp \ + ../camera/CameraParameters.cpp \ + ../camera/ICamera.cpp \ + ../camera/ICameraClient.cpp \ + ../camera/ICameraService.cpp + +LOCAL_SRC_FILES+= \ + ../surfaceflinger_client/ISurfaceComposer.cpp \ + ../surfaceflinger_client/ISurface.cpp \ + ../surfaceflinger_client/ISurfaceFlingerClient.cpp \ + ../surfaceflinger_client/LayerState.cpp \ + ../surfaceflinger_client/SharedBufferStack.cpp \ + ../surfaceflinger_client/Surface.cpp \ + ../surfaceflinger_client/SurfaceComposerClient.cpp + +endif + LOCAL_MODULE:= libui ifeq ($(TARGET_SIMULATOR),true) diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp index d45eaf0..0a98cb2 100644 --- a/libs/ui/EventHub.cpp +++ b/libs/ui/EventHub.cpp @@ -642,7 +642,10 @@ int EventHub::open_device(const char *deviceName) if (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bitmask)), rel_bitmask) >= 0) { if (test_bit(REL_X, rel_bitmask) && test_bit(REL_Y, rel_bitmask)) { - device->classes |= CLASS_TRACKBALL; + if (test_bit(BTN_LEFT, key_bitmask) && test_bit(BTN_RIGHT, key_bitmask)) + device->classes |= CLASS_MOUSE; + else + device->classes |= CLASS_TRACKBALL; } } } diff --git a/libs/ui/Overlay.cpp b/libs/ui/Overlay.cpp index 3aa8950..96f8006 100644 --- a/libs/ui/Overlay.cpp +++ b/libs/ui/Overlay.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2007 The Android Open Source Project + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -83,6 +84,12 @@ status_t Overlay::getCrop(uint32_t* x, uint32_t* y, uint32_t* w, uint32_t* h) return mOverlayData->getCrop(mOverlayData, x, y, w, h); } +status_t Overlay::setFd(int fd) +{ + if (mStatus != NO_ERROR) return mStatus; + return mOverlayData->setFd(mOverlayData, fd); +} + int32_t Overlay::getBufferCount() const { if (mStatus != NO_ERROR) return mStatus; diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 7e0f881..1a40deb 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -4393,7 +4393,6 @@ void ResTable::print(bool inclValues) const } printf("\n"); - if (inclValues) { if (valuePtr != NULL) { printf(" "); print_value(pkg, value); @@ -4413,7 +4412,6 @@ void ResTable::print(bool inclValues) const + size + sizeof(*mapPtr)-sizeof(mapPtr->value)); } } - } } } } diff --git a/location/java/android/location/Address.java b/location/java/android/location/Address.java index ac275c6..b152f48 100644 --- a/location/java/android/location/Address.java +++ b/location/java/android/location/Address.java @@ -500,7 +500,10 @@ public class Address implements Parcelable { a.mAdminArea = in.readString(); a.mSubAdminArea = in.readString(); a.mLocality = in.readString(); + a.mSubLocality = in.readString(); a.mThoroughfare = in.readString(); + a.mSubThoroughfare = in.readString(); + a.mPremises = in.readString(); a.mPostalCode = in.readString(); a.mCountryCode = in.readString(); a.mCountryName = in.readString(); @@ -544,7 +547,10 @@ public class Address implements Parcelable { parcel.writeString(mAdminArea); parcel.writeString(mSubAdminArea); parcel.writeString(mLocality); + parcel.writeString(mSubLocality); parcel.writeString(mThoroughfare); + parcel.writeString(mSubThoroughfare); + parcel.writeString(mPremises); parcel.writeString(mPostalCode); parcel.writeString(mCountryCode); parcel.writeString(mCountryName); diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index bbbba74..1cd8fd0 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -1215,6 +1215,13 @@ public class AudioManager { return Settings.System.getInt(mContext.getContentResolver(), Settings.System.SOUND_EFFECTS_ENABLED, 0) != 0; } + /** + * See if haptic feedback is enabled for screen touches of objects (called by ViewRoot) + * @hide + */ + public boolean queryHapticsAllEnabled() { + return Settings.System.getInt(mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ALL_ENABLED, 0) != 0; + } /** * Load Sound effects. diff --git a/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java index 64d6460..5915494 100644 --- a/media/java/android/media/CamcorderProfile.java +++ b/media/java/android/media/CamcorderProfile.java @@ -48,12 +48,16 @@ public class CamcorderProfile * resolution and higher audio sampling rate, etc, than those with low quality * level. * + * An optional profile, "front" is defined as a high quality profile for devices + * with a front-facing camera. + * * Do not change these values/ordinals without updating their counterpart * in include/media/MediaProfiles.h! */ public static final int QUALITY_LOW = 0; public static final int QUALITY_HIGH = 1; - + public static final int QUALITY_FRONT = 2; + /** * Default recording duration in seconds before the session is terminated. * This is useful for applications like MMS has limited file size requirement. @@ -123,7 +127,7 @@ public class CamcorderProfile * @param quality the target quality level for the camcorder profile */ public static CamcorderProfile get(int quality) { - if (quality < QUALITY_LOW || quality > QUALITY_HIGH) { + if (quality < QUALITY_LOW || quality > QUALITY_FRONT) { String errMessage = "Unsupported quality level: " + quality; throw new IllegalArgumentException(errMessage); } diff --git a/media/java/android/media/EncoderCapabilities.java b/media/java/android/media/EncoderCapabilities.java index 71cb1b3..1f285bb 100644 --- a/media/java/android/media/EncoderCapabilities.java +++ b/media/java/android/media/EncoderCapabilities.java @@ -24,7 +24,6 @@ import android.util.Log; * The EncoderCapabilities class is used to retrieve the * capabilities for different video and audio * encoders supported on a specific Android platform. - * {@hide} */ public class EncoderCapabilities { diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java index 9d1d420..aece959 100644 --- a/media/java/android/media/MediaFile.java +++ b/media/java/android/media/MediaFile.java @@ -46,8 +46,9 @@ public class MediaFile { public static final int FILE_TYPE_WMA = 6; public static final int FILE_TYPE_OGG = 7; public static final int FILE_TYPE_AAC = 8; + public static final int FILE_TYPE_FLAC = 9; private static final int FIRST_AUDIO_FILE_TYPE = FILE_TYPE_MP3; - private static final int LAST_AUDIO_FILE_TYPE = FILE_TYPE_AAC; + private static final int LAST_AUDIO_FILE_TYPE = FILE_TYPE_FLAC; // MIDI file types public static final int FILE_TYPE_MID = 11; @@ -134,6 +135,7 @@ public class MediaFile { addFileType("OGG", FILE_TYPE_OGG, "application/ogg"); addFileType("OGA", FILE_TYPE_OGG, "application/ogg"); addFileType("AAC", FILE_TYPE_AAC, "audio/aac"); + addFileType("FLAC", FILE_TYPE_FLAC, "audio/flac"); addFileType("MID", FILE_TYPE_MID, "audio/midi"); addFileType("MIDI", FILE_TYPE_MID, "audio/midi"); diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index 47a8cfc..646dfbb 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -204,7 +204,7 @@ public class MediaRecorder public static final int AMR_NB = 1; /** @hide AMR (Wideband) audio codec */ public static final int AMR_WB = 2; - /** @hide AAC audio codec */ + /** AAC audio codec */ public static final int AAC = 3; /** @hide enhanced AAC audio codec */ public static final int AAC_PLUS = 4; @@ -259,6 +259,9 @@ public class MediaRecorder public native void setVideoSource(int video_source) throws IllegalStateException; + public native void setCameraParameters(String params) + throws IllegalStateException; + /** * Uses the settings from a CamcorderProfile object for recording. This method should * be called after the video AND audio sources are set, and before setOutputFile(). diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java index 0ce3526..0cecc3f 100644 --- a/media/java/android/media/Ringtone.java +++ b/media/java/android/media/Ringtone.java @@ -166,6 +166,9 @@ public class Ringtone { } private void openMediaPlayer() throws IOException { + if (mAudio != null) { + return; + } mAudio = new MediaPlayer(); if (mUri != null) { mAudio.setDataSource(mContext, mUri); diff --git a/media/jni/Android.mk b/media/jni/Android.mk index a6a25cd..29bb027 100644 --- a/media/jni/Android.mk +++ b/media/jni/Android.mk @@ -21,9 +21,13 @@ LOCAL_SHARED_LIBRARIES := \ libmedia \ libskia \ libui \ - libcutils \ - libsurfaceflinger_client \ - libcamera_client + libcutils + +ifneq ($(BOARD_USES_ECLAIR_LIBCAMERA),true) + LOCAL_SHARED_LIBRARIES += \ + libsurfaceflinger_client \ + libcamera_client +endif ifneq ($(BUILD_WITHOUT_PV),true) diff --git a/media/jni/android_media_MediaProfiles.cpp b/media/jni/android_media_MediaProfiles.cpp index 7d7533a..ff3379e 100644 --- a/media/jni/android_media_MediaProfiles.cpp +++ b/media/jni/android_media_MediaProfiles.cpp @@ -165,7 +165,7 @@ static jobject android_media_MediaProfiles_native_get_camcorder_profile(JNIEnv *env, jobject thiz, jint quality) { LOGV("native_get_camcorder_profile: %d", quality); - if (quality != CAMCORDER_QUALITY_HIGH && quality != CAMCORDER_QUALITY_LOW) { + if (quality != CAMCORDER_QUALITY_HIGH && quality != CAMCORDER_QUALITY_LOW && quality != CAMCORDER_QUALITY_FRONT) { jniThrowException(env, "java/lang/RuntimeException", "Unknown camcorder profile quality"); return NULL; } diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp index 00af3a2..35e1d64 100644 --- a/media/jni/android_media_MediaRecorder.cpp +++ b/media/jni/android_media_MediaRecorder.cpp @@ -246,6 +246,29 @@ android_media_MediaRecorder_setParameter(JNIEnv *env, jobject thiz, jstring para } static void +android_media_MediaRecorder_setCameraParameters(JNIEnv *env, jobject thiz, jstring params) +{ + LOGV("setCameraParameters()"); + if (params == NULL) + { + LOGE("Invalid or empty params string. This parameter will be ignored."); + return; + } + + sp<MediaRecorder> mr = getMediaRecorder(env, thiz); + + const char* params8 = env->GetStringUTFChars(params, NULL); + if (params8 == NULL) + { + LOGE("Failed to covert jstring to String8. This parameter will be ignored."); + return; + } + + process_media_recorder_call(env, mr->setCameraParameters(String8(params8)), "java/lang/RuntimeException", "setCameraParameters failed."); + env->ReleaseStringUTFChars(params,params8); +} + +static void android_media_MediaRecorder_setOutputFileFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length) { LOGV("setOutputFile"); @@ -456,6 +479,7 @@ static JNINativeMethod gMethods[] = { {"setVideoEncoder", "(I)V", (void *)android_media_MediaRecorder_setVideoEncoder}, {"setAudioEncoder", "(I)V", (void *)android_media_MediaRecorder_setAudioEncoder}, {"setParameter", "(Ljava/lang/String;)V", (void *)android_media_MediaRecorder_setParameter}, + {"setCameraParameters", "(Ljava/lang/String;)V", (void *)android_media_MediaRecorder_setCameraParameters}, {"_setOutputFile", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaRecorder_setOutputFileFD}, {"setVideoSize", "(II)V", (void *)android_media_MediaRecorder_setVideoSize}, {"setVideoFrameRate", "(I)V", (void *)android_media_MediaRecorder_setVideoFrameRate}, diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk index 3adabcc..02b8324 100644 --- a/media/libmedia/Android.mk +++ b/media/libmedia/Android.mk @@ -29,7 +29,13 @@ LOCAL_SRC_FILES:= \ MediaProfiles.cpp LOCAL_SHARED_LIBRARIES := \ - libui libcutils libutils libbinder libsonivox libicuuc libexpat libsurfaceflinger_client libcamera_client + libui libcutils libutils libbinder libsonivox libicuuc libexpat + +ifneq ($(BOARD_USES_ECLAIR_LIBCAMERA),true) + LOCAL_SHARED_LIBRARIES += \ + libsurfaceflinger_client \ + libcamera_client +endif LOCAL_MODULE:= libmedia diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index ad037d6..4e38773 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -37,6 +37,10 @@ #include <utils/Timers.h> #include <cutils/atomic.h> +#ifdef USE_ECLAIR_MEMORYDEALER +#include <binder/MemoryDealer.h> +#endif + #define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) #define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index cd7bcd5..ba99d81 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -37,6 +37,10 @@ #include <utils/Timers.h> #include <cutils/atomic.h> +#ifdef USE_ECLAIR_MEMORYDEALER +#include <binder/MemoryDealer.h> +#endif + #define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) #define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp index 1de9f9b..11ca90c 100644 --- a/media/libmedia/IMediaRecorder.cpp +++ b/media/libmedia/IMediaRecorder.cpp @@ -45,6 +45,7 @@ enum { SET_VIDEO_SIZE, SET_VIDEO_FRAMERATE, SET_PARAMETERS, + SET_CAMERA_PARAMETERS, SET_PREVIEW_SURFACE, SET_CAMERA, SET_LISTENER @@ -189,6 +190,16 @@ public: return reply.readInt32(); } + status_t setCameraParameters(const String8& params) + { + LOGV("setCameraParameter(%s)", params.string()); + Parcel data, reply; + data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor()); + data.writeString8(params); + remote()->transact(SET_CAMERA_PARAMETERS, data, &reply); + return reply.readInt32(); + } + status_t setListener(const sp<IMediaPlayerClient>& listener) { LOGV("setListener(%p)", listener.get()); @@ -396,6 +407,12 @@ status_t BnMediaRecorder::onTransact( reply->writeInt32(setParameters(data.readString8())); return NO_ERROR; } break; + case SET_CAMERA_PARAMETERS: { + LOGV("SET_CAMERA_PARAMETER"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + reply->writeInt32(setCameraParameters(data.readString8())); + return NO_ERROR; + } break; case SET_LISTENER: { LOGV("SET_LISTENER"); CHECK_INTERFACE(IMediaRecorder, data, reply); diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp index 1263373..1b3be41 100644 --- a/media/libmedia/MediaProfiles.cpp +++ b/media/libmedia/MediaProfiles.cpp @@ -59,8 +59,9 @@ const MediaProfiles::NameToTagMap MediaProfiles::sAudioDecoderNameMap[] = { }; const MediaProfiles::NameToTagMap MediaProfiles::sCamcorderQualityNameMap[] = { - {"high", CAMCORDER_QUALITY_HIGH}, - {"low", CAMCORDER_QUALITY_LOW} + {"high", CAMCORDER_QUALITY_HIGH}, + {"low", CAMCORDER_QUALITY_LOW}, + {"front", CAMCORDER_QUALITY_FRONT} }; /*static*/ void diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp index 5adc116..bfa4b2b 100644 --- a/media/libmedia/mediarecorder.cpp +++ b/media/libmedia/mediarecorder.cpp @@ -385,6 +385,28 @@ status_t MediaRecorder::setParameters(const String8& params) { return ret; } +status_t MediaRecorder::setCameraParameters(const String8& params) { + LOGV("setCameraParameters(%s)", params.string()); + if(mMediaRecorder == NULL) { + LOGE("media recorder is not initialized yet"); + return INVALID_OPERATION; + } + + bool isInvalidState = (mCurrentState & + MEDIA_RECORDER_ERROR); + if (isInvalidState) { + LOGE("setCameraParameters is called in an invalid state: %d", mCurrentState); + return INVALID_OPERATION; + } + + status_t ret = mMediaRecorder->setCameraParameters(params); + if (OK != ret) { + LOGE("setCameraParameters(%s) failed: %d", params.string(), ret); + } + + return ret; +} + status_t MediaRecorder::prepare() { LOGV("prepare"); diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk index cf97b23..ecd1d2e 100644 --- a/media/libmediaplayerservice/Android.mk +++ b/media/libmediaplayerservice/Android.mk @@ -13,7 +13,8 @@ LOCAL_SRC_FILES:= \ TestPlayerStub.cpp \ VorbisPlayer.cpp \ VorbisMetadataRetriever.cpp \ - MidiMetadataRetriever.cpp \ + FLACPlayer.cpp \ + MidiMetadataRetriever.cpp \ MidiFile.cpp ifeq ($(BUILD_WITH_FULL_STAGEFRIGHT),true) @@ -40,12 +41,17 @@ LOCAL_SHARED_LIBRARIES := \ libandroid_runtime \ libstagefright \ libstagefright_omx \ - libstagefright_color_conversion \ - libsurfaceflinger_client + libstagefright_color_conversion + +ifneq ($(BOARD_USES_ECLAIR_LIBCAMERA),true) + LOCAL_SHARED_LIBRARIES += \ + libsurfaceflinger_client +endif ifneq ($(BUILD_WITHOUT_PV),true) LOCAL_SHARED_LIBRARIES += \ libopencore_player \ + libFLAC \ libopencore_author else LOCAL_CFLAGS += -DNO_OPENCORE @@ -56,6 +62,7 @@ LOCAL_SHARED_LIBRARIES += libdl endif LOCAL_C_INCLUDES := \ + external/flac/include \ $(JNI_H_INCLUDE) \ $(call include-path-for, graphics corecg) \ $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \ diff --git a/media/libmediaplayerservice/FLACPlayer.cpp b/media/libmediaplayerservice/FLACPlayer.cpp new file mode 100644 index 0000000..1f8e3b6 --- /dev/null +++ b/media/libmediaplayerservice/FLACPlayer.cpp @@ -0,0 +1,577 @@ +/* +** Copyright 2009, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "FLACPlayer" +#include "utils/Log.h" + +#include <stdio.h> +#include <assert.h> +#include <limits.h> +#include <unistd.h> +#include <fcntl.h> +#include <sched.h> +#include <sys/types.h> +#include <sys/stat.h> + + +#include "FLACPlayer.h" + +#ifdef HAVE_GETTID +static pid_t myTid() { return gettid(); } +#else +static pid_t myTid() { return getpid(); } +#endif + +// ---------------------------------------------------------------------------- + +namespace android { + +// ---------------------------------------------------------------------------- + +// TODO: Determine appropriate return codes +static status_t ERROR_NOT_OPEN = -1; +static status_t ERROR_OPEN_FAILED = -2; +static status_t ERROR_ALLOCATE_FAILED = -4; +static status_t ERROR_NOT_SUPPORTED = -8; +static status_t ERROR_NOT_READY = -16; +static status_t STATE_INIT = 0; +static status_t STATE_ERROR = 1; +static status_t STATE_OPEN = 2; + + +FLACPlayer::FLACPlayer() : + mTotalSamples(-1), mCurrentSample(0), mBytesPerSample(-1), + mChannels(-1), mSampleRate(-1), mAudioBuffer(NULL), + mAudioBufferSize(0), mAudioBufferFilled(0), + mState(STATE_ERROR), mStreamType(AudioSystem::MUSIC), + mLoop(false), mAndroidLoop(false), mExit(false), mPaused(false), + mRender(false), mRenderTid(-1) +{ + LOGV("constructor"); +} + +void FLACPlayer::onFirstRef() +{ + LOGV("onFirstRef"); + // create playback thread + Mutex::Autolock l(mMutex); + createThreadEtc(renderThread, this, "FLAC decoder", ANDROID_PRIORITY_AUDIO); + mCondition.wait(mMutex); + if (mRenderTid > 0) { + LOGV("render thread(%d) started", mRenderTid); + mState = STATE_INIT; + } +} + +status_t FLACPlayer::initCheck() +{ + if (mState != STATE_ERROR) return NO_ERROR; + return ERROR_NOT_READY; +} + +FLACPlayer::~FLACPlayer() { + LOGV("FLACPlayer destructor"); + release(); +} + +status_t FLACPlayer::setDataSource( + const char *uri, const KeyedVector<String8, String8> *headers) { + return setdatasource(uri, -1, 0, 0x7ffffffffffffffLL); // intentionally less than LONG_MAX +} + +status_t FLACPlayer::setDataSource(int fd, int64_t offset, int64_t length) +{ + return setdatasource(NULL, fd, offset, length); +} + +status_t FLACPlayer::setdatasource(const char *path, int fd, int64_t offset, int64_t length) +{ + LOGV("setDataSource url=%s, fd=%d", path, fd); + + // file still open? + Mutex::Autolock l(mMutex); + if (mState == STATE_OPEN) { + reset_nosync(); + } + + // open file and set paused state + if (path) { + mFile = fopen(path, "r"); + } else { + mFile = fdopen(dup(fd), "r"); + } + if (mFile == NULL) { + return ERROR_OPEN_FAILED; + } + + struct stat sb; + int ret; + if (path) { + ret = stat(path, &sb); + } else { + ret = fstat(fd, &sb); + } + if (ret != 0) { + mState = STATE_ERROR; + fclose(mFile); + return ERROR_OPEN_FAILED; + } + + fseek(mFile, offset, SEEK_SET); + + mDecoder = FLAC__stream_decoder_new(); + if (mDecoder == NULL) { + LOGE("failed to allocate decoder\n"); + mState = STATE_ERROR; + fclose(mFile); + return ERROR_OPEN_FAILED; + } + + FLAC__stream_decoder_set_md5_checking(mDecoder, false); + FLAC__stream_decoder_set_metadata_ignore_all(mDecoder); + FLAC__stream_decoder_set_metadata_respond(mDecoder, FLAC__METADATA_TYPE_STREAMINFO); + FLAC__stream_decoder_set_metadata_respond(mDecoder, FLAC__METADATA_TYPE_VORBIS_COMMENT); + + FLAC__StreamDecoderInitStatus init_status; + init_status = FLAC__stream_decoder_init_FILE(mDecoder, mFile, vp_write, vp_metadata, vp_error, this); + if (init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK) { + LOGE("FLAC__stream_decoder_init_FILE failed: [%d]\n", (int)init_status); + mState = STATE_ERROR; + fclose(mFile); + return ERROR_OPEN_FAILED; + } + + if (!FLAC__stream_decoder_process_until_end_of_metadata(mDecoder)) { + LOGE("FLAC__stream_decoder_process_until_end_of_metadata failed\n"); + mState = STATE_ERROR; + fclose(mFile); + return ERROR_OPEN_FAILED; + } + + mState = STATE_OPEN; + return NO_ERROR; +} + +status_t FLACPlayer::prepare() +{ + LOGV("prepare"); + if (mState != STATE_OPEN ) { + return ERROR_NOT_OPEN; + } + return NO_ERROR; +} + +status_t FLACPlayer::prepareAsync() { + LOGV("prepareAsync"); + // can't hold the lock here because of the callback + // it's safe because we don't change state + if (mState != STATE_OPEN) { + sendEvent(MEDIA_ERROR); + return NO_ERROR; + } + sendEvent(MEDIA_PREPARED); + return NO_ERROR; +} + +void FLACPlayer::vp_metadata(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) { + FLACPlayer *self = (FLACPlayer *)client_data; + + if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { + self->mTotalSamples = metadata->data.stream_info.total_samples; + self->mBytesPerSample = metadata->data.stream_info.bits_per_sample / 8; + self->mChannels = metadata->data.stream_info.channels; + self->mSampleRate = metadata->data.stream_info.sample_rate; + + if (self->mBytesPerSample != 2) { + LOGE("Can only support 16 bits per sample; input is %d\n", self->mBytesPerSample * 8); + self->mState = STATE_ERROR; + return; + } + + self->mLengthInMsec = self->mTotalSamples / self->mSampleRate * 1000 + + self->mTotalSamples % self->mSampleRate / ( self->mSampleRate / 1000 ); + } else if (metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { + for (unsigned int i = 0; i < metadata->data.vorbis_comment.num_comments; i++) { + char *ptr = (char *)metadata->data.vorbis_comment.comments[i].entry; + + // does the comment start with ANDROID_LOOP_TAG + if (strncmp(ptr, ANDROID_LOOP_TAG, strlen(ANDROID_LOOP_TAG)) == 0) { + // read the value of the tag + char *val = ptr + strlen(ANDROID_LOOP_TAG) + 1; + self->mAndroidLoop = (strncmp(val, "true", 4) == 0); + } + + LOGV_IF(self->mAndroidLoop, "looped sound"); + } + } +} + +void FLACPlayer::vp_error(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) { + LOGV("vp_error"); + FLACPlayer *self = (FLACPlayer *)client_data; + self->sendEvent(MEDIA_ERROR); + self->mState = STATE_ERROR; +} + +status_t FLACPlayer::start() +{ + LOGV("start\n"); + Mutex::Autolock l(mMutex); + if (mState != STATE_OPEN) { + return ERROR_NOT_OPEN; + } + + mPaused = false; + mRender = true; + + // wake up render thread + LOGV(" wakeup render thread\n"); + mCondition.signal(); + return NO_ERROR; +} + +status_t FLACPlayer::stop() +{ + LOGV("stop\n"); + Mutex::Autolock l(mMutex); + if (mState != STATE_OPEN) { + return ERROR_NOT_OPEN; + } + mPaused = true; + mRender = false; + return NO_ERROR; +} + +status_t FLACPlayer::seekTo(int msec) +{ + LOGV("seekTo %d\n", msec); + Mutex::Autolock l(mMutex); + if (mState != STATE_OPEN) { + return ERROR_NOT_OPEN; + } + + FLAC__uint64 target_sample = mTotalSamples * msec / mLengthInMsec; + + if (mTotalSamples > 0 && target_sample >= mTotalSamples && target_sample > 0) + target_sample = mTotalSamples - 1; + + if (!FLAC__stream_decoder_seek_absolute(mDecoder, target_sample)) { + LOGE("FLAC__stream_decoder_seek_absolute failed\n"); + if (FLAC__stream_decoder_get_state(mDecoder) == FLAC__STREAM_DECODER_SEEK_ERROR) { + FLAC__stream_decoder_flush(mDecoder); + } + return ERROR_NOT_SUPPORTED; + } + + mCurrentSample = target_sample; + + sendEvent(MEDIA_SEEK_COMPLETE); + return NO_ERROR; +} + +status_t FLACPlayer::pause() +{ + LOGV("pause\n"); + Mutex::Autolock l(mMutex); + if (mState != STATE_OPEN) { + return ERROR_NOT_OPEN; + } + mPaused = true; + return NO_ERROR; +} + +bool FLACPlayer::isPlaying() +{ + LOGV("isPlaying\n"); + if (mState == STATE_OPEN) { + return mRender; + } + return false; +} + +status_t FLACPlayer::getCurrentPosition(int* msec) +{ + LOGV("getCurrentPosition\n"); + Mutex::Autolock l(mMutex); + if (mState != STATE_OPEN) { + LOGE("getCurrentPosition(): file not open"); + return ERROR_NOT_OPEN; + } + + *msec = (int)(mCurrentSample * 1000 / mSampleRate); + return NO_ERROR; +} + +status_t FLACPlayer::getDuration(int* duration) +{ + LOGV("getDuration\n"); + if (mState != STATE_OPEN) { + return ERROR_NOT_OPEN; + } + + *duration = mLengthInMsec; + return NO_ERROR; +} + +status_t FLACPlayer::release() +{ + LOGV("release\n"); + Mutex::Autolock l(mMutex); + reset_nosync(); + + // TODO: timeout when thread won't exit + // wait for render thread to exit + if (mRenderTid > 0) { + mExit = true; + mCondition.signal(); + mCondition.wait(mMutex); + } + return NO_ERROR; +} + +status_t FLACPlayer::reset() +{ + LOGV("reset\n"); + Mutex::Autolock l(mMutex); + return reset_nosync(); +} + +// always call with lock held +status_t FLACPlayer::reset_nosync() +{ + // close file + if (mFile != NULL) { + FLAC__stream_decoder_delete(mDecoder); + fclose(mFile); + mFile = NULL; + } + mState = STATE_ERROR; + + mTotalSamples = -1; + mBytesPerSample = -1; + mChannels = -1; + mSampleRate = -1; + mLoop = false; + mAndroidLoop = false; + mPaused = false; + mRender = false; + return NO_ERROR; +} + +status_t FLACPlayer::setLooping(int loop) +{ + LOGV("setLooping\n"); + Mutex::Autolock l(mMutex); + mLoop = (loop != 0); + return NO_ERROR; +} + +status_t FLACPlayer::createOutputTrack() { + LOGV("Create AudioTrack object: rate=%ld, channels=%d\n", + mSampleRate, mChannels); + if (mAudioSink->open(mSampleRate, mChannels, AudioSystem::PCM_16_BIT, DEFAULT_AUDIOSINK_BUFFERCOUNT) != NO_ERROR) { + LOGE("mAudioSink open failed\n"); + return ERROR_OPEN_FAILED; + } + return NO_ERROR; +} + +FLAC__StreamDecoderWriteStatus FLACPlayer::vp_write(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) { + FLACPlayer *self = (FLACPlayer *)client_data; + + const uint32_t bytes_per_sample = self->mBytesPerSample; + const uint32_t incr = bytes_per_sample * self->mChannels; + const uint32_t wide_samples = frame->header.blocksize; + const uint32_t frame_size = incr * wide_samples; + + self->mCurrentSample = frame->header.number.sample_number; + + uint32_t sample, wide_sample, channel; + + if (self->mAudioBufferSize < frame_size) { + if (self->mAudioBuffer != NULL) { + delete [] self->mAudioBuffer; + } + self->mAudioBuffer = new FLAC__int8[frame_size]; + self->mAudioBufferSize = frame_size; + } + + FLAC__int8 *s8buffer = self->mAudioBuffer; + FLAC__int16 *s16buffer = (FLAC__int16 *)s8buffer; + + // Interleave channel data like PCM + if (self->mChannels == 2) { + for (sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++) { + s16buffer[sample++] = (FLAC__int16)(buffer[0][wide_sample]); + s16buffer[sample++] = (FLAC__int16)(buffer[1][wide_sample]); + } + } else if (self->mChannels == 1) { + for (sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++) { + s16buffer[sample++] = (FLAC__int16)(buffer[0][wide_sample]); + } + } else { + for (sample = wide_sample = 0; wide_sample < wide_samples; wide_sample++) { + for (channel = 0; channel < self->mChannels; channel++, sample++) { + s16buffer[sample] = (FLAC__int16)(buffer[channel][wide_sample]); + } + } + } + self->mAudioBufferFilled = frame_size; + + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + +int FLACPlayer::renderThread(void* p) { + return ((FLACPlayer*)p)->render(); +} + +int FLACPlayer::render() { + int result = -1; + int temp; + int current_section = 0; + bool audioStarted = false; + + LOGV("render\n"); + + // let main thread know we're ready + { + Mutex::Autolock l(mMutex); + mRenderTid = myTid(); + mCondition.signal(); + } + + while (1) { + FLAC__bool status = true; + { + Mutex::Autolock l(mMutex); + + // pausing? + if (mPaused) { + if (mAudioSink->ready()) mAudioSink->pause(); + mRender = false; + audioStarted = false; + } + + // nothing to render, wait for client thread to wake us up + if (!mExit && !mRender) { + LOGV("render - signal wait\n"); + mCondition.wait(mMutex); + LOGV("render - signal rx'd\n"); + } + if (mExit) break; + + // We could end up here if start() is called, and before we get a + // chance to run, the app calls stop() or reset(). Re-check render + // flag so we don't try to render in stop or reset state. + if (!mRender) continue; + + // create audio output track if necessary + if (!mAudioSink->ready()) { + LOGV("render - create output track\n"); + if (createOutputTrack() != NO_ERROR) + break; + } + + + // start audio output if necessary + if (!audioStarted && !mPaused && !mExit) { + LOGV("render - starting audio\n"); + mAudioSink->start(); + audioStarted = true; + } + + if (FLAC__stream_decoder_get_state(mDecoder) != FLAC__STREAM_DECODER_END_OF_STREAM) { + status = FLAC__stream_decoder_process_single(mDecoder); + } else { + // end of file, do we need to loop? + // ... + if (mLoop || mAndroidLoop) { + FLAC__stream_decoder_seek_absolute(mDecoder, 0); + mCurrentSample = 0; + status = FLAC__stream_decoder_process_single(mDecoder); + } else { + mAudioSink->stop(); + audioStarted = false; + mRender = false; + mPaused = true; + + FLAC__uint64 endpos; + if (!FLAC__stream_decoder_get_decode_position(mDecoder, &endpos)) { + endpos = 0; + } + + LOGV("send MEDIA_PLAYBACK_COMPLETE\n"); + sendEvent(MEDIA_PLAYBACK_COMPLETE); + + // wait until we're started again + LOGV("playback complete - wait for signal\n"); + mCondition.wait(mMutex); + LOGV("playback complete - signal rx'd\n"); + if (mExit) break; + + // if we're still at the end, restart from the beginning + if (mState == STATE_OPEN) { + FLAC__uint64 curpos; + if (FLAC__stream_decoder_get_decode_position(mDecoder, &curpos)) { + curpos = 0; + } + if (curpos == endpos) { + FLAC__stream_decoder_seek_absolute(mDecoder, 0); + mCurrentSample = 0; + } + status = FLAC__stream_decoder_process_single(mDecoder); + } + } + } + } + + if (!status) { + LOGE("Error in FLAC decoder: %s\n", FLAC__stream_decoder_get_resolved_state_string(mDecoder)); + sendEvent(MEDIA_ERROR); + break; + } + + if (mAudioBufferFilled > 0) { + /* Be sure to clear mAudioBufferFilled even if there's an error. */ + uint32_t toPlay = mAudioBufferFilled; + mAudioBufferFilled = 0; + + if (!mAudioSink->write(mAudioBuffer, toPlay)) { + LOGE("Error in FLAC decoder: %s\n", FLAC__stream_decoder_get_resolved_state_string(mDecoder)); + sendEvent(MEDIA_ERROR); + break; + } + } + } + +threadExit: + mAudioSink.clear(); + if (mAudioBuffer != NULL) { + delete [] mAudioBuffer; + mAudioBuffer = NULL; + mAudioBufferSize = 0; + mAudioBufferFilled = 0; + } + + // tell main thread goodbye + Mutex::Autolock l(mMutex); + mRenderTid = -1; + mCondition.signal(); + return result; +} + +} // end namespace android diff --git a/media/libmediaplayerservice/FLACPlayer.h b/media/libmediaplayerservice/FLACPlayer.h new file mode 100644 index 0000000..b7068c3 --- /dev/null +++ b/media/libmediaplayerservice/FLACPlayer.h @@ -0,0 +1,100 @@ +/* +** +** Copyright 2009, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef ANDROID_FLACPLAYER_H +#define ANDROID_FLACPLAYER_H + +#include <utils/threads.h> + +#include <media/MediaPlayerInterface.h> +#include <media/AudioTrack.h> + +#include "FLAC/all.h" + +#define ANDROID_LOOP_TAG "ANDROID_LOOP" + +namespace android { + +class FLACPlayer : public MediaPlayerInterface { +public: + FLACPlayer(); + ~FLACPlayer(); + + virtual void onFirstRef(); + virtual status_t initCheck(); + + virtual status_t setDataSource( + const char *uri, const KeyedVector<String8, String8> *headers); + + virtual status_t setDataSource(int fd, int64_t offset, int64_t length); + virtual status_t setVideoSurface(const sp<ISurface>& surface) { return UNKNOWN_ERROR; } + virtual status_t prepare(); + virtual status_t prepareAsync(); + virtual status_t start(); + virtual status_t stop(); + virtual status_t seekTo(int msec); + virtual status_t pause(); + virtual bool isPlaying(); + virtual status_t getCurrentPosition(int* msec); + virtual status_t getDuration(int* msec); + virtual status_t release(); + virtual status_t reset(); + virtual status_t setLooping(int loop); + virtual player_type playerType() { return FLAC_PLAYER; } + virtual status_t invoke(const Parcel& request, Parcel *reply) {return INVALID_OPERATION;} + +private: + status_t setdatasource(const char *path, int fd, int64_t offset, int64_t length); + status_t reset_nosync(); + status_t createOutputTrack(); + static int renderThread(void*); + int render(); + + static void vp_metadata(const FLAC__StreamDecoder *, const FLAC__StreamMetadata *, void *); + static void vp_error(const FLAC__StreamDecoder *, const FLAC__StreamDecoderErrorStatus, void *); + static FLAC__StreamDecoderWriteStatus + vp_write(const FLAC__StreamDecoder *, const FLAC__Frame *, const FLAC__int32 * const[], void *); + + FLAC__uint64 mTotalSamples; + FLAC__uint64 mCurrentSample; + uint32_t mBytesPerSample; + uint32_t mChannels; + uint32_t mSampleRate; + uint32_t mLengthInMsec; + + FLAC__int8 * mAudioBuffer; + uint32_t mAudioBufferSize; + uint32_t mAudioBufferFilled; + + Mutex mMutex; + Condition mCondition; + FILE* mFile; + FLAC__StreamDecoder* mDecoder; + status_t mState; + int mStreamType; + bool mLoop; + bool mAndroidLoop; + volatile bool mExit; + bool mPaused; + volatile bool mRender; + pid_t mRenderTid; +}; + +}; // namespace android + +#endif // ANDROID_FLACPLAYER_H + diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 3e1f4a5..911d89f 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -57,6 +57,7 @@ #include "MidiFile.h" #include "VorbisPlayer.h" +#include "FLACPlayer.h" #include <media/PVPlayer.h> #include "TestPlayerStub.h" #include "StagefrightPlayer.h" @@ -204,6 +205,7 @@ extmap FILE_EXTS [] = { {".wmv", PV_PLAYER}, {".asf", PV_PLAYER}, #endif + {".flac", FLAC_PLAYER}, }; // TODO: Find real cause of Audio/Video delay in PV framework and remove this workaround @@ -719,6 +721,9 @@ player_type getPlayerType(int fd, int64_t offset, int64_t length) } #endif + if (ident == 0x43614c66) // 'fLaC' + return FLAC_PLAYER; + // Some kind of MIDI? EAS_DATA_HANDLE easdata; if (EAS_Init(&easdata) == EAS_SUCCESS) { @@ -812,6 +817,10 @@ static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie, LOGV("Create Test Player stub"); p = new TestPlayerStub(); break; + case FLAC_PLAYER: + LOGV(" create FLACPlayer"); + p = new FLACPlayer(); + break; } if (p != NULL) { if (p->initCheck() == NO_ERROR) { diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp index 12de0d9..189d97d3 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.cpp +++ b/media/libmediaplayerservice/MediaRecorderClient.cpp @@ -196,6 +196,16 @@ status_t MediaRecorderClient::setParameters(const String8& params) { return mRecorder->setParameters(params); } +status_t MediaRecorderClient::setCameraParameters(const String8& params) { + LOGV("setCameraParameters(%s)", params.string()); + Mutex::Autolock lock(mLock); + if (mRecorder == NULL) { + LOGE("recorder is not initialized"); + return NO_INIT; + } + return mRecorder->setCameraParameters(params); +} + status_t MediaRecorderClient::prepare() { LOGV("prepare"); diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h index 805005d..4a6b9a1 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.h +++ b/media/libmediaplayerservice/MediaRecorderClient.h @@ -40,6 +40,7 @@ public: virtual status_t setVideoSize(int width, int height); virtual status_t setVideoFrameRate(int frames_per_second); virtual status_t setParameters(const String8& params); + virtual status_t setCameraParameters(const String8& params); virtual status_t setListener(const sp<IMediaPlayerClient>& listener); virtual status_t prepare(); virtual status_t getMaxAmplitude(int* max); diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp index 550b84d..070680e 100644 --- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp +++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp @@ -28,8 +28,12 @@ #include <string.h> #include <cutils/atomic.h> #include <cutils/properties.h> +#ifdef USE_ECLAIR_MEMORYDEALER +#include <binder/MemoryDealer.h> +#else #include <binder/MemoryBase.h> #include <binder/MemoryHeapBase.h> +#endif #include <android_runtime/ActivityManager.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> @@ -63,6 +67,10 @@ MetadataRetrieverClient::MetadataRetrieverClient(pid_t pid) { LOGV("MetadataRetrieverClient constructor pid(%d)", pid); mPid = pid; +#ifdef USE_ECLAIR_MEMORYDEALER + mThumbnailDealer = NULL; + mAlbumArtDealer = NULL; +#endif mThumbnail = NULL; mAlbumArt = NULL; mRetriever = NULL; @@ -93,6 +101,10 @@ void MetadataRetrieverClient::disconnect() LOGV("disconnect from pid %d", mPid); Mutex::Autolock lock(mLock); mRetriever.clear(); +#ifdef USE_ECLAIR_MEMORYDEALER + mThumbnailDealer.clear(); + mAlbumArtDealer.clear(); +#endif mThumbnail.clear(); mAlbumArt.clear(); mMode = METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL; @@ -239,6 +251,9 @@ sp<IMemory> MetadataRetrieverClient::captureFrame() LOGV("captureFrame"); Mutex::Autolock lock(mLock); mThumbnail.clear(); +#ifdef USE_ECLAIR_MEMORYDEALER + mThumbnailDealer.clear(); +#endif if (mRetriever == NULL) { LOGE("retriever is not initialized"); return NULL; @@ -249,15 +264,27 @@ sp<IMemory> MetadataRetrieverClient::captureFrame() return NULL; } size_t size = sizeof(VideoFrame) + frame->mSize; +#ifdef USE_ECLAIR_MEMORYDEALER + mThumbnailDealer = new MemoryDealer(size); + if (mThumbnailDealer == NULL) { +#else sp<MemoryHeapBase> heap = new MemoryHeapBase(size, 0, "MetadataRetrieverClient"); if (heap == NULL) { +#endif LOGE("failed to create MemoryDealer"); delete frame; return NULL; } +#ifdef USE_ECLAIR_MEMORYDEALER + mThumbnail = mThumbnailDealer->allocate(size); +#else mThumbnail = new MemoryBase(heap, 0, size); +#endif if (mThumbnail == NULL) { LOGE("not enough memory for VideoFrame size=%u", size); +#ifdef USE_ECLAIR_MEMORYDEALER + mThumbnailDealer.clear(); +#endif delete frame; return NULL; } @@ -278,6 +305,9 @@ sp<IMemory> MetadataRetrieverClient::extractAlbumArt() LOGV("extractAlbumArt"); Mutex::Autolock lock(mLock); mAlbumArt.clear(); +#ifdef USE_ECLAIR_MEMORYDEALER + mAlbumArtDealer.clear(); +#endif if (mRetriever == NULL) { LOGE("retriever is not initialized"); return NULL; @@ -288,15 +318,27 @@ sp<IMemory> MetadataRetrieverClient::extractAlbumArt() return NULL; } size_t size = sizeof(MediaAlbumArt) + albumArt->mSize; +#ifdef USE_ECLAIR_MEMORYDEALER + mAlbumArtDealer = new MemoryDealer(size); + if (mAlbumArtDealer == NULL) { +#else sp<MemoryHeapBase> heap = new MemoryHeapBase(size, 0, "MetadataRetrieverClient"); if (heap == NULL) { +#endif LOGE("failed to create MemoryDealer object"); delete albumArt; return NULL; } +#ifdef USE_ECLAIR_MEMORYDEALER + mAlbumArt = mAlbumArtDealer->allocate(size); +#else mAlbumArt = new MemoryBase(heap, 0, size); +#endif if (mAlbumArt == NULL) { LOGE("not enough memory for MediaAlbumArt size=%u", size); +#ifdef USE_ECLAIR_MEMORYDEALER + mAlbumArtDealer.clear(); +#endif delete albumArt; return NULL; } diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.h b/media/libmediaplayerservice/MetadataRetrieverClient.h index 4aab94f..7e6d223 100644 --- a/media/libmediaplayerservice/MetadataRetrieverClient.h +++ b/media/libmediaplayerservice/MetadataRetrieverClient.h @@ -24,7 +24,6 @@ #include <utils/Errors.h> #include <utils/KeyedVector.h> #include <binder/IMemory.h> - #include <media/MediaMetadataRetrieverInterface.h> @@ -63,6 +62,10 @@ private: int mMode; // Keep the shared memory copy of album art and capture frame (for thumbnail) +#ifdef USE_ECLAIR_MEMORYDEALER + sp<MemoryDealer> mAlbumArtDealer; + sp<MemoryDealer> mThumbnailDealer; +#endif sp<IMemory> mAlbumArt; sp<IMemory> mThumbnail; }; diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index 531fd11..573932f 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -133,6 +133,10 @@ status_t StagefrightRecorder::setParameters(const String8 ¶ms) { return OK; } +status_t StagefrightRecorder::setCameraParameters(const String8 ¶ms) { + return OK; +} + status_t StagefrightRecorder::setListener(const sp<IMediaPlayerClient> &listener) { mListener = listener; diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h index 7ec412d..e5a84c1 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -43,6 +43,7 @@ struct StagefrightRecorder : public MediaRecorderBase { virtual status_t setOutputFile(const char *path); virtual status_t setOutputFile(int fd, int64_t offset, int64_t length); virtual status_t setParameters(const String8& params); + virtual status_t setCameraParameters(const String8& params); virtual status_t setListener(const sp<IMediaPlayerClient>& listener); virtual status_t prepare(); virtual status_t start(); diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 81f995b..c4b88cc 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -47,10 +47,11 @@ LOCAL_CFLAGS += -DBUILD_WITH_FULL_STAGEFRIGHT endif LOCAL_C_INCLUDES:= \ - $(JNI_H_INCLUDE) \ + $(JNI_H_INCLUDE) \ $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \ $(TOP)/external/opencore/android \ - $(TOP)/external/tremolo + $(TOP)/external/tremolo \ + $(TOP)/external/flac/include LOCAL_SHARED_LIBRARIES := \ libbinder \ @@ -60,8 +61,13 @@ LOCAL_SHARED_LIBRARIES := \ libui \ libsonivox \ libvorbisidec \ - libsurfaceflinger_client \ - libcamera_client + libFLAC + +ifneq ($(BOARD_USES_ECLAIR_LIBCAMERA),true) + LOCAL_SHARED_LIBRARIES += \ + libsurfaceflinger_client \ + libcamera_client +endif LOCAL_STATIC_LIBRARIES := \ libstagefright_aacdec \ diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 69da7ef..e9a7248 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -1211,8 +1211,11 @@ status_t OMXCodec::allocateBuffersOnPort(OMX_U32 portIndex) { } size_t totalSize = def.nBufferCountActual * def.nBufferSize; +#ifdef USE_ECLAIR_MEMORYDEALER + mDealer[portIndex] = new MemoryDealer(totalSize); +#else mDealer[portIndex] = new MemoryDealer(totalSize, "OMXCodec"); - +#endif for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) { sp<IMemory> mem = mDealer[portIndex]->allocate(def.nBufferSize); CHECK(mem.get() != NULL); diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp index 03287dd1..25236cf 100644 --- a/media/libstagefright/StagefrightMediaScanner.cpp +++ b/media/libstagefright/StagefrightMediaScanner.cpp @@ -30,6 +30,9 @@ #include <Tremolo/ivorbiscodec.h> #include <Tremolo/ivorbisfile.h> +// FLAC includes +#include <FLAC/all.h> + namespace android { StagefrightMediaScanner::StagefrightMediaScanner() @@ -42,7 +45,8 @@ static bool FileHasAcceptableExtension(const char *extension) { static const char *kValidExtensions[] = { ".mp3", ".mp4", ".m4a", ".3gp", ".3gpp", ".3g2", ".3gpp2", ".mpeg", ".ogg", ".mid", ".smf", ".imy", ".wma", ".aac", - ".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota" + ".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota", + ".flac" }; static const size_t kNumValidExtensions = sizeof(kValidExtensions) / sizeof(kValidExtensions[0]); @@ -144,10 +148,78 @@ failure: return UNKNOWN_ERROR; } +static FLAC__StreamDecoderWriteStatus flac_write(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data) { + (void)decoder, (void)frame, (void)buffer, (void)client_data; + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + +static void flac_error(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) { + (void)decoder, (void)status, (void)client_data; +} + +static void flac_metadata(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) { + MediaScannerClient *client = (MediaScannerClient *)client_data; + + if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { + FLAC__uint64 duration = 1000 * metadata->data.stream_info.total_samples / metadata->data.stream_info.sample_rate; + if (duration > 0) { + char buffer[20]; + sprintf(buffer, "%lld", duration); + if (!client->addStringTag("duration", buffer)) + return; + } + } else if (metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { + for (uint32_t i = 0; i < metadata->data.vorbis_comment.num_comments; i++) { + char *ptr = (char *)metadata->data.vorbis_comment.comments[i].entry; + + char *val = strchr(ptr, '='); + if (val) { + int keylen = val++ - ptr; + char key[keylen + 1]; + strncpy(key, ptr, keylen); + key[keylen] = 0; + LOGD("flac_metadata: key: %s value: %s", key, val); + if (!client->addStringTag(key, val)) return; + } + } + } +} + +static status_t HandleFLAC(const char *filename, MediaScannerClient *client) +{ + status_t status = UNKNOWN_ERROR; + + FLAC__StreamDecoder *decoder; + + decoder = FLAC__stream_decoder_new(); + if (!decoder) + return status; + + FLAC__stream_decoder_set_md5_checking(decoder, false); + FLAC__stream_decoder_set_metadata_ignore_all(decoder); + FLAC__stream_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_STREAMINFO); + FLAC__stream_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT); + + FLAC__StreamDecoderInitStatus init_status; + init_status = FLAC__stream_decoder_init_file(decoder, filename, flac_write, flac_metadata, flac_error, client); + if (init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK) + goto exit; + + if (!FLAC__stream_decoder_process_until_end_of_metadata(decoder)) + goto exit; + + status = OK; + +exit: + FLAC__stream_decoder_finish(decoder); + FLAC__stream_decoder_delete(decoder); + + return OK; +} + status_t StagefrightMediaScanner::processFile( const char *path, const char *mimeType, MediaScannerClient &client) { - LOGV("processFile '%s'.", path); client.setLocale(locale()); client.beginFile(); @@ -179,6 +251,10 @@ status_t StagefrightMediaScanner::processFile( return HandleOGG(path, &client); } + if (!strcasecmp(extension, ".flac")) { + return HandleFLAC(path, &client); + } + if (mRetriever->setDataSource(path) == OK && mRetriever->setMode( METADATA_MODE_METADATA_RETRIEVAL_ONLY) == OK) { diff --git a/media/libstagefright/colorconversion/Android.mk b/media/libstagefright/colorconversion/Android.mk index b9ba1be..7e8a14a 100644 --- a/media/libstagefright/colorconversion/Android.mk +++ b/media/libstagefright/colorconversion/Android.mk @@ -13,9 +13,13 @@ LOCAL_SHARED_LIBRARIES := \ libmedia \ libutils \ libui \ - libcutils \ - libsurfaceflinger_client\ - libcamera_client + libcutils + +ifneq ($(BOARD_USES_ECLAIR_LIBCAMERA),true) + LOCAL_SHARED_LIBRARIES += \ + libsurfaceflinger_client \ + libcamera_client +endif LOCAL_MODULE:= libstagefright_color_conversion diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp index fcf506d..0245c11 100644 --- a/media/libstagefright/omx/tests/OMXHarness.cpp +++ b/media/libstagefright/omx/tests/OMXHarness.cpp @@ -24,7 +24,11 @@ #include <binder/ProcessState.h> #include <binder/IServiceManager.h> +#ifdef USE_ECLAIR_MEMORYDEALER #include <binder/MemoryDealer.h> +#else +#include <binder/MemoryDealer.h> +#endif #include <media/IMediaPlayerService.h> #include <media/stagefright/DataSource.h> #include <media/stagefright/MediaBuffer.h> @@ -286,7 +290,11 @@ status_t Harness::testStateTransitions( return OK; } +#ifdef USE_ECLAIR_MEMORYDEALER + sp<MemoryDealer> dealer = new MemoryDealer(8 * 1024 * 1024); +#else sp<MemoryDealer> dealer = new MemoryDealer(8 * 1024 * 1024, "OMXHarness"); +#endif IOMX::node_id node; status_t err = diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp index 89b3e1f..40317e7 100644 --- a/opengl/libs/EGL/egl.cpp +++ b/opengl/libs/EGL/egl.cpp @@ -1043,6 +1043,10 @@ EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, int i=0, index=0; egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index); if (cnx) { + if (share_list != EGL_NO_CONTEXT) { + egl_context_t* const c = get_context(share_list); + share_list = c->context; + } EGLContext context = cnx->egl.eglCreateContext( dp->disp[i].dpy, dp->disp[i].config[index], share_list, attrib_list); diff --git a/packages/VpnServices/src/com/android/server/vpn/OpenvpnService.java b/packages/VpnServices/src/com/android/server/vpn/OpenvpnService.java new file mode 100644 index 0000000..6bb6496 --- /dev/null +++ b/packages/VpnServices/src/com/android/server/vpn/OpenvpnService.java @@ -0,0 +1,423 @@ +/* + * Copyright (C) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vpn; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Scanner; + +import android.net.LocalSocket; +import android.net.LocalSocketAddress; +import android.net.vpn.OpenvpnProfile; +import android.net.vpn.VpnManager; +import android.security.Credentials; +import android.util.Log; + +/** + * The service that manages the openvpn VPN connection. + */ +class OpenvpnService extends VpnService<OpenvpnProfile> { + private static final String OPENVPN_DAEMON = "openvpn"; + private static final String MTPD = "mtpd"; + private static final String USE_INLINE = "[[INLINE]]"; + private static final String USE_KEYSTORE = "[[ANDROID]]"; + private static final String TAG = OpenvpnService.class.getSimpleName(); + private static int count = 0; + + private final String socketName = OPENVPN_DAEMON + getCount(); + + private transient OpenvpnThread thread = null; + + private transient String mPassword; + private transient String mUsername; + + private synchronized static String getCount() { + return Integer.toString(count++); + } + + @Override + protected void connect(String serverIp, String username, String password) + throws IOException { + OpenvpnProfile p = getProfile(); + ArrayList<String> args = new ArrayList<String>(); + + mUsername = username; + mPassword = password; + + args.add(OPENVPN_DAEMON); + args.add("--dev"); + if (p.getDevice() != null) { + args.add(p.getDevice()); + } else { + args.add("tun"); + } + args.add("--remote"); + args.add(serverIp); + args.add("--nobind"); + args.add("--proto"); + args.add(p.getProto()); + args.add("--client"); + args.add("--rport"); + args.add(p.getPort()); + if (p.getCAName() != null) { + args.add("--ca"); + args.add(USE_INLINE); + args.add(USE_KEYSTORE + Credentials.CA_CERTIFICATE + p.getCAName()); + } + if (p.getCertName() != null) { + args.add("--cert"); + args.add(USE_INLINE); + args.add(USE_KEYSTORE + Credentials.USER_CERTIFICATE + p.getCertName()); + args.add("--key"); + args.add(USE_INLINE); + args.add(USE_KEYSTORE + Credentials.USER_PRIVATE_KEY + p.getCertName()); + } + args.add("--persist-tun"); + args.add("--persist-key"); + args.add("--management"); + args.add("/dev/socket/" + socketName); + args.add("unix"); + args.add("--management-hold"); + if (p.getUseCompLzo()) { + args.add("--comp-lzo"); + } + if (p.getRedirectGateway()) { + args.add("--redirect-gateway def1"); + } + if (p.getUserAuth()) { + args.add("--auth-user-pass"); + args.add("--management-query-passwords"); + } + if (p.getSupplyAddr()) { + args.add("--ifconfig"); + args.add(p.getLocalAddr()); + args.add(p.getRemoteAddr()); + } + if (p.getCipher() != null) { + args.add("--cipher"); + args.add(p.getCipher()); + } + if (p.getKeySize() != null && !p.getKeySize().equals("")) { + args.add("--keysize"); + args.add(p.getKeySize()); + } + args.add("--up"); + args.add("/system/xbin/openvpn-up.sh"); + args.add("--script-security"); + args.add("2"); + + if (p.getExtra() != null && !p.getExtra().equals("")) { + Scanner s = new Scanner(p.getExtra()); + while(s.hasNext()) args.add(s.next()); + } + DaemonProxy mtpd = getDaemons().startDaemon(MTPD); + mtpd.sendCommand(args.toArray(new String[args.size()])); + } + + @Override + protected void disconnect() { + if (thread != null) + thread.disconnectAndWait(); + } + + @Override + void waitUntilConnectedOrTimedout() throws IOException { + thread = new OpenvpnThread(); + thread.openvpnStart(); + thread.waitConnect(60); + setVpnStateUp(true); + } + + @Override + protected void recover() { + try { + thread = new OpenvpnThread(); + thread.openvpnStart(); + } catch (IOException e) { + onError(e); + } + } + + void startConnectivityMonitor() { + /* Openvpn is completely event driven, so we don't need + * a polling monitor at all, so do nothing here */ + } + + private class OpenvpnThread extends Thread { + InputStream in; + OutputStream out; + LocalSocket mSocket; + + boolean finalDisconnect = false; + boolean firstConnect = false; + boolean disconnecting = false; + boolean passwordError = false; + + boolean SFbool; + volatile String SFreason; + String vpnState = "WAIT"; // initial state + + OpenvpnThread() throws IOException { + openSocket(); + in = mSocket.getInputStream(); + out = mSocket.getOutputStream(); + } + + public void openvpnStart() throws IOException { + super.start(); + send("state on"); // make state dynamic + send("log on"); // dynamically log over the socket + send("hold off"); // don't hold for subsequent reconnects + send("hold release"); // release from hold + send("bytecount 2"); // need this to update the monitor + } + + public synchronized void disconnectAndWait() { + try { + disconnecting = true; + send("signal SIGTERM"); + while (!finalDisconnect) + this.wait(); + } catch(Exception e) { + // we're done + } + } + + public synchronized void waitConnect(long seconds) throws IOException { + long endTime = System.currentTimeMillis() + seconds * 1000; + long wait; + while (!isConnected() && (wait = (endTime - System.currentTimeMillis())) > 0) { + try { + this.wait(wait); + } catch(InterruptedException e) { + // do nothing + } + if (passwordError) + throw new VpnConnectingError(VpnManager.VPN_ERROR_AUTH); + } + if (!isConnected()) + throw new VpnConnectingError(VpnManager.VPN_ERROR_CONNECTION_FAILED); + firstConnect = true; + } + + private boolean isConnected() { + return vpnState.equals("CONNECTED"); + } + + private void openSocket() throws IOException { + LocalSocket s = new LocalSocket(); + LocalSocketAddress a = new LocalSocketAddress(socketName, + LocalSocketAddress.Namespace.RESERVED); + IOException excp = null; + for (int i = 0; i < 10; i++) { + try { + s.connect(a); + mSocket = s; + return; + } catch (IOException e) { + excp = e; + try { + Thread.currentThread().sleep(500); + } catch (InterruptedException ex) { + throw new RuntimeException(ex); + } + } + } + throw excp; + } + + private synchronized boolean waitForSuccessOrFail() { + SFreason = null; + try { + while (SFreason == null) { + this.wait(); + } + return SFbool; + } catch(InterruptedException e) { + return false; + } + } + + private synchronized void signalSuccessOrFail(boolean success, String reason) { + SFbool = success; + SFreason = reason; + this.notifyAll(); + } + + + private synchronized void sendAsync(String str) throws IOException { + str += "\n"; + out.write(str.getBytes()); + out.flush(); + } + + private boolean send(String str) throws IOException { + sendAsync(str); + return waitForSuccessOrFail(); + } + + private synchronized void signalState(String s) { + // state strings come as <date in secs>, <state>, <other stuff> + int first = s.indexOf(','); + if (first == -1) + return; + int second = s.indexOf(',', first + 1); + if (second == -1) + return; + String state = s.substring(first + 1, second); + + /* + * state can be: + * + * + * CONNECTING -- OpenVPN's initial state. + * WAIT -- (Client only) Waiting for initial response + * from server. + * AUTH -- (Client only) Authenticating with server. + * GET_CONFIG -- (Client only) Downloading configuration options + * from server. + * ASSIGN_IP -- Assigning IP address to virtual network + * interface. + * ADD_ROUTES -- Adding routes to system. + * CONNECTED -- Initialization Sequence Completed. + * RECONNECTING -- A restart has occurred. + * EXITING -- A graceful exit is in progress. + * + * Really all we care about is connected or not + */ + vpnState = state; + this.notifyAll(); + if (state.equals("EXITING") && firstConnect && !disconnecting) + onError(new IOException("Connection Closed")); + } + + private synchronized void signalPassword(String s) throws IOException { + /* message should be Need '<auth type>' password + * but coult be Verification Failed: '<auth type' + */ + + int first = s.indexOf('\''); + int second = s.indexOf('\'', first + 1); + final String authType = s.substring(first + 1, second); + + /* AuthType can be one of + * + * "Auth" - regular client server authentication + * "Private Key" - password for private key (unimplemented) + */ + + if (s.startsWith("Need")) { + /* we're in the processor thread, so we have to send + * these asynchronously to avoid a deadlock */ + sendAsync("username '" + authType +"' '" + mUsername + "'"); + sendAsync("password '" + authType +"' '" + mPassword + "'"); + } else { + // must be signalling authentication failure + passwordError = true; + this.notifyAll(); + } + } + + private void signalBytecount(String s) { + int index = s.indexOf(','); + if (index == -1) + // no , in message, ignore it + return; + + String in = s.substring(0, index); + String out = s.substring(index+1); + vpnStateUpdate(Long.parseLong(in), Long.parseLong(out)); + } + + private void signalLog(String s) { + //log format is <date in secs>,<severity>,<message> + int first = s.indexOf(','); + if (first == -1) + return; + int second = s.indexOf(',', first + 1); + if (second == -1) + return; + String message = s.substring(second + 1); + Log.i("openvpn", message); + } + + private void parseLine(String s) throws IOException { + int index = s.indexOf(':'); + if (index == -1) + // no : in message, ignore it + return; + + String token = s.substring(0, index); + String body = s.substring(index +1); + + if (token.equals(">INFO")) { + // This is the starting string, just skip it + } else if (token.equals("SUCCESS")) { + signalSuccessOrFail(true, body); + } else if (token.equals("ERROR")) { + signalSuccessOrFail(false, body); + } else if (token.equals(">STATE")) { + signalState(body); + } else if (token.equals(">FATAL")) { + signalState("EXITING," + body); + } else if (token.equals(">PASSWORD")) { + signalPassword(body); + } else if (token.equals(">LOG")) { + signalLog(body); + }else if (token.equals(">HOLD")) { + // just warning us we're in a hold state, ignore + } else if (token.equals(">BYTECOUNT")) { + signalBytecount(body); + } else { + Log.w(TAG, "Unknown control token:\"" + token + "\""); + } + } + + public void run() { + + System.out.println("THREAD " + this + " RUNNING"); + + try { + int c; + StringBuffer s = new StringBuffer(); + while (true) { + c = in.read(); + if (c == -1) + throw new IOException("End of Stream"); + if (c == '\n') { + parseLine(s.toString()); + s = new StringBuffer(); + continue; + } + if (c == '\r') + continue; + s.append((char)c); + } + } catch(IOException e) { + // terminate + } finally { + synchronized(this) { + finalDisconnect = true; + this.notifyAll(); + } + System.out.println("THREAD " + this + " TERMINATED"); + } + } + } +} diff --git a/packages/VpnServices/src/com/android/server/vpn/VpnDaemons.java b/packages/VpnServices/src/com/android/server/vpn/VpnDaemons.java index 499195f..2346299 100644 --- a/packages/VpnServices/src/com/android/server/vpn/VpnDaemons.java +++ b/packages/VpnServices/src/com/android/server/vpn/VpnDaemons.java @@ -99,7 +99,7 @@ class VpnDaemons implements Serializable { return 0; } - private synchronized DaemonProxy startDaemon(String daemonName) + synchronized DaemonProxy startDaemon(String daemonName) throws IOException { DaemonProxy daemon = new DaemonProxy(daemonName); mDaemonList.add(daemon); diff --git a/packages/VpnServices/src/com/android/server/vpn/VpnService.java b/packages/VpnServices/src/com/android/server/vpn/VpnService.java index 63b87b1..7c64f95 100644 --- a/packages/VpnServices/src/com/android/server/vpn/VpnService.java +++ b/packages/VpnServices/src/com/android/server/vpn/VpnService.java @@ -85,6 +85,13 @@ abstract class VpnService<E extends VpnProfile> implements Serializable { String password) throws IOException; /** + * Disconnects the vpn + */ + protected void disconnect() { + mDaemons.stopAll(); + } + + /** * Returns the daemons management class for this service object. */ protected VpnDaemons getDaemons() { @@ -110,14 +117,19 @@ abstract class VpnService<E extends VpnProfile> implements Serializable { recover(context); } + // intended for override by subclasses + protected void recover() { + startConnectivityMonitor(); + } + void recover(VpnServiceBinder context) { mContext = context; mNotification = new NotificationHelper(); if (VpnState.CONNECTED.equals(mState)) { Log.i("VpnService", " recovered: " + mProfile.getName()); - startConnectivityMonitor(); - } + recover(); + } } VpnState getState() { @@ -147,7 +159,7 @@ abstract class VpnService<E extends VpnProfile> implements Serializable { setState(VpnState.DISCONNECTING); mNotification.showDisconnect(); - mDaemons.stopAll(); + disconnect(); } catch (Throwable e) { Log.e(TAG, "onDisconnect()", e); } finally { @@ -155,7 +167,7 @@ abstract class VpnService<E extends VpnProfile> implements Serializable { } } - private void onError(Throwable error) { + void onError(Throwable error) { // error may occur during or after connection setup // and it may be due to one or all services gone if (mError != null) { @@ -167,7 +179,7 @@ abstract class VpnService<E extends VpnProfile> implements Serializable { onDisconnect(); } - private void onError(int errorCode) { + void onError(int errorCode) { onError(new VpnConnectingError(errorCode)); } @@ -183,7 +195,7 @@ abstract class VpnService<E extends VpnProfile> implements Serializable { } } - private void waitUntilConnectedOrTimedout() throws IOException { + void waitUntilConnectedOrTimedout() throws IOException { sleep(2000); // 2 seconds for (int i = 0; i < 80; i++) { if (mState != VpnState.CONNECTING) { @@ -207,6 +219,21 @@ abstract class VpnService<E extends VpnProfile> implements Serializable { } } + void setVpnStateUp(boolean state) throws IOException { + if (state) { + SystemProperties.set(VPN_STATUS, VPN_IS_UP); + onConnected(); + mNotification.update(System.currentTimeMillis()); + } else { + SystemProperties.set(VPN_STATUS, VPN_IS_DOWN); + } + } + + void vpnStateUpdate(long in, long out) { + // currently don't show in and out bytes in status + mNotification.update(System.currentTimeMillis()); + } + private synchronized void onConnected() throws IOException { if (DBG) Log.d(TAG, "onConnected()"); @@ -276,6 +303,11 @@ abstract class VpnService<E extends VpnProfile> implements Serializable { private void setVpnDns() { String vpnDns1 = SystemProperties.get(VPN_DNS1); String vpnDns2 = SystemProperties.get(VPN_DNS2); + if (vpnDns1.length() == 0) { + Log.i(TAG, "No vpn dns supplied, not updating"); + return; + } + SystemProperties.set(DNS1, vpnDns1); SystemProperties.set(DNS2, vpnDns2); Log.i(TAG, String.format("set vpn dns prop: %s, %s", @@ -323,7 +355,7 @@ abstract class VpnService<E extends VpnProfile> implements Serializable { } } - private void startConnectivityMonitor() { + void startConnectivityMonitor() { new Thread(new Runnable() { public void run() { Log.i(TAG, "VPN connectivity monitor running"); diff --git a/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java b/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java index 5672a01..162aea7 100644 --- a/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java +++ b/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java @@ -22,6 +22,7 @@ import android.net.vpn.IVpnService; import android.net.vpn.L2tpIpsecProfile; import android.net.vpn.L2tpIpsecPskProfile; import android.net.vpn.L2tpProfile; +import android.net.vpn.OpenvpnProfile; import android.net.vpn.PptpProfile; import android.net.vpn.VpnManager; import android.net.vpn.VpnProfile; @@ -47,7 +48,7 @@ public class VpnServiceBinder extends Service { private static final String TAG = VpnServiceBinder.class.getSimpleName(); private static final boolean DBG = true; - private static final String STATES_FILE_RELATIVE_PATH = "/misc/vpn/.states"; + private static final String STATES_FILE_RELATIVE_PATH = "/data/misc/vpn/.states"; // The actual implementation is delegated to the VpnService class. private VpnService<? extends VpnProfile> mService; @@ -159,6 +160,11 @@ public class VpnServiceBinder extends Service { l2tp.setContext(this, (L2tpProfile) p); return l2tp; + case OPENVPN: + OpenvpnService ovpn = new OpenvpnService(); + ovpn.setContext(this, (OpenvpnProfile)p ); + return ovpn; + case PPTP: PptpService pptp = new PptpService(); pptp.setContext(this, (PptpProfile) p); diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index 81b8d40..5c8ee53 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -430,6 +430,12 @@ public class ConnectivityService extends IConnectivityManager.Stub { public NetworkInfo getNetworkInfo(int networkType) { enforceAccessPermission(); + + // HACK! - return a stub NetworkInfo object to make bad apps happy. + if (ConnectivityManager.TYPE_WIMAX == networkType) { + return NetworkInfo.getEmptyWimaxNetworkInfo(); + } + if (ConnectivityManager.isNetworkTypeValid(networkType)) { NetworkStateTracker t = mNetTrackers[networkType]; if (t != null) diff --git a/services/java/com/android/server/InputDevice.java b/services/java/com/android/server/InputDevice.java index 414b69f..e198118 100644 --- a/services/java/com/android/server/InputDevice.java +++ b/services/java/com/android/server/InputDevice.java @@ -23,27 +23,32 @@ import android.view.Surface; import android.view.WindowManagerPolicy; import java.io.PrintWriter; +import java.io.FileInputStream; +import java.util.StringTokenizer; +import android.view.RawInputEvent; public class InputDevice { static final boolean DEBUG_POINTERS = false; static final boolean DEBUG_HACKS = false; - + static final boolean DEBUG_MOUSE = false; + static final String TAG = "InputDevice"; + /** Amount that trackball needs to move in order to generate a key event. */ static final int TRACKBALL_MOVEMENT_THRESHOLD = 6; /** Maximum number of pointers we will track and report. */ static final int MAX_POINTERS = 10; - + /** * Slop distance for jumpy pointer detection. * The vertical range of the screen divided by this is our epsilon value. */ private static final int JUMPY_EPSILON_DIVISOR = 212; - + /** Number of jumpy points to drop for touchscreens that need it. */ private static final int JUMPY_TRANSITION_DROPS = 3; private static final int JUMPY_DROP_LIMIT = 3; - + final int id; final int classes; final String name; @@ -51,18 +56,18 @@ public class InputDevice { final AbsoluteInfo absY; final AbsoluteInfo absPressure; final AbsoluteInfo absSize; - + long mKeyDownTime = 0; int mMetaKeysState = 0; - + // For use by KeyInputQueue for keeping track of the current touch // data in the old non-multi-touch protocol. final int[] curTouchVals = new int[MotionEvent.NUM_SAMPLE_DATA * 2]; - + final MotionState mAbs = new MotionState(0, 0); final MotionState mRel = new MotionState(TRACKBALL_MOVEMENT_THRESHOLD, TRACKBALL_MOVEMENT_THRESHOLD); - + static class MotionState { int xPrecision; int yPrecision; @@ -72,16 +77,16 @@ public class InputDevice { boolean changed = false; boolean everChanged = false; long mDownTime = 0; - + // The currently assigned pointer IDs, corresponding to the last data. int[] mPointerIds = new int[MAX_POINTERS]; - + // This is the last generated pointer data, ordered to match // mPointerIds. boolean mSkipLastPointers; int mLastNumPointers = 0; final int[] mLastData = new int[MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS]; - + // This is the next set of pointer data being generated. It is not // in any known order, and will be propagated in to mLastData // as part of mapping it to the appropriate pointer IDs. @@ -90,14 +95,14 @@ public class InputDevice { int mNextNumPointers = 0; final int[] mNextData = new int[(MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS) + MotionEvent.NUM_SAMPLE_DATA]; - + // Used to determine whether we dropped bad data, to avoid doing // it repeatedly. final boolean[] mDroppedBadPoint = new boolean[MAX_POINTERS]; // Used to count the number of jumpy points dropped. private int mJumpyPointsDropped = 0; - + // Used to perform averaging of reported coordinates, to smooth // the data and filter out transients during a release. static final int HISTORY_SIZE = 5; @@ -106,19 +111,19 @@ public class InputDevice { final int[] mHistoryData = new int[(MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS) * HISTORY_SIZE]; final int[] mAveragedData = new int[MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS]; - + // Temporary data structures for doing the pointer ID mapping. final int[] mLast2Next = new int[MAX_POINTERS]; final int[] mNext2Last = new int[MAX_POINTERS]; final long[] mNext2LastDistance = new long[MAX_POINTERS]; - + // Temporary data structure for generating the final motion data. final float[] mReportData = new float[MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS]; - + // This is not used here, but can be used by callers for state tracking. int mAddingPointerOffset = 0; final boolean[] mDown = new boolean[MAX_POINTERS]; - + void dumpIntArray(PrintWriter pw, int[] array) { pw.print("["); for (int i=0; i<array.length; i++) { @@ -127,7 +132,7 @@ public class InputDevice { } pw.print("]"); } - + void dumpBooleanArray(PrintWriter pw, boolean[] array) { pw.print("["); for (int i=0; i<array.length; i++) { @@ -136,7 +141,7 @@ public class InputDevice { } pw.print("]"); } - + void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print("xPrecision="); pw.print(xPrecision); pw.print(" yPrecision="); pw.println(yPrecision); @@ -168,7 +173,7 @@ public class InputDevice { pw.print(prefix); pw.print("mDown="); dumpBooleanArray(pw, mDown); pw.println(""); } - + MotionState(int mx, int my) { xPrecision = mx; yPrecision = my; @@ -178,7 +183,7 @@ public class InputDevice { mPointerIds[i] = i; } } - + /** * Special hack for devices that have bad screen data: if one of the * points has moved more than a screen height from the last position, @@ -195,13 +200,13 @@ public class InputDevice { if (mNextNumPointers != mLastNumPointers) { return; } - + // We consider a single movement across more than a 7/16 of // the long size of the screen to be bad. This was a magic value // determined by looking at the maximum distance it is feasible // to actually move in one sample. final int maxDy = ((dev.absY.maxValue-dev.absY.minValue)*7)/16; - + // Look through all new points and see if any are farther than // acceptable from all previous points. for (int i=mNextNumPointers-1; i>=0; i--) { @@ -244,31 +249,31 @@ public class InputDevice { mDroppedBadPoint[i] = dropped; } } - + void dropJumpyPoint(InputDevice dev) { // We should always have absY, but let's be paranoid. if (dev.absY == null) { return; } final int jumpyEpsilon = dev.absY.range / JUMPY_EPSILON_DIVISOR; - + final int nextNumPointers = mNextNumPointers; final int lastNumPointers = mLastNumPointers; final int[] nextData = mNextData; final int[] lastData = mLastData; - + if (nextNumPointers != mLastNumPointers) { if (DEBUG_HACKS) { - Slog.d("InputDevice", "Different pointer count " + lastNumPointers + + Slog.d("InputDevice", "Different pointer count " + lastNumPointers + " -> " + nextNumPointers); for (int i = 0; i < nextNumPointers; i++) { int ioff = i * MotionEvent.NUM_SAMPLE_DATA; - Slog.d("InputDevice", "Pointer " + i + " (" + + Slog.d("InputDevice", "Pointer " + i + " (" + mNextData[ioff + MotionEvent.SAMPLE_X] + ", " + mNextData[ioff + MotionEvent.SAMPLE_Y] + ")"); } } - + // Just drop the first few events going from 1 to 2 pointers. // They're bad often enough that they're not worth considering. if (lastNumPointers == 1 && nextNumPointers == 2 @@ -278,36 +283,36 @@ public class InputDevice { } else if (lastNumPointers == 2 && nextNumPointers == 1 && mJumpyPointsDropped < JUMPY_TRANSITION_DROPS) { // The event when we go from 2 -> 1 tends to be messed up too - System.arraycopy(lastData, 0, nextData, 0, + System.arraycopy(lastData, 0, nextData, 0, lastNumPointers * MotionEvent.NUM_SAMPLE_DATA); mNextNumPointers = lastNumPointers; mJumpyPointsDropped++; - + if (DEBUG_HACKS) { for (int i = 0; i < mNextNumPointers; i++) { int ioff = i * MotionEvent.NUM_SAMPLE_DATA; - Slog.d("InputDevice", "Pointer " + i + " replaced (" + + Slog.d("InputDevice", "Pointer " + i + " replaced (" + mNextData[ioff + MotionEvent.SAMPLE_X] + ", " + mNextData[ioff + MotionEvent.SAMPLE_Y] + ")"); } } } else { mJumpyPointsDropped = 0; - + if (DEBUG_HACKS) { Slog.d("InputDevice", "Transition - drop limit reset"); } } return; } - + // A 'jumpy' point is one where the coordinate value for one axis // has jumped to the other pointer's location. No need to do anything // else if we only have one pointer. if (nextNumPointers < 2) { return; } - + int badPointerIndex = -1; int badPointerReplaceXWith = 0; int badPointerReplaceYWith = 0; @@ -315,13 +320,13 @@ public class InputDevice { for (int i = nextNumPointers - 1; i >= 0; i--) { boolean dropx = false; boolean dropy = false; - + // Limit how many times a jumpy point can get dropped. if (mJumpyPointsDropped < JUMPY_DROP_LIMIT) { final int ioff = i * MotionEvent.NUM_SAMPLE_DATA; final int x = nextData[ioff + MotionEvent.SAMPLE_X]; final int y = nextData[ioff + MotionEvent.SAMPLE_Y]; - + if (DEBUG_HACKS) { Slog.d("InputDevice", "Point " + i + " (" + x + ", " + y + ")"); } @@ -339,14 +344,14 @@ public class InputDevice { dropx = Math.abs(x - xOther) <= jumpyEpsilon; dropy = Math.abs(y - yOther) <= jumpyEpsilon; } - + if (dropx) { int xreplace = lastData[MotionEvent.SAMPLE_X]; int yreplace = lastData[MotionEvent.SAMPLE_Y]; int distance = Math.abs(yreplace - y); for (int j = 1; j < lastNumPointers; j++) { final int joff = j * MotionEvent.NUM_SAMPLE_DATA; - int lasty = lastData[joff + MotionEvent.SAMPLE_Y]; + int lasty = lastData[joff + MotionEvent.SAMPLE_Y]; int currDist = Math.abs(lasty - y); if (currDist < distance) { xreplace = lastData[joff + MotionEvent.SAMPLE_X]; @@ -354,7 +359,7 @@ public class InputDevice { distance = currDist; } } - + int badXDelta = Math.abs(xreplace - x); if (badXDelta > badPointerDistance) { badPointerDistance = badXDelta; @@ -368,7 +373,7 @@ public class InputDevice { int distance = Math.abs(xreplace - x); for (int j = 1; j < lastNumPointers; j++) { final int joff = j * MotionEvent.NUM_SAMPLE_DATA; - int lastx = lastData[joff + MotionEvent.SAMPLE_X]; + int lastx = lastData[joff + MotionEvent.SAMPLE_X]; int currDist = Math.abs(lastx - x); if (currDist < distance) { xreplace = lastx; @@ -376,7 +381,7 @@ public class InputDevice { distance = currDist; } } - + int badYDelta = Math.abs(yreplace - y); if (badYDelta > badPointerDistance) { badPointerDistance = badYDelta; @@ -402,7 +407,7 @@ public class InputDevice { mJumpyPointsDropped = 0; } } - + /** * Special hack for devices that have bad screen data: aggregate and * compute averages of the coordinate data, to reduce the amount of @@ -484,7 +489,7 @@ public class InputDevice { } } } - + // Now compute the average. int start = mHistoryDataStart[i]; int end = mHistoryDataEnd[i]; @@ -519,7 +524,7 @@ public class InputDevice { } return mAveragedData; } - + private boolean assignPointer(int nextIndex, boolean allowOverlap) { final int lastNumPointers = mLastNumPointers; final int[] next2Last = mNext2Last; @@ -528,12 +533,12 @@ public class InputDevice { final int[] lastData = mLastData; final int[] nextData = mNextData; final int id = nextIndex * MotionEvent.NUM_SAMPLE_DATA; - + if (DEBUG_POINTERS) Slog.v("InputDevice", "assignPointer: nextIndex=" + nextIndex + " dataOff=" + id); final int x1 = nextData[id + MotionEvent.SAMPLE_X]; final int y1 = nextData[id + MotionEvent.SAMPLE_Y]; - + long bestDistance = -1; int bestIndex = -1; for (int j=0; j<lastNumPointers; j++) { @@ -552,51 +557,51 @@ public class InputDevice { bestIndex = j; } } - + if (DEBUG_POINTERS) Slog.v("InputDevice", "New index " + nextIndex + " best old index=" + bestIndex + " (distance=" + bestDistance + ")"); next2Last[nextIndex] = bestIndex; next2LastDistance[nextIndex] = bestDistance; - + if (bestIndex < 0) { return true; } - + if (last2Next[bestIndex] == -1) { last2Next[bestIndex] = nextIndex; return false; } - + if (DEBUG_POINTERS) Slog.v("InputDevice", "Old index " + bestIndex + " has multiple best new pointers!"); - + last2Next[bestIndex] = -2; return true; } - + private int updatePointerIdentifiers() { final int[] lastData = mLastData; final int[] nextData = mNextData; final int nextNumPointers = mNextNumPointers; final int lastNumPointers = mLastNumPointers; - + if (nextNumPointers == 1 && lastNumPointers == 1) { System.arraycopy(nextData, 0, lastData, 0, MotionEvent.NUM_SAMPLE_DATA); return -1; } - + // Clear our old state. final int[] last2Next = mLast2Next; for (int i=0; i<lastNumPointers; i++) { last2Next[i] = -1; } - + if (DEBUG_POINTERS) Slog.v("InputDevice", "Update pointers: lastNumPointers=" + lastNumPointers + " nextNumPointers=" + nextNumPointers); - + // Figure out the closes new points to the previous points. final int[] next2Last = mNext2Last; final long[] next2LastDistance = mNext2LastDistance; @@ -604,25 +609,25 @@ public class InputDevice { for (int i=0; i<nextNumPointers; i++) { conflicts |= assignPointer(i, true); } - + // Resolve ambiguities in pointer mappings, when two or more // new pointer locations find their best previous location is // the same. if (conflicts) { if (DEBUG_POINTERS) Slog.v("InputDevice", "Resolving conflicts"); - + for (int i=0; i<lastNumPointers; i++) { if (last2Next[i] != -2) { continue; } - + // Note that this algorithm is far from perfect. Ideally // we should do something like the one described at // http://portal.acm.org/citation.cfm?id=997856 - + if (DEBUG_POINTERS) Slog.v("InputDevice", "Resolving last index #" + i); - + int numFound; do { numFound = 0; @@ -638,7 +643,7 @@ public class InputDevice { worstJ = j; } } - + if (worstJ >= 0) { if (DEBUG_POINTERS) Slog.v("InputDevice", "Worst new pointer: " + worstJ @@ -652,9 +657,9 @@ public class InputDevice { } while (numFound > 2); } } - + int retIndex = -1; - + if (lastNumPointers < nextNumPointers) { // We have one or more new pointers that are down. Create a // new pointer identifier for one of them. @@ -678,14 +683,14 @@ public class InputDevice { i++; nextId++; } - + if (DEBUG_POINTERS) Slog.v("InputDevice", "New pointer id " + nextId + " at index " + i); - + mLastNumPointers++; retIndex = i; mPointerIds[i] = nextId; - + // And assign this identifier to the first new pointer. for (int j=0; j<nextNumPointers; j++) { if (next2Last[j] < 0) { @@ -696,7 +701,7 @@ public class InputDevice { } } } - + // Propagate all of the current data into the appropriate // location in the old data to match the pointer ID that was // assigned to it. @@ -711,7 +716,7 @@ public class InputDevice { MotionEvent.NUM_SAMPLE_DATA); } } - + if (lastNumPointers > nextNumPointers) { // One or more pointers has gone up. Find the first one, // and adjust accordingly. @@ -725,10 +730,10 @@ public class InputDevice { } } } - + return retIndex; } - + void removeOldPointer(int index) { final int lastNumPointers = mLastNumPointers; if (index >= 0 && index < lastNumPointers) { @@ -740,20 +745,20 @@ public class InputDevice { mLastNumPointers--; } } - + MotionEvent generateAbsMotion(InputDevice device, long curTime, long curTimeNano, Display display, int orientation, int metaState) { - + boolean isMouse = (device.classes & RawInputEvent.CLASS_MOUSE) != 0; if (mSkipLastPointers) { mSkipLastPointers = false; mLastNumPointers = 0; } - - if (mNextNumPointers <= 0 && mLastNumPointers <= 0) { + + if (!isMouse && (mNextNumPointers <= 0 && mLastNumPointers <= 0)) { return null; } - + final int lastNumPointers = mLastNumPointers; final int nextNumPointers = mNextNumPointers; if (mNextNumPointers > MAX_POINTERS) { @@ -761,24 +766,28 @@ public class InputDevice { + " exceeded maximum of " + MAX_POINTERS); mNextNumPointers = MAX_POINTERS; } - - int upOrDownPointer = updatePointerIdentifiers(); - + + /* + * This is not used for mouse + */ + int upOrDownPointer = isMouse ? 0 : updatePointerIdentifiers(); + final float[] reportData = mReportData; final int[] rawData; - if (KeyInputQueue.BAD_TOUCH_HACK) { + if (!isMouse && KeyInputQueue.BAD_TOUCH_HACK) { rawData = generateAveragedData(upOrDownPointer, lastNumPointers, nextNumPointers); } else { - rawData = mLastData; + rawData = isMouse ? mNextData : mLastData; } - - final int numPointers = mLastNumPointers; - - if (DEBUG_POINTERS) Slog.v("InputDevice", "Processing " + + final int numPointers = isMouse ? 1 : mLastNumPointers; + + if (DEBUG_POINTERS || DEBUG_MOUSE) + Slog.v("InputDevice", "Processing " + numPointers + " pointers (going from " + lastNumPointers - + " to " + nextNumPointers + ")"); - + + " to " + nextNumPointers + ")" + " touch hack " + + KeyInputQueue.BAD_TOUCH_HACK); for (int i=0; i<numPointers; i++) { final int pos = i * MotionEvent.NUM_SAMPLE_DATA; reportData[pos + MotionEvent.SAMPLE_X] = rawData[pos + MotionEvent.SAMPLE_X]; @@ -786,31 +795,50 @@ public class InputDevice { reportData[pos + MotionEvent.SAMPLE_PRESSURE] = rawData[pos + MotionEvent.SAMPLE_PRESSURE]; reportData[pos + MotionEvent.SAMPLE_SIZE] = rawData[pos + MotionEvent.SAMPLE_SIZE]; } - + int action; int edgeFlags = 0; - if (nextNumPointers != lastNumPointers) { - if (nextNumPointers > lastNumPointers) { - if (lastNumPointers == 0) { - action = MotionEvent.ACTION_DOWN; - mDownTime = curTime; + if (!isMouse) { + if (nextNumPointers != lastNumPointers) { + if (nextNumPointers > lastNumPointers) { + if (lastNumPointers == 0) { + action = MotionEvent.ACTION_DOWN; + mDownTime = curTime; + } else { + action = MotionEvent.ACTION_POINTER_DOWN + | (upOrDownPointer << MotionEvent.ACTION_POINTER_ID_SHIFT); + } } else { - action = MotionEvent.ACTION_POINTER_DOWN + if (numPointers == 1) { + action = MotionEvent.ACTION_UP; + } else { + action = MotionEvent.ACTION_POINTER_UP | (upOrDownPointer << MotionEvent.ACTION_POINTER_INDEX_SHIFT); + } } + currentMove = null; } else { - if (numPointers == 1) { + action = MotionEvent.ACTION_MOVE; + } + } else { + if (mNextNumPointers != mLastNumPointers) { + if (mNextNumPointers == 1) { + action = MotionEvent.ACTION_DOWN; + mDownTime = curTime; + } else if (mNextNumPointers == 2) { action = MotionEvent.ACTION_UP; } else { - action = MotionEvent.ACTION_POINTER_UP - | (upOrDownPointer << MotionEvent.ACTION_POINTER_INDEX_SHIFT); + action = MotionEvent.ACTION_MOVE; } + mLastNumPointers = mNextNumPointers; + currentMove = null; + } else { + action = MotionEvent.ACTION_MOVE; } - currentMove = null; - } else { - action = MotionEvent.ACTION_MOVE; + if (DEBUG_MOUSE) + Slog.i(TAG, "mouse action " + action); } - + final int dispW = display.getWidth()-1; final int dispH = display.getHeight()-1; int w = dispW; @@ -821,14 +849,14 @@ public class InputDevice { w = h; h = tmp; } - + final AbsoluteInfo absX = device.absX; final AbsoluteInfo absY = device.absY; final AbsoluteInfo absPressure = device.absPressure; final AbsoluteInfo absSize = device.absSize; for (int i=0; i<numPointers; i++) { final int j = i * MotionEvent.NUM_SAMPLE_DATA; - + if (absX != null) { reportData[j + MotionEvent.SAMPLE_X] = ((reportData[j + MotionEvent.SAMPLE_X]-absX.minValue) @@ -840,16 +868,16 @@ public class InputDevice { / absY.range) * h; } if (absPressure != null) { - reportData[j + MotionEvent.SAMPLE_PRESSURE] = + reportData[j + MotionEvent.SAMPLE_PRESSURE] = ((reportData[j + MotionEvent.SAMPLE_PRESSURE]-absPressure.minValue) / (float)absPressure.range); } if (absSize != null) { - reportData[j + MotionEvent.SAMPLE_SIZE] = + reportData[j + MotionEvent.SAMPLE_SIZE] = ((reportData[j + MotionEvent.SAMPLE_SIZE]-absSize.minValue) / (float)absSize.range); } - + switch (orientation) { case Surface.ROTATION_90: { final float temp = reportData[j + MotionEvent.SAMPLE_X]; @@ -870,7 +898,7 @@ public class InputDevice { } } } - + // We only consider the first pointer when computing the edge // flags, since they are global to the event. if (action == MotionEvent.ACTION_DOWN) { @@ -885,9 +913,9 @@ public class InputDevice { edgeFlags |= MotionEvent.EDGE_BOTTOM; } } - + if (currentMove != null) { - if (false) Slog.i("InputDevice", "Adding batch x=" + if (DEBUG_MOUSE) Slog.i("InputDevice", "Adding batch x=" + reportData[MotionEvent.SAMPLE_X] + " y=" + reportData[MotionEvent.SAMPLE_Y] + " to " + currentMove); @@ -897,42 +925,42 @@ public class InputDevice { } return null; } - + MotionEvent me = MotionEvent.obtainNano(mDownTime, curTime, curTimeNano, action, numPointers, mPointerIds, reportData, metaState, xPrecision, yPrecision, device.id, edgeFlags); if (action == MotionEvent.ACTION_MOVE) { currentMove = me; } - - if (nextNumPointers < lastNumPointers) { + + if ((!isMouse) && (nextNumPointers < lastNumPointers)) { removeOldPointer(upOrDownPointer); } - + return me; } - + boolean hasMore() { return mLastNumPointers != mNextNumPointers; } - + void finish() { mNextNumPointers = mAddingPointerOffset = 0; mNextData[MotionEvent.SAMPLE_PRESSURE] = 0; } - + MotionEvent generateRelMotion(InputDevice device, long curTime, long curTimeNano, int orientation, int metaState) { - + final float[] scaled = mReportData; - + // For now we only support 1 pointer with relative motions. scaled[MotionEvent.SAMPLE_X] = mNextData[MotionEvent.SAMPLE_X]; scaled[MotionEvent.SAMPLE_Y] = mNextData[MotionEvent.SAMPLE_Y]; scaled[MotionEvent.SAMPLE_PRESSURE] = 1.0f; scaled[MotionEvent.SAMPLE_SIZE] = 0; int edgeFlags = 0; - + int action; if (mNextNumPointers != mLastNumPointers) { mNextData[MotionEvent.SAMPLE_X] = @@ -950,7 +978,7 @@ public class InputDevice { } else { action = MotionEvent.ACTION_MOVE; } - + scaled[MotionEvent.SAMPLE_X] *= xMoveScale; scaled[MotionEvent.SAMPLE_Y] *= yMoveScale; switch (orientation) { @@ -972,7 +1000,7 @@ public class InputDevice { break; } } - + if (currentMove != null) { if (false) Slog.i("InputDevice", "Adding batch x=" + scaled[MotionEvent.SAMPLE_X] @@ -984,7 +1012,7 @@ public class InputDevice { } return null; } - + MotionEvent me = MotionEvent.obtainNano(mDownTime, curTime, curTimeNano, action, 1, mPointerIds, scaled, metaState, xPrecision, yPrecision, device.id, edgeFlags); @@ -994,14 +1022,14 @@ public class InputDevice { return me; } } - + static class AbsoluteInfo { int minValue; int maxValue; int range; int flat; int fuzz; - + final void dump(PrintWriter pw) { pw.print("minValue="); pw.print(minValue); pw.print(" maxValue="); pw.print(maxValue); @@ -1010,7 +1038,7 @@ public class InputDevice { pw.print(" fuzz="); pw.print(fuzz); } }; - + InputDevice(int _id, int _classes, String _name, AbsoluteInfo _absX, AbsoluteInfo _absY, AbsoluteInfo _absPressure, AbsoluteInfo _absSize) { diff --git a/services/java/com/android/server/KeyInputQueue.java b/services/java/com/android/server/KeyInputQueue.java index f30346b..5d0d127 100644 --- a/services/java/com/android/server/KeyInputQueue.java +++ b/services/java/com/android/server/KeyInputQueue.java @@ -50,10 +50,13 @@ import java.util.ArrayList; public abstract class KeyInputQueue { static final String TAG = "KeyInputQueue"; + static final int UPKEY_KEYWORD = 19; + static final int DOWNKEY_KEYWORD = 20; static final boolean DEBUG = false; static final boolean DEBUG_VIRTUAL_KEYS = false; static final boolean DEBUG_POINTERS = false; + static final boolean DEBUG_MOUSE = false; /** * Turn on some hacks we have to improve the touch interaction with a @@ -85,6 +88,8 @@ public abstract class KeyInputQueue { Display mDisplay = null; int mDisplayWidth; int mDisplayHeight; + int mCx; + int mCy; int mOrientation = Surface.ROTATION_0; int[] mKeyRotationMap = null; @@ -322,6 +327,8 @@ public abstract class KeyInputQueue { // buttons based on that display. mDisplayWidth = display.getWidth(); mDisplayHeight = display.getHeight(); + mCx = mDisplayWidth / 2; + mCy = mDisplayHeight / 2; } public void getInputConfiguration(Configuration config) { @@ -639,11 +646,35 @@ public abstract class KeyInputQueue { di.mAbs.mDown[0] = ev.value != 0; // Trackball (mouse) protocol: press down or up. - } else if (ev.scancode == RawInputEvent.BTN_MOUSE && - (classes&RawInputEvent.CLASS_TRACKBALL) != 0) { - di.mRel.changed = true; - di.mRel.mNextNumPointers = ev.value != 0 ? 1 : 0; - send = true; + } else if (ev.scancode == RawInputEvent.BTN_MOUSE) { + if ((classes&RawInputEvent.CLASS_TRACKBALL) != 0) { + di.mRel.changed = true; + di.mRel.mNextNumPointers = ev.value != 0 ? 1 : 0; + send = true; + } else if ((classes&RawInputEvent.CLASS_MOUSE) != 0) { + if (DEBUG_MOUSE) + Slog.i(TAG, "Mouse key event found, down:" + + ev.value + " was :" + + di.mAbs.mDown[0] + " Send " + send); + di.mAbs.changed = true; + di.mAbs.mNextNumPointers = (ev.value != 0) ? 1 : 2; + send = true; + } + } else if ((ev.scancode == RawInputEvent.BTN_RIGHT || + ev.scancode == RawInputEvent.BTN_MIDDLE) && + (classes&RawInputEvent.CLASS_MOUSE) != 0) { + boolean down = (ev.value != 0); + if (down) + di.mKeyDownTime = curTime; + + addLocked(di, curTime, ev.flags, + RawInputEvent.CLASS_KEYBOARD, + newKeyEvent(di, di.mKeyDownTime, curTime, down, + (ev.scancode == RawInputEvent.BTN_RIGHT) + ? KeyEvent.KEYCODE_BACK : KeyEvent.KEYCODE_MENU, + 0, scancode, + ((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0) + ? KeyEvent.FLAG_WOKE_HERE : 0)); } // Process position events from multitouch protocol. @@ -695,15 +726,59 @@ public abstract class KeyInputQueue { } // Process movement events from trackball (mouse) protocol. - } else if (ev.type == RawInputEvent.EV_REL && - (classes&RawInputEvent.CLASS_TRACKBALL) != 0) { - // Add this relative movement into our totals. - if (ev.scancode == RawInputEvent.REL_X) { - di.mRel.changed = true; - di.mRel.mNextData[MotionEvent.SAMPLE_X] += ev.value; - } else if (ev.scancode == RawInputEvent.REL_Y) { - di.mRel.changed = true; - di.mRel.mNextData[MotionEvent.SAMPLE_Y] += ev.value; + } else if (ev.type == RawInputEvent.EV_REL) { + if (DEBUG_MOUSE) + Slog.i(TAG, "rel event found, class :" + classes + " mouse value: " + RawInputEvent.CLASS_MOUSE); + if ((classes&RawInputEvent.CLASS_TRACKBALL) != 0) { + // Add this relative movement into our totals. + if (ev.scancode == RawInputEvent.REL_X) { + di.mRel.changed = true; + di.mRel.mNextData[MotionEvent.SAMPLE_X] += ev.value; + } else if (ev.scancode == RawInputEvent.REL_Y) { + di.mRel.changed = true; + di.mRel.mNextData[MotionEvent.SAMPLE_Y] += ev.value; + } + } else if ((classes&RawInputEvent.CLASS_MOUSE) != 0) { + if (ev.scancode == RawInputEvent.REL_X) { + di.mAbs.changed = true; + mCx += (int)ev.value; + mCx = ((mCx < 0) ? 0 : (mCx >= mDisplayWidth ? mDisplayWidth - 1 : mCx)); + di.mAbs.mNextData[MotionEvent.SAMPLE_X] = mCx; + } else if (ev.scancode == RawInputEvent.REL_Y) { + di.mAbs.changed = true; + mCy += (int)ev.value; + mCy = ((mCy < 0) ? 0 : (mCy >= mDisplayHeight ? mDisplayHeight - 1 : mCy)); + di.mAbs.mNextData[MotionEvent.SAMPLE_Y] = mCy; + } else if (ev.scancode == RawInputEvent.REL_WHEEL && + (classes&RawInputEvent.CLASS_MOUSE) != 0) { + boolean down; + int keycode; + if (ev.value != 0) { + down = true; + di.mKeyDownTime = curTime; + } else { + down = false; + } + if (ev.value < 0){ + keycode = rotateKeyCodeLocked(DOWNKEY_KEYWORD); + } else if(ev.value > 0){ + keycode = rotateKeyCodeLocked(UPKEY_KEYWORD); + } else { + keycode = rotateKeyCodeLocked(ev.keycode); + } + addLocked(di, curTime, ev.flags, + RawInputEvent.CLASS_KEYBOARD, + newKeyEvent(di, di.mKeyDownTime, curTime, down, + keycode, 0, scancode, + ((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0) + ? KeyEvent.FLAG_WOKE_HERE : 0)); + addLocked(di, curTime, ev.flags, + RawInputEvent.CLASS_KEYBOARD, + newKeyEvent(di, di.mKeyDownTime, curTime, !down, + keycode, 0, scancode, + ((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0) + ? KeyEvent.FLAG_WOKE_HERE : 0)); + } } } @@ -790,7 +865,8 @@ public abstract class KeyInputQueue { me = ms.generateAbsMotion(di, curTime, curTimeNano, mDisplay, mOrientation, mGlobalMetaState); - if (DEBUG_POINTERS) Slog.v(TAG, "Absolute: x=" + if (DEBUG_POINTERS || DEBUG_MOUSE) + Slog.v(TAG, "Absolute: x=" + di.mAbs.mNextData[MotionEvent.SAMPLE_X] + " y=" + di.mAbs.mNextData[MotionEvent.SAMPLE_Y] @@ -799,8 +875,15 @@ public abstract class KeyInputQueue { if (WindowManagerPolicy.WATCH_POINTER) { Slog.i(TAG, "Enqueueing: " + me); } - addLocked(di, curTimeNano, ev.flags, - RawInputEvent.CLASS_TOUCHSCREEN, me); + if ((classes & RawInputEvent.CLASS_TOUCHSCREEN) != 0) { + addLocked(di, curTime, ev.flags, + RawInputEvent.CLASS_TOUCHSCREEN, me); + } else if ((classes & RawInputEvent.CLASS_MOUSE) != 0) { + addLocked(di, curTime, ev.flags, + RawInputEvent.CLASS_MOUSE, me); + } else { + Slog.w(TAG, "Unknown classes? " + classes); + } } } while (ms.hasMore()); } else { diff --git a/services/java/com/android/server/LightsService.java b/services/java/com/android/server/LightsService.java index 9b57735..4fd2eb3 100644 --- a/services/java/com/android/server/LightsService.java +++ b/services/java/com/android/server/LightsService.java @@ -23,6 +23,7 @@ import android.os.IHardwareService; import android.os.ServiceManager; import android.os.Message; import android.util.Slog; +import android.util.Log; import java.io.File; import java.io.FileInputStream; @@ -94,13 +95,20 @@ public class LightsService { public void pulse(int color, int onMS) { synchronized (this) { - if (mColor == 0 && !mFlashing) { + if (mColor == 0 && !mFlashing) { setLightLocked(color, LIGHT_FLASH_HARDWARE, onMS, 1000, BRIGHTNESS_MODE_USER); mH.sendMessageDelayed(Message.obtain(mH, 1, this), onMS); } } } + public void notificationPulse(int color, int onMs, int offMs) { + synchronized (this) { + setLightLocked(color, LIGHT_FLASH_TIMED, onMs, 1000, BRIGHTNESS_MODE_USER); + mH.sendMessageDelayed(Message.obtain(mH, 1, this), onMs); + } + } + public void turnOff() { synchronized (this) { setLightLocked(0, LIGHT_FLASH_NONE, 0, 0, 0); @@ -114,13 +122,13 @@ public class LightsService { } private void setLightLocked(int color, int mode, int onMS, int offMS, int brightnessMode) { - if (color != mColor || mode != mMode || onMS != mOnMS || offMS != mOffMS) { - mColor = color; - mMode = mode; - mOnMS = onMS; - mOffMS = offMS; - setLight_native(mNativePointer, mId, color, mode, onMS, offMS, brightnessMode); - } + if (color != mColor || mode != mMode || onMS != mOnMS || offMS != mOffMS) { + mColor = color; + mMode = mode; + mOnMS = onMS; + mOffMS = offMS; + setLight_native(mNativePointer, mId, color, mode, onMS, offMS, brightnessMode); + } } private int mId; diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index 73d17ea..675c4b1 100755 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -19,7 +19,6 @@ package com.android.server; import com.android.server.status.IconData; import com.android.server.status.NotificationData; import com.android.server.status.StatusBarService; - import android.app.ActivityManagerNative; import android.app.IActivityManager; import android.app.INotificationManager; @@ -43,6 +42,7 @@ import android.media.AudioManager; import android.net.Uri; import android.os.BatteryManager; import android.os.Binder; +import android.os.Environment; import android.os.Handler; import android.os.IBinder; import android.os.Message; @@ -57,14 +57,27 @@ import android.text.TextUtils; import android.util.EventLog; import android.util.Slog; import android.util.Log; +import java.util.Timer; +import java.util.TimerTask; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.widget.Toast; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import android.os.PowerManager; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileWriter; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Random; + +//For Notification Colors. +import android.graphics.Color; class NotificationManagerService extends INotificationManager.Stub { @@ -106,6 +119,7 @@ class NotificationManagerService extends INotificationManager.Stub // for enabling and disabling notification pulse behavior private boolean mScreenOn = true; + private boolean mScreenUpdate = false; private boolean mInCall = false; private boolean mNotificationPulseEnabled; @@ -125,6 +139,7 @@ class NotificationManagerService extends INotificationManager.Stub private boolean mBatteryCharging; private boolean mBatteryLow; private boolean mBatteryFull; + private int mBatteryLevel; private NotificationRecord mLedNotification; private static final int BATTERY_LOW_ARGB = 0xFFFF0000; // Charging Low - red solid on @@ -133,6 +148,14 @@ class NotificationManagerService extends INotificationManager.Stub private static final int BATTERY_BLINK_ON = 125; private static final int BATTERY_BLINK_OFF = 2875; + private static ExecutorService threadExecutor; + private static int mLastLight = 0; + private static boolean isTimer = false; + private static boolean hasLights = false; + private static PowerManager PowerMan; + private static PowerManager.WakeLock powerWake = null; + //private static ExecutorService threadExecutor; + private static String idDebugString(Context baseContext, String packageName, int id) { Context c = null; @@ -317,7 +340,9 @@ class NotificationManagerService extends INotificationManager.Stub boolean batteryLow = (level >= 0 && level <= Power.LOW_BATTERY_THRESHOLD); int status = intent.getIntExtra("status", BatteryManager.BATTERY_STATUS_UNKNOWN); boolean batteryFull = (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90); + int percentage = intent.getIntExtra("scale", 100); + mBatteryLevel = level*100/percentage; if (batteryCharging != mBatteryCharging || batteryLow != mBatteryLow || batteryFull != mBatteryFull) { @@ -359,10 +384,18 @@ class NotificationManagerService extends INotificationManager.Stub } } else if (action.equals(Intent.ACTION_SCREEN_ON)) { mScreenOn = true; - updateNotificationPulse(); + mScreenUpdate = true; + int mPulseScreen = Settings.System.getInt(mContext.getContentResolver(), Settings.System.TRACKBALL_SCREEN_ON, 0); + if(mPulseScreen == 0) { //Why bother if we are going to pulse anyways? + updateNotificationPulse(); + } } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { mScreenOn = false; - updateNotificationPulse(); + mScreenUpdate = true; + int mPulseScreen = Settings.System.getInt(mContext.getContentResolver(), Settings.System.TRACKBALL_SCREEN_ON, 0); + if(mPulseScreen == 0) { //Why bother if we are going to pulse anyways? + updateNotificationPulse(); + } } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) { mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_OFFHOOK)); updateNotificationPulse(); @@ -824,6 +857,7 @@ class NotificationManagerService extends INotificationManager.Stub if (mLedNotification == old) { mLedNotification = null; } + //updatePackageList(pkg); //Slog.i(TAG, "notification.lights=" // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS) != 0)); if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) { @@ -909,7 +943,7 @@ class NotificationManagerService extends INotificationManager.Stub */ private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags, int mustNotHaveFlags) { - EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, mustHaveFlags); + EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, mustHaveFlags); synchronized (mNotificationList) { int index = indexOfNotificationLocked(pkg, tag, id); @@ -1037,9 +1071,78 @@ class NotificationManagerService extends INotificationManager.Stub } } + public boolean isNull(String mString) { + if(mString == null || mString.matches("null") + || mString.length() == 0 + || mString.matches("|") + || mString.matches("")) { + return true; + } else { + return false; + } + } + + //Grab our Array Information For Lights + public String[] getArray(String mGetFrom) { + if(isNull(mGetFrom)) { + return null; + } + String[] temp = mGetFrom.split("\\|"); + return temp; + } + + public String[] getPackageInfo(String mString) { + if(mString == null || mString == "=" || mString.length() == 0) { + return null; + } + String[] temp; + temp = mString.split("="); + return temp; + } + + public class StartTimerClass implements Runnable { + private long sleepTimer; + + public StartTimerClass(long timer) { + sleepTimer = timer; + } + public void run() { + powerWake.acquire(sleepTimer); + try { + Thread.sleep(sleepTimer); + } catch (InterruptedException e) { + } + isTimer = false; + updateLights(); + //threadExecutor = null; + } + } + + private int lastColor = 1; + private String[] colorList = {"green", "white", "red", "blue", "yellow", "cyan", "#800080", "#ffc0cb", "#ffa500", "#add8e6"}; + + public void newExecutor() { + threadExecutor = Executors.newSingleThreadExecutor(); + } + // lock on mNotificationList private void updateLightsLocked() { + String[] mPackages; + String mPackageList = Settings.System.getString(mContext.getContentResolver(), Settings.System.NOTIFICATION_PACKAGE_COLORS); + mPackages = getArray(mPackageList); + int mPulseScreen = Settings.System.getInt(mContext.getContentResolver(), Settings.System.TRACKBALL_SCREEN_ON, 0); + int mSuccession = Settings.System.getInt(mContext.getContentResolver(), Settings.System.TRACKBALL_NOTIFICATION_SUCCESSION, 0); + int mRandomColor = Settings.System.getInt(mContext.getContentResolver(), Settings.System.TRACKBALL_NOTIFICATION_RANDOM, 0); + int mPulseAllColor = Settings.System.getInt(mContext.getContentResolver(), Settings.System.TRACKBALL_NOTIFICATION_PULSE_ORDER, 0); + int mBlendColor = Settings.System.getInt(mContext.getContentResolver(), Settings.System.TRACKBALL_NOTIFICATION_BLEND_COLOR, 0); + + if(mBatteryLevel <= 15 || (mBlendColor == 1)) { + mSuccession = 0; + mRandomColor = 0; + mPulseAllColor = 0; + } + // Battery low always shows, other states only show if charging. if (mBatteryLow) { if (mBatteryCharging) { @@ -1066,29 +1169,147 @@ class NotificationManagerService extends INotificationManager.Stub if (n > 0) { mLedNotification = mLights.get(n-1); } - } + } else if (mSuccession != 1) { + //We always want to have the newst notification to pulse without Succession + int n = mLights.size(); + if (n > 0) { + mLedNotification = mLights.get(n-1); + } + } // we only flash if screen is off and persistent pulsing is enabled // and we are not currently in a call - if (mLedNotification == null || mScreenOn || mInCall) { - mNotificationLight.turnOff(); + if (mLedNotification == null || (mScreenOn && (mPulseScreen == 0)) || mInCall) { + mNotificationLight.turnOff(); + hasLights = false; + isTimer = false; } else { - int ledARGB = mLedNotification.notification.ledARGB; - int ledOnMS = mLedNotification.notification.ledOnMS; - int ledOffMS = mLedNotification.notification.ledOffMS; - if ((mLedNotification.notification.defaults & Notification.DEFAULT_LIGHTS) != 0) { - ledARGB = mDefaultNotificationColor; - ledOnMS = mDefaultNotificationLedOn; - ledOffMS = mDefaultNotificationLedOff; - } - if (mNotificationPulseEnabled) { - // pulse repeatedly - mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED, - ledOnMS, ledOffMS); - } else { + hasLights = true; + if(mSuccession == 1) { + int n = mLights.size(); + if (n > 0) { + int thisLight = n; + if(mLastLight > thisLight) { + thisLight = 1; + } else { + thisLight = mLastLight + 1; + } + //One last arthmetic check for sanitys sake. + if((thisLight == (mLastLight - 1)) || (thisLight > n)) { + thisLight = 1; + } + mLedNotification = mLights.get(thisLight-1); + mLastLight = thisLight; + } + } + int ledARGB = mLedNotification.notification.ledARGB; + int ledOnMS = mLedNotification.notification.ledOnMS; + int ledOffMS = mLedNotification.notification.ledOffMS; + if ((mLedNotification.notification.defaults & Notification.DEFAULT_LIGHTS) != 0) { + ledARGB = mDefaultNotificationColor; + ledOnMS = mDefaultNotificationLedOn; + ledOffMS = mDefaultNotificationLedOff; + } + //Possible Cleaner way? + //String[] mPackageInfo = findPackage(mLedNotification.pkg); + //if(mPackageInfo != null) { + // ledARGB = Color.parseColor(mPackageInfo[1]); + // ledOffMS = Integer.parseInt(mPackageInfo[2]); + //} + if(mPackages != null) { + int i = 0; + for(i = 0; i < mPackages.length; i++) { + String[] mPackageInfo = getPackageInfo(mPackages[i]); + if(mPackageInfo == null) { + continue; + } + if(mPackageInfo[0].matches(mLedNotification.pkg)) { + if(mPackageInfo[1].equals("random")) { + Random generator = new Random(); + int x = generator.nextInt(colorList.length - 1); + ledARGB = Color.parseColor(colorList[x]); + } else { + ledARGB = Color.parseColor(mPackageInfo[1]); + } + if(mPackageInfo[2].equals(".5")) { + ledOffMS = 500; + } else { + ledOffMS = (Integer.parseInt(mPackageInfo[2]) * 1000); + } + } + } + } + + if(mRandomColor == 1) { + //Lets make this intresting... + Random generator = new Random(); + int x = generator.nextInt(colorList.length - 1); + ledARGB = Color.parseColor(colorList[x]); + } else if(mPulseAllColor == 1) { + if(lastColor >= colorList.length) + lastColor = 1; + + ledARGB = Color.parseColor(colorList[lastColor - 1]); + lastColor = lastColor + 1; + } else if(mBlendColor == 1) { // Blend lights: Credit to eshabtai for the application of this. + for (int n=0; n < mLights.size(); n++) { + int x = 0; + boolean found = false; + long pkgcolor = Color.parseColor("white"); + for(x = 0; x < mPackages.length; x++) { + String[] mPackageInfo = getPackageInfo(mPackages[x]); + if(mPackageInfo == null) { + continue; + } + if(mPackageInfo[0].matches(mLights.get(n).pkg)) { + if(mPackageInfo[1].equals("random")) { + Random generator = new Random(); + int j = generator.nextInt(colorList.length - 1); + pkgcolor = Color.parseColor(colorList[j]); + } else { + pkgcolor = Color.parseColor(mPackageInfo[1]); + } + found = true; + break; + } + } + if(found) { + ledARGB |= pkgcolor; + } else { + ledARGB |= mLights.get(n).notification.ledARGB; + } + } + } + if (mNotificationPulseEnabled) { + // pulse repeatedly + if((mSuccession == 1) || (mRandomColor == 1) || (mPulseAllColor == 1)) { + /* Our wake lock information to keep us alive */ + if(powerWake == null) { + PowerMan = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + powerWake = PowerMan.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "NotificationLights"); + } + long scheduleTime = ledOnMS+ledOffMS; + if(scheduleTime < 2500) { + scheduleTime = 2500; + } + StartTimerClass timerRun = new StartTimerClass(scheduleTime); + if(threadExecutor == null) { + newExecutor(); + } + if(isTimer == false) { + isTimer = true; + threadExecutor.execute(timerRun); + } + mNotificationLight.notificationPulse(ledARGB, ledOnMS, ledOffMS); + } else { + //We are going to divide the time in half, as it seems long when normal. + mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED, + ledOnMS, ledOffMS); + } + } else { // pulse only once - mNotificationLight.pulse(ledARGB, ledOnMS); - } + mNotificationLight.pulse(ledARGB, ledOnMS); + } } } @@ -1120,7 +1341,9 @@ class NotificationManagerService extends INotificationManager.Stub // to accidentally lose. private void updateAdbNotification() { if (mAdbEnabled && mUsbConnected) { - if ("0".equals(SystemProperties.get("persist.adb.notify"))) { + if ("0".equals(SystemProperties.get("persist.adb.notify")) || + Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.ADB_NOTIFY, 1) == 0) { return; } if (!mAdbNotificationShown) { @@ -1137,7 +1360,6 @@ class NotificationManagerService extends INotificationManager.Stub mAdbNotification = new Notification(); mAdbNotification.icon = com.android.internal.R.drawable.stat_sys_adb; mAdbNotification.when = 0; - mAdbNotification.flags = Notification.FLAG_ONGOING_EVENT; mAdbNotification.tickerText = title; mAdbNotification.defaults = 0; // please be quiet mAdbNotification.sound = null; diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index 0b84c8d..136a042 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -962,13 +962,13 @@ class PackageManagerService extends IPackageManager.Stub { SystemClock.uptimeMillis()); mAppInstallObserver = new AppDirObserver( mAppInstallDir.getPath(), OBSERVER_EVENTS, false); - mAppInstallObserver.startWatching(); scanDirLI(mAppInstallDir, 0, scanMode); + mAppInstallObserver.startWatching(); mDrmAppInstallObserver = new AppDirObserver( mDrmAppPrivateInstallDir.getPath(), OBSERVER_EVENTS, false); - mDrmAppInstallObserver.startWatching(); scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK, scanMode); + mDrmAppInstallObserver.startWatching(); EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END, SystemClock.uptimeMillis()); diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index 7bbc32f..e0d1c49 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -75,6 +75,7 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManager, Watchdog.Monitor { private static final String TAG = "PowerManagerService"; + private static final String TAGF = "LightFilter"; static final String PARTIAL_NAME = "PowerManagerService"; private static final boolean LOG_PARTIAL_WL = false; @@ -238,6 +239,34 @@ class PowerManagerService extends IPowerManager.Stub private int[] mKeyboardBacklightValues; private int mLightSensorWarmupTime; + // Custom light housekeeping + private long mLightSettingsTag = -1; + + // Light sensor levels / values + private boolean mLightDecrease; + private float mLightHysteresis; + private boolean mCustomLightEnabled; + private int[] mCustomLightLevels; + private int[] mCustomLcdValues; + private int[] mCustomButtonValues; + private int[] mCustomKeyboardValues; + private int mLastLcdValue; + private int mLastButtonValue; + private int mLastKeyboardValue; + private int mScreenDim = Power.BRIGHTNESS_DIM; + private boolean mAlwaysOnAndDimmed; + + // Light sensor filter, times in milliseconds + private boolean mLightFilterEnabled; + private boolean mLightFilterRunning; + private int mLightFilterSample = -1; + private int[] mLightFilterSamples; + private int mLightFilterIndex; + private int mLightFilterCounter; + private int mLightFilterWindow; + private int mLightFilterInterval; + private int mLightFilterReset; + // Used when logging number and duration of touch-down cycles private long mTotalTouchDownTime; private long mLastTouchDown; @@ -422,6 +451,8 @@ class PowerManagerService extends IPowerManager.Stub // DIM_SCREEN //mDimScreen = getInt(DIM_SCREEN) != 0; + updateLightSettings(); + // SCREEN_BRIGHTNESS_MODE setScreenBrightnessMode(getInt(SCREEN_BRIGHTNESS_MODE)); @@ -525,9 +556,10 @@ class PowerManagerService extends IPowerManager.Stub "(" + Settings.System.NAME + "=?) or (" + Settings.System.NAME + "=?) or (" + Settings.System.NAME + "=?) or (" + + Settings.System.NAME + "=?) or (" + Settings.System.NAME + "=?)", new String[]{STAY_ON_WHILE_PLUGGED_IN, SCREEN_OFF_TIMEOUT, DIM_SCREEN, - SCREEN_BRIGHTNESS_MODE}, + SCREEN_BRIGHTNESS_MODE, Settings.System.LIGHTS_CHANGED}, null); mSettings = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, mHandler); SettingsObserver settingsObserver = new SettingsObserver(); @@ -1450,6 +1482,8 @@ class PowerManagerService extends IPowerManager.Stub mLightSensorValue = -1; // reset our highest light sensor value when the screen turns off mHighestLightSensorValue = -1; + lightFilterStop(); + resetLastLightValues(); } } } @@ -1573,6 +1607,7 @@ class PowerManagerService extends IPowerManager.Stub } else { // cancel light sensor task mHandler.removeCallbacks(mAutoBrightnessTask); + lightFilterStop(); mScreenOffTime = SystemClock.elapsedRealtime(); long identity = Binder.clearCallingIdentity(); try { @@ -1687,7 +1722,7 @@ class PowerManagerService extends IPowerManager.Stub nominalCurrentValue = preferredBrightness; break; case SCREEN_ON_BIT: - nominalCurrentValue = Power.BRIGHTNESS_DIM; + nominalCurrentValue = mScreenDim; break; case 0: nominalCurrentValue = Power.BRIGHTNESS_OFF; @@ -1706,7 +1741,7 @@ class PowerManagerService extends IPowerManager.Stub // the scale is because the brightness ramp isn't linear and this biases // it so the later parts take longer. final float scale = 1.5f; - float ratio = (((float)Power.BRIGHTNESS_DIM)/preferredBrightness); + float ratio = (((float)mScreenDim)/preferredBrightness); if (ratio > 1.0f) ratio = 1.0f; if ((newState & SCREEN_ON_BIT) == 0) { if ((oldState & SCREEN_BRIGHT_BIT) != 0) { @@ -1732,8 +1767,9 @@ class PowerManagerService extends IPowerManager.Stub // still have a sense of when it is inactive, we // will then count going dim as turning off. mScreenOffTime = SystemClock.elapsedRealtime(); + mAlwaysOnAndDimmed = true; } - brightness = Power.BRIGHTNESS_DIM; + brightness = mScreenDim; } } long identity = Binder.clearCallingIdentity(); @@ -1916,7 +1952,7 @@ class PowerManagerService extends IPowerManager.Stub final int brightness = Settings.System.getInt(mContext.getContentResolver(), SCREEN_BRIGHTNESS); // Don't let applications turn the screen all the way off - return Math.max(brightness, Power.BRIGHTNESS_DIM); + return Math.max(brightness, mScreenDim); } catch (SettingNotFoundException snfe) { return Power.BRIGHTNESS_ON; } @@ -2101,11 +2137,41 @@ class PowerManagerService extends IPowerManager.Stub } } - private int getAutoBrightnessValue(int sensorValue, int[] values) { + private int getAutoBrightnessValue(int current, int last, int[] levels, int[] values) { try { + // If have a last value and sensor value is decreasing + // we should include hysteresis in calculations + if (last >= 0 && current < last) { + int i; + for (i = 0; i < levels.length; i++) { + if (last < levels[i]) { + break; + } + } + // First sensor level begins at 0 and doesn't have hysteresis + if (i > 0) { + final int length = levels[i - 1] - (i == 1 ? 0 : levels[i - 2]); + final int lower = levels[i - 1] - Math.round(mLightHysteresis * length); + if (mDebugLightSensor) { + Slog.d(TAG, "level=" + i + " current=" + current + " last=" + last + + " length=" + length + " lower=" + lower); + } + if (current < levels[i - 1] && current >= lower) { + // Current sensor value has decreased to the level below but is + // still too large due to the hysteresis. + // Ignore current to reduce flicker (use last) + if (mDebugLightSensor) { + Slog.d(TAG, "using last instead of current: " + current + + "->" + last); + } + current = last; + } + } + } + int i; - for (i = 0; i < mAutoBrightnessLevels.length; i++) { - if (sensorValue < mAutoBrightnessLevels[i]) { + for (i = 0; i < levels.length; i++) { + if (current < levels[i]) { break; } } @@ -2143,17 +2209,114 @@ class PowerManagerService extends IPowerManager.Stub } }; + private Runnable mLightFilterTask = new Runnable() { + public void run() { + synchronized (mLocks) { + boolean again = false; + if (mLightFilterSample > 0 && !isScreenTurningOffLocked()) { + mLightFilterSamples[mLightFilterIndex] = mLightFilterSample; + mLightFilterIndex = (mLightFilterIndex + 1) % + mLightFilterSamples.length; + int sum, count, sample; + sum = count = sample = 0; + for (int i = 0; i < mLightFilterSamples.length; i++) { + sample = mLightFilterSamples[i]; + if (sample < 0) { + break; + } + sum += sample; + count++; + } + // Count can't be zero here + int average = Math.round((float)sum / count); + if (average != (int)mLightSensorValue) { + lightSensorChangedLocked(average); + } + if ((int)mLightSensorValue != mLightFilterSample) { + again = true; + if (mDebugLightSensor) { + Slog.d(TAGF, "Tick: " + (int)mLightSensorValue + "::" + + mLightFilterSample); + } + } else if (mDebugLightSensor) { + mLightFilterCounter++; + again = mLightFilterCounter < mLightFilterSamples.length; + Slog.d(TAGF, "Done: " + (int)mLightSensorValue + " " + + mLightFilterCounter + "/" + mLightFilterSamples.length); + } + } + if (again) { + mHandler.postDelayed(mLightFilterTask, mLightFilterInterval); + } else { + lightFilterStop(); + } + } + } + }; + + private void lightFilterStop() { + if (mDebugLightSensor) { + Slog.d(TAGF, "stop"); + } + mLightFilterRunning = false; + mHandler.removeCallbacks(mLightFilterTask); + mLightFilterSample = -1; + } + + private void lightFilterReset(int initial) { + mLightFilterCounter = 0; + mLightFilterIndex = 0; + mLightFilterSamples = new int[(mLightFilterWindow / mLightFilterInterval)]; + java.util.Arrays.fill(mLightFilterSamples, initial); + if (mDebugLightSensor) { + Slog.d(TAGF, "reset: " + initial); + } + } + + private void resetLastLightValues() { + mLastLcdValue = -1; + mLastButtonValue = -1; + mLastKeyboardValue = -1; + } + + public int getLightSensorValue() { + return (int) mLightSensorValue; + } + + public int getRawLightSensorValue() { + if (mLightFilterEnabled && mLightFilterSample != -1) { + return mLightFilterSample; + } else { + return getLightSensorValue(); + } + } + + public int getLightSensorScreenBrightness() { + return mLightSensorScreenBrightness; + } + + public int getLightSensorButtonBrightness() { + return mLightSensorButtonBrightness; + } + + public int getLightSensorKeyboardBrightness() { + return mLightSensorKeyboardBrightness; + } + private void dockStateChanged(int state) { synchronized (mLocks) { mIsDocked = (state != Intent.EXTRA_DOCK_STATE_UNDOCKED); if (mIsDocked) { mHighestLightSensorValue = -1; + resetLastLightValues(); } if ((mPowerState & SCREEN_ON_BIT) != 0) { // force lights recalculation int value = (int)mLightSensorValue; mLightSensorValue = -1; + resetLastLightValues(); lightSensorChangedLocked(value); + lightFilterReset((int)mLightSensorValue); } } } @@ -2163,8 +2326,12 @@ class PowerManagerService extends IPowerManager.Stub Slog.d(TAG, "lightSensorChangedLocked " + value); } - // do not allow light sensor value to decrease - if (mHighestLightSensorValue < value) { + // do not allow light sensor value to decrease unless the + // environment is very dark or user has actively permitted it + if (mLightDecrease || value < 200) { + mHighestLightSensorValue = value; + } + else if (mHighestLightSensorValue < value) { mHighestLightSensorValue = value; } @@ -2176,11 +2343,18 @@ class PowerManagerService extends IPowerManager.Stub // stationary in a dock. int lcdValue = getAutoBrightnessValue( (mIsDocked ? value : mHighestLightSensorValue), - mLcdBacklightValues); - int buttonValue = getAutoBrightnessValue(value, mButtonBacklightValues); + mLastLcdValue, + (mCustomLightEnabled ? mCustomLightLevels : mAutoBrightnessLevels), + (mCustomLightEnabled ? mCustomLcdValues : mLcdBacklightValues)); + + int buttonValue = getAutoBrightnessValue(value, mLastButtonValue, + (mCustomLightEnabled ? mCustomLightLevels : mAutoBrightnessLevels), + (mCustomLightEnabled ? mCustomButtonValues : mButtonBacklightValues)); int keyboardValue; if (mKeyboardVisible) { - keyboardValue = getAutoBrightnessValue(value, mKeyboardBacklightValues); + keyboardValue = getAutoBrightnessValue(value, mLastKeyboardValue, + (mCustomLightEnabled ? mCustomLightLevels : mAutoBrightnessLevels), + (mCustomLightEnabled ? mCustomKeyboardValues : mKeyboardBacklightValues)); } else { keyboardValue = 0; } @@ -2195,18 +2369,29 @@ class PowerManagerService extends IPowerManager.Stub } boolean startAnimation = false; - if (mAutoBrightessEnabled && mScreenBrightnessOverride < 0) { + if (mAutoBrightessEnabled && mScreenBrightnessOverride < 0 && !mAlwaysOnAndDimmed) { if (ANIMATE_SCREEN_LIGHTS) { if (mScreenBrightness.setTargetLocked(lcdValue, AUTOBRIGHTNESS_ANIM_STEPS, INITIAL_SCREEN_BRIGHTNESS, (int)mScreenBrightness.curValue)) { startAnimation = true; + mLastLcdValue = value; } } else { int brightnessMode = (mAutoBrightessEnabled ? LightsService.BRIGHTNESS_MODE_SENSOR : LightsService.BRIGHTNESS_MODE_USER); mLcdLight.setBrightness(lcdValue, brightnessMode); + mLastLcdValue = value; + } + // If we don't report brightness correctly stats will be borked + long identity = Binder.clearCallingIdentity(); + try { + mBatteryStats.noteScreenBrightness(getPreferredBrightness()); + } catch (RemoteException e) { + Slog.w(TAG, "RemoteException calling noteScreenOn on BatteryStatsService", e); + } finally { + Binder.restoreCallingIdentity(identity); } } if (mButtonBrightnessOverride < 0) { @@ -2215,9 +2400,11 @@ class PowerManagerService extends IPowerManager.Stub AUTOBRIGHTNESS_ANIM_STEPS, INITIAL_BUTTON_BRIGHTNESS, (int)mButtonBrightness.curValue)) { startAnimation = true; + mLastButtonValue = value; } } else { mButtonLight.setBrightness(buttonValue); + mLastButtonValue = value; } } if (mButtonBrightnessOverride < 0 || !mKeyboardVisible) { @@ -2226,9 +2413,11 @@ class PowerManagerService extends IPowerManager.Stub AUTOBRIGHTNESS_ANIM_STEPS, INITIAL_BUTTON_BRIGHTNESS, (int)mKeyboardBrightness.curValue)) { startAnimation = true; + mLastKeyboardValue = value; } } else { mKeyboardLight.setBrightness(keyboardValue); + mLastKeyboardValue = value; } } if (startAnimation) { @@ -2365,6 +2554,7 @@ class PowerManagerService extends IPowerManager.Stub int value = (int)mLightSensorValue; mLightSensorValue = -1; lightSensorChangedLocked(value); + lightFilterReset((int)mLightSensorValue); } } userActivity(SystemClock.uptimeMillis(), false, BUTTON_EVENT, true); @@ -2400,10 +2590,143 @@ class PowerManagerService extends IPowerManager.Stub if (mLightSensorValue >= 0) { int value = (int)mLightSensorValue; mLightSensorValue = -1; + resetLastLightValues(); lightSensorChangedLocked(value); } + lightFilterReset((int)mLightSensorValue); + if (!enabled) { + lightFilterStop(); + } + } + } + } + + private void updateLightSettings() { + ContentResolver cr = mContext.getContentResolver(); + boolean filterWasEnabled = mLightFilterEnabled; + + long tag = Settings.System.getLong(cr, + Settings.System.LIGHTS_CHANGED, 0); + if (tag == mLightSettingsTag) { + return; + } + mLightSettingsTag = tag; + mCustomLightEnabled = Settings.System.getInt(cr, + Settings.System.LIGHT_SENSOR_CUSTOM, 0) != 0; + mLightDecrease = Settings.System.getInt(cr, + Settings.System.LIGHT_DECREASE, 0) != 0; + mLightHysteresis = Settings.System.getInt(cr, + Settings.System.LIGHT_HYSTERESIS, 50) / 100f; + mLightFilterEnabled = Settings.System.getInt(cr, + Settings.System.LIGHT_FILTER, 0) != 0; + mLightFilterWindow = Settings.System.getInt(cr, + Settings.System.LIGHT_FILTER_WINDOW, 30000); + mLightFilterInterval = Settings.System.getInt(cr, + Settings.System.LIGHT_FILTER_INTERVAL, 1000); + mLightFilterReset = Settings.System.getInt(cr, + Settings.System.LIGHT_FILTER_RESET, 800); + if (mCustomLightEnabled) { + mScreenDim = Settings.System.getInt(cr, + Settings.System.LIGHT_SCREEN_DIM, Power.BRIGHTNESS_DIM); + } else { + mScreenDim = Power.BRIGHTNESS_DIM; + } + + if (mLightFilterEnabled) { + if (!filterWasEnabled) { + lightFilterReset(-1); } + } else { + lightFilterStop(); } + + if (mDebugLightSensor) { + Slog.d(TAG, "custom: " + mCustomLightEnabled); + Slog.d(TAG, "decrease: " + mLightDecrease); + Slog.d(TAG, "hysteresis: " + mLightHysteresis); + Slog.d(TAG, "filter: " + mLightFilterEnabled); + Slog.d(TAG, "window: " + mLightFilterWindow); + Slog.d(TAG, "interval: " + mLightFilterInterval); + Slog.d(TAG, "reset: " + mLightFilterReset); + Slog.d(TAG, "dim: " + mScreenDim); + } + + if (mCustomLightEnabled) { + // Load custom values + try { + mCustomLightLevels = parseIntArray(Settings.System.getString( + cr, Settings.System.LIGHT_SENSOR_LEVELS)); + if (mDebugLightSensor) { + Slog.d(TAG, "levels: " + + java.util.Arrays.toString(mCustomLightLevels)); + } + + mCustomLcdValues = parseIntArray(Settings.System.getString( + cr, Settings.System.LIGHT_SENSOR_LCD_VALUES)); + if (mDebugLightSensor) { + Slog.d(TAG, "lcd values: " + + java.util.Arrays.toString(mCustomLcdValues)); + } + + mCustomButtonValues = parseIntArray(Settings.System.getString( + cr, Settings.System.LIGHT_SENSOR_BUTTON_VALUES)); + if (mDebugLightSensor) { + Slog.d(TAG, "button values: " + + java.util.Arrays.toString(mCustomButtonValues)); + } + + mCustomKeyboardValues = parseIntArray(Settings.System.getString( + cr, Settings.System.LIGHT_SENSOR_KEYBOARD_VALUES)); + if (mDebugLightSensor) { + Slog.d(TAG, "keyboard values: " + + java.util.Arrays.toString(mCustomKeyboardValues)); + } + + if (mDebugLightSensor) { + Slog.d(TAG, "default levels: " + + java.util.Arrays.toString(mAutoBrightnessLevels)); + Slog.d(TAG, "default lcd values: " + + java.util.Arrays.toString(mLcdBacklightValues)); + Slog.d(TAG, "default button values: " + + java.util.Arrays.toString(mButtonBacklightValues)); + Slog.d(TAG, "default keyboard values: " + + java.util.Arrays.toString(mKeyboardBacklightValues)); + } + + // Sanity check + int N = mCustomLightLevels.length; + if (N < 1 || mCustomLcdValues.length != (N + 1) + || mCustomButtonValues.length != (N + 1) + || mCustomKeyboardValues.length != (N + 1)) { + throw new Exception("sanity check failed"); + } + // force recompute of backlight values + if (mLightSensorValue >= 0) { + int value = (int)mLightSensorValue; + mLightSensorValue = -1; + resetLastLightValues(); + lightSensorChangedLocked(value); + } + } catch (Exception e) { + // Use defaults since we can't trust custom values + mCustomLightEnabled = false; + Slog.e(TAG, "loading custom settings failed", e); + } + } + } + + private int[] parseIntArray(String intArray) { + int[] result; + if (intArray == null || intArray.length() == 0) { + result = new int[0]; + } else { + String[] split = intArray.split(","); + result = new int[split.length]; + for (int i = 0; i < split.length; i++) { + result[i] = Integer.parseInt(split[i]); + } + } + return result; } /** Sets the screen off timeouts: @@ -2606,7 +2929,7 @@ class PowerManagerService extends IPowerManager.Stub public void setBacklightBrightness(int brightness) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); // Don't let applications turn the screen all the way off - brightness = Math.max(brightness, Power.BRIGHTNESS_DIM); + brightness = Math.max(brightness, mScreenDim); mLcdLight.setBrightness(brightness); mKeyboardLight.setBrightness(mKeyboardVisible ? brightness : 0); mButtonLight.setBrightness(brightness); @@ -2722,6 +3045,7 @@ class PowerManagerService extends IPowerManager.Stub mSensorManager.registerListener(mLightListener, mLightSensor, SensorManager.SENSOR_DELAY_NORMAL); } else { + lightFilterStop(); mSensorManager.unregisterListener(mLightListener); mHandler.removeCallbacks(mAutoBrightnessTask); } @@ -2789,6 +3113,40 @@ class PowerManagerService extends IPowerManager.Stub Slog.d(TAG, "onSensorChanged: light value: " + value); } mHandler.removeCallbacks(mAutoBrightnessTask); + mLightFilterSample = value; + if (mAutoBrightessEnabled && mLightFilterEnabled) { + if (mLightFilterRunning && mLightSensorValue != -1) { + // Large changes -> quick response + int diff = Math.abs((int)mLightSensorValue - value); + if (diff > mLightFilterReset) { + if (mDebugLightSensor) { + Slog.d(TAGF, "reset cause: " + value + + " " + mLightSensorValue + " " + diff); + } + lightFilterReset(-1); + } + if (mDebugLightSensor) { + Slog.d(TAGF, "sample: " + value); + } + } else { + if (mLightSensorValue == -1 || + milliseconds < mLastScreenOnTime + mLightSensorWarmupTime) { + // process the value immediately if screen has just turned on + lightFilterReset(-1); + lightSensorChangedLocked(value); + } else { + // Small changes -> filtered + lightFilterReset((int)mLightSensorValue); + if (mDebugLightSensor) { + Slog.d(TAGF, "start: " + value); + } + mLightFilterRunning = true; + mHandler.postDelayed(mLightFilterTask, LIGHT_SENSOR_DELAY); + } + } + return; + } + if (mLightSensorValue != value) { if (mLightSensorValue == -1 || milliseconds < mLastScreenOnTime + mLightSensorWarmupTime) { diff --git a/services/java/com/android/server/ProcessStats.java b/services/java/com/android/server/ProcessStats.java index a02c4e7..a155f59 100644 --- a/services/java/com/android/server/ProcessStats.java +++ b/services/java/com/android/server/ProcessStats.java @@ -138,7 +138,7 @@ public class ProcessStats { private boolean mFirst = true; - private byte[] mBuffer = new byte[256]; + private byte[] mBuffer = new byte[1024]; /** * The time in microseconds that the CPU has been running at each speed. @@ -502,7 +502,7 @@ public class ProcessStats { private long[] getCpuSpeedTimes(long[] out) { long[] tempTimes = out; long[] tempSpeeds = mCpuSpeeds; - final int MAX_SPEEDS = 20; + final int MAX_SPEEDS = 30; if (out == null) { tempTimes = new long[MAX_SPEEDS]; // Hopefully no more than that tempSpeeds = new long[MAX_SPEEDS]; @@ -514,20 +514,22 @@ public class ProcessStats { StringTokenizer st = new StringTokenizer(file, "\n "); while (st.hasMoreElements()) { String token = st.nextToken(); - try { - long val = Long.parseLong(token); - tempSpeeds[speed] = val; - token = st.nextToken(); - val = Long.parseLong(token); - tempTimes[speed] = val; - speed++; - if (speed == MAX_SPEEDS) break; // No more - if (localLOGV && out == null) { - Slog.v(TAG, "First time : Speed/Time = " + tempSpeeds[speed - 1] - + "\t" + tempTimes[speed - 1]); + if (st.hasMoreElements()) { + try { + long val = Long.parseLong(token); + tempSpeeds[speed] = val; + token = st.nextToken(); + val = Long.parseLong(token); + tempTimes[speed] = val; + speed++; + if (speed == MAX_SPEEDS) break; // No more + if (localLOGV && out == null) { + Slog.v(TAG, "First time : Speed/Time = " + tempSpeeds[speed - 1] + + "\t" + tempTimes[speed - 1]); + } + } catch (NumberFormatException nfe) { + Slog.i(TAG, "Unable to parse time_in_state"); } - } catch (NumberFormatException nfe) { - Slog.i(TAG, "Unable to parse time_in_state"); } } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 9d5d035..b7a7bbf 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -39,6 +39,7 @@ import android.os.*; import android.provider.Contacts.People; import android.provider.Settings; import android.server.BluetoothA2dpService; +import android.server.BluetoothHidService; import android.server.BluetoothService; import android.server.search.SearchManagerService; import android.util.EventLog; @@ -95,6 +96,7 @@ class ServerThread extends Thread { WindowManagerService wm = null; BluetoothService bluetooth = null; BluetoothA2dpService bluetoothA2dp = null; + BluetoothHidService bluetoothHid = null; HeadsetObserver headset = null; DockObserver dock = null; UiModeManagerService uiMode = null; @@ -193,6 +195,10 @@ class ServerThread extends Thread { bluetoothA2dp = new BluetoothA2dpService(context, bluetooth); ServiceManager.addService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE, bluetoothA2dp); + bluetoothHid = new BluetoothHidService(context, bluetooth); + ServiceManager.addService(BluetoothHidService.BLUETOOTH_HID_SERVICE, + bluetoothHid); + //Log.e(TAG, "Bluetooth HID Service"); int bluetoothOn = Settings.Secure.getInt(mContentResolver, Settings.Secure.BLUETOOTH_ON, 0); @@ -442,7 +448,7 @@ class ServerThread extends Thread { // Enable the JIT for the system_server process VMRuntime.getRuntime().startJitCompilation(); } - + // It is now time to start up the app processes... if (devicePolicy != null) { diff --git a/services/java/com/android/server/ThrottleService.java b/services/java/com/android/server/ThrottleService.java index a93a6ee..5b89ee0 100644 --- a/services/java/com/android/server/ThrottleService.java +++ b/services/java/com/android/server/ThrottleService.java @@ -900,7 +900,12 @@ public class ThrottleService extends IThrottleManager.Stub { dataFile = new File(throttleDir, imsiHash); } // touch the file so it's not LRU - dataFile.setLastModified(System.currentTimeMillis()); + if (System.currentTimeMillis() >= 0) { + dataFile.setLastModified(System.currentTimeMillis()); + } else { + // system clock is out of whack, but that doesn't mean we can't boot + dataFile.setLastModified(Long.MAX_VALUE); + } checkAndDeleteLRUDataFile(throttleDir); return dataFile; } diff --git a/services/java/com/android/server/VibratorService.java b/services/java/com/android/server/VibratorService.java index 2e7e3e1..379c158 100755 --- a/services/java/com/android/server/VibratorService.java +++ b/services/java/com/android/server/VibratorService.java @@ -123,8 +123,17 @@ public class VibratorService extends IVibratorService.Stub { return; } Vibration vib = new Vibration(token, milliseconds); + try { + token.linkToDeath(vib, 0); + } catch (RemoteException e) { + return; + } synchronized (mVibrations) { removeVibrationLocked(token); + if ((mCurrentVibration != null) && (mThread == null)) { + mCurrentVibration.mToken.unlinkToDeath(mCurrentVibration, 0); + mCurrentVibration = null; + } doCancelVibrateLocked(); mCurrentVibration = vib; startVibrationLocked(vib); @@ -174,6 +183,10 @@ public class VibratorService extends IVibratorService.Stub { synchronized (mVibrations) { removeVibrationLocked(token); + if ((mCurrentVibration != null) && (mThread == null)) { + mCurrentVibration.mToken.unlinkToDeath(mCurrentVibration, 0); + mCurrentVibration = null; + } doCancelVibrateLocked(); if (repeat >= 0) { mVibrations.addFirst(vib); @@ -205,6 +218,10 @@ public class VibratorService extends IVibratorService.Stub { doCancelVibrateLocked(); startNextVibrationLocked(); } + if ((mCurrentVibration != null) && (mThread == null)) { + mCurrentVibration.mToken.unlinkToDeath(mCurrentVibration, 0); + mCurrentVibration = null; + } } } finally { diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index a8dad88..0715340 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -64,6 +64,7 @@ import android.content.pm.PackageManager; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.Matrix; +import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.Region; @@ -133,6 +134,9 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; +import android.graphics.Canvas; +import android.graphics.Path; + /** {@hide} */ public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor, KeyInputQueue.HapticFeedbackCallback { @@ -387,6 +391,13 @@ public class WindowManagerService extends IWindowManager.Stub Surface mBlurSurface; boolean mBlurShown; + Surface mMouseSurface; + boolean mMouseDisplayed = false; + int mMlx; + int mMly; + int mMlw; + int mMlh; + int mTransactionSequence = 0; final float[] mTmpFloats = new float[9]; @@ -2148,6 +2159,7 @@ public class WindowManagerService extends IWindowManager.Stub private void removeWindowInnerLocked(Session session, WindowState win) { mKeyWaiter.finishedKey(session, win.mClient, true, KeyWaiter.RETURN_NOTHING); + mKeyWaiter.releaseMotionTarget(win); mKeyWaiter.releasePendingPointerLocked(win.mSession); mKeyWaiter.releasePendingTrackballLocked(win.mSession); @@ -5260,7 +5272,7 @@ public class WindowManagerService extends IWindowManager.Stub // dispatch the event. try { if (DEBUG_INPUT || DEBUG_FOCUS || WindowManagerPolicy.WATCH_POINTER) { - Slog.v(TAG, "Delivering pointer " + qev + " to " + target); + Slog.v(TAG, "Delivering pointer " + qev + " Ev " + ev + " to " + target); } if (MEASURE_LATENCY) { @@ -6119,6 +6131,12 @@ public class WindowManagerService extends IWindowManager.Stub } } + void releaseMotionTarget(WindowState win) { + if (mMotionTarget == win) { + mMotionTarget = null; + } + } + MotionEvent finishedKey(Session session, IWindow client, boolean force, int returnWhat) { if (DEBUG_INPUT) Slog.v( @@ -6169,7 +6187,7 @@ public class WindowManagerService extends IWindowManager.Stub if (qev != null) { res = (MotionEvent)qev.event; if (DEBUG_INPUT) Slog.v(TAG, - "Returning pending motion: " + res); + "Returning pending motion: " + res + " q: " + qev); mQueue.recycleEvent(qev); if (win != null && returnWhat == RETURN_PENDING_POINTER) { res.offsetLocation(-win.mFrame.left, -win.mFrame.top); @@ -6394,7 +6412,8 @@ public class WindowManagerService extends IWindowManager.Stub if (screenIsOff) { if (!mPolicy.isWakeRelMovementTq(event.deviceId, device.classes, event)) { - //Slog.i(TAG, "dropping because screenIsOff and !isWakeKey"); + if (DEBUG_INPUT) + Slog.i(TAG, "dropping because screenIsOff and !isWakeKey"); return false; } event.flags |= WindowManagerPolicy.FLAG_WOKE_HERE; @@ -6540,7 +6559,8 @@ public class WindowManagerService extends IWindowManager.Stub if (ev.classType == RawInputEvent.CLASS_TOUCHSCREEN) { eventType = eventType((MotionEvent)ev.event); } else if (ev.classType == RawInputEvent.CLASS_KEYBOARD || - ev.classType == RawInputEvent.CLASS_TRACKBALL) { + ev.classType == RawInputEvent.CLASS_TRACKBALL || + ev.classType == RawInputEvent.CLASS_MOUSE) { eventType = LocalPowerManager.BUTTON_EVENT; } else { eventType = LocalPowerManager.OTHER_EVENT; @@ -6600,6 +6620,44 @@ public class WindowManagerService extends IWindowManager.Stub case RawInputEvent.CLASS_TOUCHSCREEN: //Slog.i(TAG, "Read next event " + ev); dispatchPointer(ev, (MotionEvent)ev.event, 0, 0); + if (mMouseDisplayed) { + Surface.openTransaction(); + mMouseSurface.hide(); + Surface.closeTransaction(); + mMouseDisplayed = false; + } + break; + case RawInputEvent.CLASS_MOUSE: + MotionEvent mmev = (MotionEvent)ev.event; + int mcx = (int)mmev.getX(); + int mcy = (int)mmev.getY(); + + if (mMouseSurface != null && (mMlx != mcx || mMly != mcy)) { + Surface.openTransaction(); + if (DEBUG_INPUT) + Slog.i(TAG, "Open transaction for the mouse surface"); + WindowState top = + (WindowState)mWindows.get(mWindows.size() - 1); + try { + if (DEBUG_INPUT) + Slog.i(TAG, "Move surf, x: " + + Integer.toString(mcx) + " y:" + + Integer.toString(mcy)); + + mMouseSurface.setPosition(mcx, mcy); + mMouseSurface.setLayer(top.mAnimLayer + 1); + if (!mMouseDisplayed) { + mMouseSurface.show(); + mMouseDisplayed = true; + } + mMlx = mcx; + mMly = mcy; + } catch ( RuntimeException e) { + Slog.e(TAG, "Failure showing mouse surface",e); + } + Surface.closeTransaction(); + } + dispatchPointer(ev, (MotionEvent)ev.event, 0, 0); break; case RawInputEvent.CLASS_TRACKBALL: dispatchTrackball(ev, (MotionEvent)ev.event, 0, 0); @@ -9584,6 +9642,61 @@ public class WindowManagerService extends IWindowManager.Stub mFxSession = new SurfaceSession(); } + if (mMouseSurface == null) { + int mMx, mMy, mMw, mMh; + Canvas mCanvas; + Path mPath = new Path(); + + if (DEBUG_INPUT) + Slog.i(TAG, "Create Mouse Surface"); + + mMw = 12; + mMh = 20; + mMx = (mDisplay.getWidth() - mMw) / 2; + mMy = (mDisplay.getHeight() - mMh) / 2; + + try { + + /* + *First Mouse event, create Surface + */ + + mMouseSurface = + new Surface(mFxSession, + 0, -1, mMw, mMh, + PixelFormat.TRANSPARENT, + Surface.FX_SURFACE_NORMAL); + mCanvas = mMouseSurface.lockCanvas(null); + Paint tPaint = new Paint(); + tPaint.setStyle(Paint.Style.STROKE); + tPaint.setStrokeWidth(2); + tPaint.setColor(0xffffffff); + mPath.moveTo(0.0f, 0.0f); + mPath.lineTo(12.0f, 12.0f); + mPath.lineTo(7.0f, 12.0f); + mPath.lineTo(11.0f, 20.0f); + mPath.lineTo(8.0f, 21.0f); + mPath.lineTo(4.0f, 13.0f); + mPath.lineTo(0.0f, 17.0f); + mPath.close(); + mCanvas.clipPath(mPath); + mCanvas.drawColor(0xff000000); + mCanvas.drawPath(mPath, tPaint); + + mMouseSurface.unlockCanvasAndPost(mCanvas); + mMouseSurface.openTransaction(); + mMouseSurface.setSize(mMw, mMh); + mMouseSurface.closeTransaction(); + + } catch (Exception e) { + Slog.e(TAG, "Exception creating mouse surface",e); + } + mMlx = mMx; + mMly = mMy; + mMlw = mMw; + mMlh = mMh; + } + if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION"); // Initialize state of exiting tokens. diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 0c11940..b8b6df7 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -52,6 +52,7 @@ import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.IIntentReceiver; @@ -1176,7 +1177,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen d.setCancelable(false); d.setTitle("System UIDs Inconsistent"); d.setMessage("UIDs on the system are inconsistent, you need to wipe your data partition or your device will be unstable."); - d.setButton("I'm Feeling Lucky", + d.setButton(DialogInterface.BUTTON_POSITIVE, "I'm Feeling Lucky", mHandler.obtainMessage(IM_FEELING_LUCKY_MSG)); mUidAlert = d; d.show(); @@ -5879,10 +5880,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen String[] pkgs = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES); if (pkgs != null) { for (String pkg : pkgs) { - if (forceStopPackageLocked(pkg, -1, false, false, false)) { - setResultCode(Activity.RESULT_OK); - return; - } + synchronized (ActivityManagerService.this) { + if (forceStopPackageLocked(pkg, -1, false, false, false)) { + setResultCode(Activity.RESULT_OK); + return; + } + } } } } @@ -6800,7 +6803,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (localLOGV) Slog.v( TAG, "getTasks: max=" + maxNum + ", flags=" + flags + ", receiver=" + receiver); - + /** This could be bad? Possibly, Might look into better way to do it: Pedlar if (checkCallingPermission(android.Manifest.permission.GET_TASKS) != PackageManager.PERMISSION_GRANTED) { if (receiver != null) { @@ -6817,7 +6820,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen + " requires " + android.Manifest.permission.GET_TASKS; Slog.w(TAG, msg); throw new SecurityException(msg); - } + } **/ int pos = mHistory.size()-1; HistoryRecord next = @@ -6929,8 +6932,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags) { synchronized (this) { - enforceCallingPermission(android.Manifest.permission.GET_TASKS, - "getRecentTasks()"); + //enforceCallingPermission(android.Manifest.permission.GET_TASKS, + // "getRecentTasks()"); IPackageManager pm = ActivityThread.getPackageManager(); @@ -9044,7 +9047,28 @@ public final class ActivityManagerService extends ActivityManagerNative implemen sr.crashCount++; } } - + + // If the crashing process is what we consider to be the "home process" and it has been + // replaced by a third-party app, clear the package preferred activities from packages + // with a home activity running in the process to prevent a repeatedly crashing app + // from blocking the user to manually clear the list. + if (app == mHomeProcess && mHomeProcess.activities.size() > 0 + && (mHomeProcess.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { + Iterator it = mHomeProcess.activities.iterator(); + while (it.hasNext()) { + HistoryRecord r = (HistoryRecord)it.next(); + if (r.isHomeActivity) { + Log.i(TAG, "Clearing package preferred activities from " + r.packageName); + try { + ActivityThread.getPackageManager() + .clearPackagePreferredActivities(r.packageName); + } catch (RemoteException c) { + // pm is in same process, this will never happen. + } + } + } + } + mProcessCrashTimes.put(app.info.processName, app.info.uid, now); return true; } @@ -13814,7 +13838,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen int adj; int schedGroup; int N; - if (app == TOP_APP) { + if ("com.android.mms".equals(app.processName)) { + // MMS can die in situations of heavy memory pressure. + // Always push it to the top. + adj = FOREGROUND_APP_ADJ; + schedGroup = Process.THREAD_GROUP_DEFAULT; + app.adjType = "mms"; + } else if (app == TOP_APP) { // The last app on the list is the foreground app. adj = FOREGROUND_APP_ADJ; schedGroup = Process.THREAD_GROUP_DEFAULT; @@ -13856,7 +13886,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } else if (app == mHomeProcess) { // This process is hosting what we currently consider to be the // home app, so we don't want to let it go into the background. - adj = HOME_APP_ADJ; + adj = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.LOCK_HOME_IN_MEMORY, 0) == 1 ? VISIBLE_APP_ADJ : HOME_APP_ADJ; schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE; app.adjType = "home"; } else if ((N=app.activities.size()) != 0) { diff --git a/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java b/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java index 8e9818d..9fb48b3 100644 --- a/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java +++ b/services/java/com/android/server/am/AppWaitingForDebuggerDialog.java @@ -17,6 +17,7 @@ package com.android.server.am; import android.content.Context; +import android.content.DialogInterface; import android.os.Handler; import android.os.Message; @@ -49,7 +50,7 @@ class AppWaitingForDebuggerDialog extends BaseErrorDialog { text.append(" is waiting for the debugger to attach."); setMessage(text.toString()); - setButton("Force Close", mHandler.obtainMessage(1, app)); + setButton(DialogInterface.BUTTON_POSITIVE, "Force Close", mHandler.obtainMessage(1, app)); setTitle("Waiting For Debugger"); getWindow().setTitle("Waiting For Debugger: " + app.info.processName); } diff --git a/services/java/com/android/server/am/FactoryErrorDialog.java b/services/java/com/android/server/am/FactoryErrorDialog.java index 2e25474..b19bb5c 100644 --- a/services/java/com/android/server/am/FactoryErrorDialog.java +++ b/services/java/com/android/server/am/FactoryErrorDialog.java @@ -17,6 +17,7 @@ package com.android.server.am; import android.content.Context; +import android.content.DialogInterface; import android.os.Handler; import android.os.Message; @@ -26,7 +27,8 @@ class FactoryErrorDialog extends BaseErrorDialog { setCancelable(false); setTitle(context.getText(com.android.internal.R.string.factorytest_failed)); setMessage(msg); - setButton(context.getText(com.android.internal.R.string.factorytest_reboot), + setButton(DialogInterface.BUTTON_POSITIVE, + context.getText(com.android.internal.R.string.factorytest_reboot), mHandler.obtainMessage(0)); getWindow().setTitle("Factory Error"); } diff --git a/services/java/com/android/server/status/IconData.java b/services/java/com/android/server/status/IconData.java index fd226f9..9aaaba1 100644 --- a/services/java/com/android/server/status/IconData.java +++ b/services/java/com/android/server/status/IconData.java @@ -16,6 +16,7 @@ package com.android.server.status; +import android.provider.Settings; import android.util.Slog; public class IconData { @@ -30,8 +31,20 @@ public class IconData { public static final int ICON = 2; /** - * The type of this item. One of TEXT, ICON, or LEVEL_ICON. + * Indicates ths item represents an integer displayed on top of an icon. + */ + public static final int ICON_NUMBER = 3; + + /** + * Default colors to use for any text appearing on each type of icon. */ + private static final int DEFAULT_TEXT_COLOR = 0xff000000; + private static final int DEFAULT_ICON_COLOR = 0xffffffff; + private static final int DEFAULT_ICON_NUMBER_COLOR = 0xffffffff; + + /** + * The type of this item. One of TEXT, ICON, or LEVEL_ICON. + */ public int type; /** @@ -65,6 +78,23 @@ public class IconData { */ public CharSequence text; + /** + * The default color of any text associated with this icon. + */ + public int textColor; + + /** + * The system setting that holds the text color for this icon. + */ + public String colorSetting; + + /** + * The system setting that determines whether the icon is visible or not. + * Currently this only applies to the TEXT type. + */ + public String visibleSetting; + public boolean defVisibility; + private IconData() { } @@ -77,14 +107,35 @@ public class IconData { data.iconId = iconId; data.iconLevel = iconLevel; data.number = number; + data.textColor = DEFAULT_ICON_COLOR; + data.colorSetting = Settings.System.NOTIF_COUNT_COLOR; return data; } - public static IconData makeText(String slot, CharSequence text) { + public static IconData makeText(String slot, CharSequence text, String colorSetting, + String visibleSetting, boolean defVisibility) { IconData data = new IconData(); data.type = TEXT; data.slot = slot; data.text = text; + data.textColor = DEFAULT_TEXT_COLOR; + data.colorSetting = colorSetting; + data.visibleSetting = visibleSetting; + data.defVisibility = defVisibility; + return data; + } + + public static IconData makeIconNumber(String slot, + String iconPackage, int iconId, int iconLevel, int number, String colorSetting) { + IconData data = new IconData(); + data.type = ICON_NUMBER; + data.slot = slot; + data.iconPackage = iconPackage; + data.iconId = iconId; + data.iconLevel = iconLevel; + data.number = number; + data.textColor = DEFAULT_ICON_NUMBER_COLOR; + data.colorSetting = colorSetting; return data; } @@ -96,6 +147,10 @@ public class IconData { this.iconLevel = that.iconLevel; this.number = that.number; this.text = that.text; // should we clone this? + this.textColor = that.textColor; + this.colorSetting = that.colorSetting; + this.visibleSetting = that.visibleSetting; + this.defVisibility = that.defVisibility; } public IconData clone() { @@ -115,6 +170,14 @@ public class IconData { + " iconId=" + Integer.toHexString(this.iconId) + " iconLevel=" + this.iconLevel + ")"; } + + else if (this.type == ICON_NUMBER) { + return "IconData(slot=" + (this.slot != null ? "'" + this.slot + "'" : "null") + + " package=" + this.iconPackage + + " iconId=" + Integer.toHexString(this.iconId) + + " iconLevel=" + this.iconLevel + + " number='" + this.number + "')"; + } else { return "IconData(type=" + type + ")"; } diff --git a/services/java/com/android/server/status/StatusBarIcon.java b/services/java/com/android/server/status/StatusBarIcon.java index 6f8b8a8..9f49747 100644 --- a/services/java/com/android/server/status/StatusBarIcon.java +++ b/services/java/com/android/server/status/StatusBarIcon.java @@ -30,6 +30,11 @@ import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; +import android.graphics.drawable.AnimationDrawable; +import android.provider.Settings; +import android.view.WindowManager; +import android.widget.FrameLayout; +import android.util.DisplayMetrics; class StatusBarIcon { // TODO: get this from a resource @@ -44,8 +49,13 @@ class StatusBarIcon { private TextView mTextView; private AnimatedImageView mImageView; private TextView mNumberView; + private int clockColor = 0xff000000; + private int batteryPercentColor = 0xffffffff; + private int notifCountColor = 0xffffffff; + private Context mContext; public StatusBarIcon(Context context, IconData data, ViewGroup parent) { + mContext = context; mData = data.clone(); switch (data.type) { @@ -55,15 +65,25 @@ class StatusBarIcon { mTextView = t; LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT, - LinearLayout.LayoutParams.MATCH_PARENT); + LinearLayout.LayoutParams.FILL_PARENT); t.setTextSize(16); - t.setTextColor(0xff000000); t.setTypeface(Typeface.DEFAULT_BOLD); t.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT); t.setPadding(6, 0, 0, 0); t.setLayoutParams(layoutParams); t.setText(data.text); this.view = t; + + data.textColor = Settings.System.getInt(mContext.getContentResolver(), data.colorSetting, data.textColor); + t.setTextColor(data.textColor); + + if (getBoolean(context, data.visibleSetting, data.defVisibility)) { + t.setVisibility(View.VISIBLE); + } + else { + t.setVisibility(View.GONE); + } + break; } @@ -85,12 +105,71 @@ class StatusBarIcon { mNumberView = nv; if (data.number > 0) { nv.setText("" + data.number); + data.textColor = Settings.System.getInt(mContext.getContentResolver(), + data.colorSetting, data.textColor); + nv.setTextColor(data.textColor); nv.setVisibility(View.VISIBLE); } else { nv.setVisibility(View.GONE); } break; } + case IconData.ICON_NUMBER: { + // container + LayoutInflater inflater = (LayoutInflater)context.getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + View v = inflater.inflate(com.android.internal.R.layout.status_bar_icon, parent, false); + this.view = v; + + // icon + AnimatedImageView im = (AnimatedImageView)v.findViewById(com.android.internal.R.id.image); + im.setImageDrawable(getIcon(context, data)); + im.setImageLevel(data.iconLevel); + mImageView = im; + + // number + TextView nv = (TextView)v.findViewById(com.android.internal.R.id.number); + mNumberView = nv; + + //remove background, center, and change gravity of text + // attempt to correct position on both hdpi and mdpi + DisplayMetrics dm = new DisplayMetrics(); + ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getMetrics(dm); + + if (DisplayMetrics.DENSITY_HIGH == dm.densityDpi) { + mNumberView.setLayoutParams( + new FrameLayout.LayoutParams( + FrameLayout.LayoutParams.WRAP_CONTENT, + FrameLayout.LayoutParams.WRAP_CONTENT, + Gravity.RIGHT | Gravity.CENTER_VERTICAL)); + + mNumberView.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL); + } + else { + mNumberView.setLayoutParams( + new FrameLayout.LayoutParams( + FrameLayout.LayoutParams.WRAP_CONTENT, + FrameLayout.LayoutParams.WRAP_CONTENT, + Gravity.CENTER | Gravity.CENTER_VERTICAL)); + + mNumberView.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL); + } + + mNumberView.setBackgroundDrawable(null); + data.textColor = Settings.System.getInt(context.getContentResolver(), + data.colorSetting, data.textColor); + mNumberView.setTextColor(data.textColor); + mNumberView.setTextSize(12); + + if (data.number == 100) { + nv.setText("" + 99); + } else if ((data.number > 0)&&(data.number < 100)) { + nv.setText("" + data.number); + } else { + nv.setText(""); + } + break; + } } } @@ -100,12 +179,16 @@ class StatusBarIcon { } switch (data.type) { case IconData.TEXT: + data.textColor = Settings.System.getInt(mContext.getContentResolver(), + data.colorSetting, data.textColor); + mTextView.setTextColor(data.textColor); if (!TextUtils.equals(mData.text, data.text)) { TextView tv = mTextView; tv.setText(data.text); } break; case IconData.ICON: + case IconData.ICON_NUMBER: if (((mData.iconPackage != null && data.iconPackage != null) && !mData.iconPackage.equals(data.iconPackage)) || mData.iconId != data.iconId @@ -114,6 +197,9 @@ class StatusBarIcon { im.setImageDrawable(getIcon(context, data)); im.setImageLevel(data.iconLevel); } + data.textColor = Settings.System.getInt(mContext.getContentResolver(), + data.colorSetting, data.textColor); + mNumberView.setTextColor(data.textColor); if (mData.number != data.number) { TextView nv = mNumberView; if (data.number > 0) { @@ -182,5 +268,8 @@ class StatusBarIcon { int getNumber() { return mData.number; } + private boolean getBoolean(Context context, String systemSettingKey, boolean defaultValue) { + return 1 == android.provider.Settings.System.getInt(context.getContentResolver(), systemSettingKey, defaultValue ? 1 : 0); + } } diff --git a/services/java/com/android/server/status/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java index 94d1cb4..1fd72e6 100644 --- a/services/java/com/android/server/status/StatusBarPolicy.java +++ b/services/java/com/android/server/status/StatusBarPolicy.java @@ -20,6 +20,7 @@ import android.app.AlertDialog; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothHeadset; +import android.bluetooth.BluetoothHid; import android.bluetooth.BluetoothPbap; import android.content.BroadcastReceiver; import android.content.ContentResolver; @@ -121,6 +122,9 @@ public class StatusBarPolicy { //***** Signal strength icons private IconData mPhoneData; + private IBinder mPhoneDbmIcon; + private IconData mPhoneDbmData; + //GSM/UMTS private static final int[] sSignalImages = new int[] { com.android.internal.R.drawable.stat_sys_signal_0, @@ -299,6 +303,7 @@ public class StatusBarPolicy { private IconData mBluetoothData; private int mBluetoothHeadsetState; private boolean mBluetoothA2dpConnected; + private int mBluetoothHidState; private int mBluetoothPbapState; private boolean mBluetoothEnabled; @@ -381,6 +386,7 @@ public class StatusBarPolicy { } else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED) || action.equals(BluetoothHeadset.ACTION_STATE_CHANGED) || + action.equals(BluetoothHid.HID_DEVICE_STATE_CHANGED_ACTION) || action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED) || action.equals(BluetoothPbap.PBAP_STATE_CHANGED_ACTION)) { updateBluetooth(intent); @@ -415,7 +421,7 @@ public class StatusBarPolicy { // clock mCalendar = Calendar.getInstance(TimeZone.getDefault()); - mClockData = IconData.makeText("clock", ""); + mClockData = IconData.makeText("clock", "", Settings.System.CLOCK_COLOR, Settings.System.SHOW_STATUS_CLOCK, true); mClockIcon = service.addIcon(mClockData, null); updateClock(); @@ -425,8 +431,9 @@ public class StatusBarPolicy { new com.android.server.status.StorageNotification(context)); // battery - mBatteryData = IconData.makeIcon("battery", - null, com.android.internal.R.drawable.stat_sys_battery_unknown, 0, 0); + mBatteryData = IconData.makeIconNumber("battery", + null, com.android.internal.R.drawable.stat_sys_battery_unknown, 0, 0, + Settings.System.BATTERY_PERCENTAGE_STATUS_COLOR); mBatteryIcon = service.addIcon(mBatteryData, null); // phone_signal @@ -435,6 +442,10 @@ public class StatusBarPolicy { null, com.android.internal.R.drawable.stat_sys_signal_null, 0, 0); mPhoneIcon = service.addIcon(mPhoneData, null); + // dbm signal level + mPhoneDbmData = IconData.makeText("phone_dbm_signal", "", Settings.System.DBM_COLOR, Settings.System.SHOW_STATUS_DBM, false); + mPhoneDbmIcon = service.addIcon(mPhoneDbmData, null); + // register for phone state notifications. ((TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE)) .listen(mPhoneStateListener, @@ -488,8 +499,14 @@ public class StatusBarPolicy { null, com.android.internal.R.drawable.stat_sys_gps_acquiring_anim, 0, 0); mGpsFixIconData = IconData.makeIcon("gps", null, com.android.internal.R.drawable.stat_sys_gps_on, 0, 0); - mGpsIcon = service.addIcon(mGpsEnabledIconData, null); - service.setIconVisibility(mGpsIcon, false); + ContentResolver resolver = mContext.getContentResolver(); + String allowedProviders = Settings.Secure.getString(resolver, + Settings.Secure.LOCATION_PROVIDERS_ALLOWED); + if((allowedProviders.equalsIgnoreCase("gps")) || (allowedProviders.equalsIgnoreCase("network"))) + { + mGpsIcon = service.addIcon(mGpsEnabledIconData, null); + service.setIconVisibility(mGpsIcon, false); + } // Alarm clock mAlarmClockIconData = IconData.makeIcon( @@ -530,6 +547,7 @@ public class StatusBarPolicy { filter.addAction(AudioManager.VIBRATE_SETTING_CHANGED_ACTION); filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED); + filter.addAction(BluetoothHid.HID_DEVICE_STATE_CHANGED_ACTION); filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED); filter.addAction(BluetoothPbap.PBAP_STATE_CHANGED_ACTION); filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); @@ -562,7 +580,12 @@ public class StatusBarPolicy { if (b24) { res = R.string.twenty_four_hour_time_format; } else { - res = R.string.twelve_hour_time_format; + if (Settings.System.getInt(mContext.getContentResolver(), Settings.System.SHOW_TWELVE_HOUR_CLOCK_PERIOD, 1) == 1) { + res = R.string.twelve_hour_time_format; + } + else { + res = R.string.twelve_hour_time_format_no_period; + } } final char MAGIC1 = '\uEF00'; @@ -648,10 +671,21 @@ public class StatusBarPolicy { private final void updateBattery(Intent intent) { mBatteryData.iconId = intent.getIntExtra("icon-small", 0); mBatteryData.iconLevel = intent.getIntExtra("level", 0); - mService.updateIcon(mBatteryIcon, mBatteryData, null); boolean plugged = intent.getIntExtra("plugged", 0) != 0; int level = intent.getIntExtra("level", -1); + + //show battery percentage if not plugged in and status is enabled + if (plugged || level >= 100 || + Settings.System.getInt(mContext.getContentResolver(), + Settings.System.BATTERY_PERCENTAGE_STATUS_ICON, 0) == 0) { + mBatteryData.number = -1; + } else { + mBatteryData.number = level; + } + + mService.updateIcon(mBatteryIcon, mBatteryData, null); + if (false) { Slog.d(TAG, "updateBattery level=" + level + " plugged=" + plugged @@ -973,6 +1007,7 @@ public class StatusBarPolicy { private final void updateSignalStrength() { int iconLevel = -1; + int dBm = 0; int[] iconList; // Display signal strength while in "emergency calls only" mode @@ -985,6 +1020,7 @@ public class StatusBarPolicy { mPhoneData.iconId = com.android.internal.R.drawable.stat_sys_signal_null; } mService.updateIcon(mPhoneIcon, mPhoneData, null); + mService.updateIcon(mPhoneDbmIcon, mPhoneDbmData, null); return; } @@ -1001,6 +1037,10 @@ public class StatusBarPolicy { else if (asu >= 5) iconLevel = 2; else iconLevel = 1; + if (asu != 99) { + dBm = asu * 2 - 113; + } + // Though mPhone is a Manager, this call is not an IPC if (mPhone.isNetworkRoaming()) { iconList = sSignalImages_r; @@ -1015,15 +1055,19 @@ public class StatusBarPolicy { // If a voice call is made then RSSI should switch to 1x. if ((mPhoneState == TelephonyManager.CALL_STATE_IDLE) && isEvdo()){ iconLevel = getEvdoLevel(); + dBm = mSignalStrength.getEvdoDbm(); if (false) { Slog.d(TAG, "use Evdo level=" + iconLevel + " to replace Cdma Level=" + getCdmaLevel()); } } else { iconLevel = getCdmaLevel(); + dBm = mSignalStrength.getCdmaDbm(); } } mPhoneData.iconId = iconList[iconLevel]; mService.updateIcon(mPhoneIcon, mPhoneData, null); + mPhoneDbmData.text = Integer.toString(dBm); + mService.updateIcon(mPhoneDbmIcon, mPhoneDbmData, null); } private int getCdmaLevel() { @@ -1212,11 +1256,15 @@ public class StatusBarPolicy { } else if (action.equals(BluetoothPbap.PBAP_STATE_CHANGED_ACTION)) { mBluetoothPbapState = intent.getIntExtra(BluetoothPbap.PBAP_STATE, BluetoothPbap.STATE_DISCONNECTED); + } else if (action.equals(BluetoothHid.HID_DEVICE_STATE_CHANGED_ACTION)) { + mBluetoothHidState = intent.getIntExtra(BluetoothHid.HID_DEVICE_STATE, + BluetoothHid.STATE_DISCONNECTED); } else { return; } if (mBluetoothHeadsetState == BluetoothHeadset.STATE_CONNECTED || mBluetoothA2dpConnected || + mBluetoothHidState == BluetoothHid.STATE_CONNECTED || mBluetoothPbapState == BluetoothPbap.STATE_CONNECTED) { iconId = com.android.internal.R.drawable.stat_sys_data_bluetooth_connected; } diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java index 93c8d34..df7c689 100644 --- a/services/java/com/android/server/status/StatusBarService.java +++ b/services/java/com/android/server/status/StatusBarService.java @@ -41,6 +41,7 @@ import android.os.Handler; import android.os.Message; import android.os.SystemClock; import android.provider.Telephony; +import android.util.Log; import android.util.Slog; import android.view.Display; import android.view.Gravity; @@ -55,6 +56,7 @@ import android.view.WindowManager; import android.view.WindowManagerImpl; import android.view.animation.Animation; import android.view.animation.AnimationUtils; +import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RemoteViews; import android.widget.ScrollView; @@ -67,6 +69,12 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Set; +import android.provider.Settings; +import java.lang.reflect.Field; + +import android.graphics.PorterDuff.Mode; +import android.graphics.drawable.StateListDrawable; + /** * The public (ok, semi-public) service for the status bar. @@ -88,6 +96,9 @@ public class StatusBarService extends IStatusBar.Stub static final String TAG = "StatusBar"; static final boolean SPEW = false; + private boolean mShowPlmnSb; + private boolean mShowSpnSb; + static final int EXPANDED_LEAVE_ALONE = -10000; static final int EXPANDED_FULL_OPEN = -10001; @@ -101,6 +112,7 @@ public class StatusBarService extends IStatusBar.Stub private static final int OP_EXPAND = 5; private static final int OP_TOGGLE = 6; private static final int OP_DISABLE = 7; + private class PendingOp { IBinder key; int code; @@ -206,6 +218,7 @@ public class StatusBarService extends IStatusBar.Stub private Ticker mTicker; private View mTickerView; private boolean mTicking; + private TickerView tickerView; // Tracking finger for opening/closing. int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore @@ -224,6 +237,22 @@ public class StatusBarService extends IStatusBar.Stub boolean mAnimatingReveal = false; int mViewDelta; int[] mAbsPos = new int[2]; + private int blackColor = 0xff000000; + private int whiteColor = 0xffffffff; + private int notificationTitleColor = blackColor; + private int notificationTextColor = blackColor; + private int notificationTimeColor = blackColor; + boolean custNotBar = false; + boolean custExpBar = false; + int notifBarColorMask; + int expBarColorMask; + Mode notifPDMode = Mode.SCREEN; + Mode expPDMode = Mode.SCREEN; + Drawable closerDrawable; + Drawable expBarBackDrawable; + Drawable expBarHeadDrawable; + Drawable expBarNotifTitleDrawable; + // for disabling the status bar ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>(); @@ -234,9 +263,13 @@ public class StatusBarService extends IStatusBar.Stub */ public StatusBarService(Context context) { mContext = context; + notificationTitleColor = Settings.System.getInt(mContext.getContentResolver(), Settings.System.NOTIF_ITEM_TITLE_COLOR, blackColor); + notificationTextColor = Settings.System.getInt(mContext.getContentResolver(), Settings.System.NOTIF_ITEM_TEXT_COLOR, blackColor); + notificationTimeColor = Settings.System.getInt(mContext.getContentResolver(), Settings.System.NOTIF_ITEM_TIME_COLOR, blackColor); mDisplay = ((WindowManager)context.getSystemService( Context.WINDOW_SERVICE)).getDefaultDisplay(); makeStatusBarView(context); + updateColors(); mUninstallReceiver = new UninstallReceiver(); } @@ -251,7 +284,7 @@ public class StatusBarService extends IStatusBar.Stub Resources res = context.getResources(); mRightIconSlots = res.getStringArray(com.android.internal.R.array.status_bar_icon_order); mRightIcons = new StatusBarIcon[mRightIconSlots.length]; - + getNotBarConfig(); ExpandedView expanded = (ExpandedView)View.inflate(context, com.android.internal.R.layout.status_bar_expanded, null); expanded.mService = this; @@ -265,14 +298,23 @@ public class StatusBarService extends IStatusBar.Stub if (bg != null) { mPixelFormat = bg.getOpacity(); } - mStatusBarView = sb; + mDateView = (DateView)sb.findViewById(R.id.date); + + if (custNotBar) { + mStatusBarView.setBackgroundDrawable(res.getDrawable(com.android.internal.R.drawable.statusbar_background_sq, + notifBarColorMask, notifPDMode)); + mDateView.setBackgroundDrawable(res.getDrawable(com.android.internal.R.drawable.statusbar_background_sq, + notifBarColorMask, notifPDMode)); + mDateView.setPadding(6, 0, 6, 0); + } + mStatusIcons = (LinearLayout)sb.findViewById(R.id.statusIcons); mNotificationIcons = (IconMerger)sb.findViewById(R.id.notificationIcons); mNotificationIcons.service = this; mIcons = (LinearLayout)sb.findViewById(R.id.icons); mTickerView = sb.findViewById(R.id.ticker); - mDateView = (DateView)sb.findViewById(R.id.date); + mExpandedDialog = new ExpandedDialog(context); mExpandedView = expanded; @@ -288,21 +330,33 @@ public class StatusBarService extends IStatusBar.Stub mPlmnLabel = (TextView)expanded.findViewById(R.id.plmnLabel); mScrollView = (ScrollView)expanded.findViewById(R.id.scroll); mNotificationLinearLayout = expanded.findViewById(R.id.notificationLinearLayout); - + if (custExpBar) { + mExpandedView.findViewById(R.id.exp_view_lin_layout). + setBackgroundDrawable(expBarHeadDrawable); + mNoNotificationsTitle.setBackgroundDrawable(expBarNotifTitleDrawable); + mOngoingTitle.setBackgroundDrawable(expBarNotifTitleDrawable); + mLatestTitle.setBackgroundDrawable(expBarNotifTitleDrawable); + } mOngoingTitle.setVisibility(View.GONE); mLatestTitle.setVisibility(View.GONE); mTicker = new MyTicker(context, sb); - TickerView tickerView = (TickerView)sb.findViewById(R.id.tickerText); + tickerView = (TickerView)sb.findViewById(R.id.tickerText); tickerView.mTicker = mTicker; mTrackingView = (TrackingView)View.inflate(context, com.android.internal.R.layout.status_bar_tracking, null); mTrackingView.mService = this; mCloseView = (CloseDragHandle)mTrackingView.findViewById(R.id.close); + if (custExpBar) { + ImageView iv = (ImageView)mTrackingView.findViewById(R.id.close_image); + mCloseView.removeAllViews(); + iv.setImageDrawable(closerDrawable); + iv.setColorFilter(expBarColorMask, expPDMode); + mCloseView.addView(iv); + } mCloseView.mService = this; - mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore); // add the more icon for the notifications @@ -828,7 +882,8 @@ public class StatusBarService extends IStatusBar.Stub }; View makeNotificationView(StatusBarNotification notification, ViewGroup parent) { - NotificationData n = notification.data; + Resources res = mContext.getResources(); + NotificationData n = notification.data; RemoteViews remoteViews = n.contentView; if (remoteViews == null) { return null; @@ -838,9 +893,22 @@ public class StatusBarService extends IStatusBar.Stub LayoutInflater inflater = (LayoutInflater)mContext.getSystemService( Context.LAYOUT_INFLATER_SERVICE); View row = inflater.inflate(com.android.internal.R.layout.status_bar_latest_event, parent, false); - - // bind the click event to the content area ViewGroup content = (ViewGroup)row.findViewById(com.android.internal.R.id.content); + if (custExpBar) { + StateListDrawable sld = new StateListDrawable(); + int stateFocused = android.R.attr.state_focused; + int statePressed = android.R.attr.state_pressed; + Drawable colornormal = res.getDrawable(com.android.internal.R.drawable.status_bar_item_background_normal_cust); + Drawable colorfocused = res.getDrawable(com.android.internal.R.drawable.status_bar_item_background_focus_cust); + Drawable colorpressed = res.getDrawable(com.android.internal.R.drawable.status_bar_item_background_pressed_cust); + sld.addState(new int[] {stateFocused}, colorfocused); + sld.addState(new int[] {statePressed}, colorpressed); + sld.addState(new int[] {}, colornormal); + sld.mutate(); + sld.setColorFilter(expBarColorMask, expPDMode); + content.setBackgroundDrawable(sld); + + } content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); content.setOnFocusChangeListener(mFocusChangeListener); PendingIntent contentIntent = n.contentIntent; @@ -860,7 +928,12 @@ public class StatusBarService extends IStatusBar.Stub Slog.e(TAG, "couldn't inflate view for package " + n.pkg, exception); return null; } - content.addView(child); + + // This will try to handle text color for all notifications from apps, applying the appropriate + // color if ID is possible, otherwise setting it to notification text color + startRecurse(child); + + content.addView(child); row.setDrawingCacheEnabled(true); @@ -870,6 +943,43 @@ public class StatusBarService extends IStatusBar.Stub return row; } + void startRecurse(View v) { + ViewGroup vg = (ViewGroup) v; + int childcount = vg.getChildCount(); + if (childcount > 0) { + int i; + for (i = 0; i < childcount; i++) { + try { + setViewColors((TextView) vg.getChildAt(i)); + } catch (Exception e) { } + try { + startRecurse((View) vg.getChildAt(i)); + } catch (Exception e) { } + } + } + } + + void setViewColors(TextView tv) { + int tvID = 0; + try { + tvID = tv.getId(); + switch (tvID) { + case com.android.internal.R.id.title: + tv.setTextColor(notificationTitleColor); + break; + case com.android.internal.R.id.text: + tv.setTextColor(notificationTextColor); + break; + case com.android.internal.R.id.time: + tv.setTextColor(notificationTimeColor); + break; + default: + tv.setTextColor(notificationTextColor); + } + } catch (Exception e) { } + } + + void addNotificationView(StatusBarNotification notification) { if (notification.view != null) { throw new RuntimeException("Assertion failed: notification.view=" @@ -1510,7 +1620,7 @@ public class StatusBarService extends IStatusBar.Stub Drawable bg; /// ---------- Tracking View -------------- - pixelFormat = PixelFormat.RGBX_8888; + pixelFormat = PixelFormat.TRANSLUCENT; bg = mTrackingView.getBackground(); if (bg != null) { pixelFormat = bg.getOpacity(); @@ -1730,6 +1840,17 @@ public class StatusBarService extends IStatusBar.Stub } } } + + private void updateColors() { + mDateView.setTextColor(Settings.System.getInt(mContext.getContentResolver(), Settings.System.DATE_COLOR, blackColor)); + mNoNotificationsTitle.setTextColor(Settings.System.getInt(mContext.getContentResolver(), Settings.System.NO_NOTIF_COLOR, whiteColor)); + mLatestTitle.setTextColor(Settings.System.getInt(mContext.getContentResolver(), Settings.System.LATEST_NOTIF_COLOR, whiteColor)); + mOngoingTitle.setTextColor(Settings.System.getInt(mContext.getContentResolver(), Settings.System.ONGOING_NOTIF_COLOR, whiteColor)); + mSpnLabel.setTextColor(Settings.System.getInt(mContext.getContentResolver(), Settings.System.SPN_LABEL_COLOR, blackColor)); + mPlmnLabel.setTextColor(Settings.System.getInt(mContext.getContentResolver(), Settings.System.PLMN_LABEL_COLOR, blackColor)); + mClearButton.setTextColor(Settings.System.getInt(mContext.getContentResolver(), Settings.System.CLEAR_BUTTON_LABEL_COLOR, blackColor)); + tickerView.updateColors(Settings.System.getInt(mContext.getContentResolver(), Settings.System.NEW_NOTIF_TICKER_COLOR, blackColor)); + } private View.OnClickListener mClearButtonListener = new View.OnClickListener() { public void onClick(View v) { @@ -1737,7 +1858,7 @@ public class StatusBarService extends IStatusBar.Stub addPendingOp(OP_EXPAND, null, false); } }; - + private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); @@ -1758,12 +1879,16 @@ public class StatusBarService extends IStatusBar.Stub }; void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) { + // Double carrier + mShowPlmnSb = (Settings.System.getInt(mContext.getContentResolver(), Settings.System.SHOW_PLMN_SB, 1) == 1); + mShowSpnSb = (Settings.System.getInt(mContext.getContentResolver(), Settings.System.SHOW_SPN_SB, 1) == 1); if (false) { Slog.d(TAG, "updateNetworkName showSpn=" + showSpn + " spn=" + spn + " showPlmn=" + showPlmn + " plmn=" + plmn); } boolean something = false; - if (showPlmn) { + // Double carrier - bcrook + if (showPlmn && mShowPlmnSb) { mPlmnLabel.setVisibility(View.VISIBLE); if (plmn != null) { mPlmnLabel.setText(plmn); @@ -1774,7 +1899,8 @@ public class StatusBarService extends IStatusBar.Stub mPlmnLabel.setText(""); mPlmnLabel.setVisibility(View.GONE); } - if (showSpn && spn != null) { + // Double carrier - bcrook, refinements from Wysie + if (showSpn && spn != null && mShowSpnSb) { mSpnLabel.setText(spn); mSpnLabel.setVisibility(View.VISIBLE); something = true; @@ -1878,4 +2004,38 @@ public class StatusBarService extends IStatusBar.Stub } } } + + private void getNotBarConfig() { + Resources res = mContext.getResources(); + /* + * Setup color and bar type for notification strip + */ + boolean useCustom = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.NOTIF_BAR_CUSTOM, 0) == 1; + notifBarColorMask = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.NOTIF_BAR_COLOR, whiteColor); + if (useCustom) { + custNotBar = true; + } else { + custNotBar = false; + } + /* + * Setup colors for expanded notification drawables + */ + boolean useCustomExp = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.NOTIF_EXPANDED_BAR_CUSTOM, 0) == 1; + expBarColorMask = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.NOTIF_EXPANDED_BAR_COLOR, whiteColor); + int noalpha = expBarColorMask | 0xFF000000; + if (useCustomExp) { + closerDrawable = res.getDrawable(com.android.internal.R.drawable.status_bar_close_on_cust); + expBarHeadDrawable = res.getDrawable(com.android.internal.R.drawable.status_bar_header_background_cust, + expBarColorMask, expPDMode); + expBarNotifTitleDrawable = res.getDrawable(com.android.internal.R.drawable.title_bar_portrait_cust, + noalpha, expPDMode); // always solid + custExpBar = true; + } else { + custExpBar = false; + } + } } diff --git a/services/java/com/android/server/status/TickerView.java b/services/java/com/android/server/status/TickerView.java index 099dffb..40e1462 100644 --- a/services/java/com/android/server/status/TickerView.java +++ b/services/java/com/android/server/status/TickerView.java @@ -19,11 +19,13 @@ package com.android.server.status; import android.content.Context; import android.util.AttributeSet; import android.widget.TextSwitcher; +import android.widget.TextView; public class TickerView extends TextSwitcher { Ticker mTicker; + private int textColor = 0xFF000000; public TickerView(Context context, AttributeSet attrs) { super(context, attrs); @@ -34,5 +36,24 @@ public class TickerView extends TextSwitcher super.onSizeChanged(w, h, oldw, oldh); mTicker.reflowText(); } + + @Override + public void setText(CharSequence text) { + final TextView t = (TextView) getNextView(); + t.setTextColor(textColor); + t.setText(text); + showNext(); + } + + @Override + public void setCurrentText(CharSequence text) { + final TextView t = (TextView) getCurrentView(); + t.setTextColor(textColor); + t.setText(text); + } + + public void updateColors(int color) { + textColor = color; + } } diff --git a/services/java/com/android/server/status/TrackingPatternView.java b/services/java/com/android/server/status/TrackingPatternView.java index 2c91aa4..46648af 100644 --- a/services/java/com/android/server/status/TrackingPatternView.java +++ b/services/java/com/android/server/status/TrackingPatternView.java @@ -17,7 +17,9 @@ package com.android.server.status; import android.content.Context; +import android.content.res.Resources; import android.content.res.TypedArray; +import android.provider.Settings; import android.util.AttributeSet; import android.util.Slog; import android.view.View; @@ -25,6 +27,10 @@ import android.graphics.BitmapFactory; import android.graphics.Bitmap; import android.graphics.Paint; import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.graphics.PorterDuff.Mode; +import android.graphics.drawable.BitmapDrawable; + public class TrackingPatternView extends View { private Bitmap mTexture; @@ -34,9 +40,7 @@ public class TrackingPatternView extends View { public TrackingPatternView(Context context, AttributeSet attrs) { super(context, attrs); - - mTexture = BitmapFactory.decodeResource(getResources(), - com.android.internal.R.drawable.status_bar_background); + setTexture(); mTextureWidth = mTexture.getWidth(); mTextureHeight = mTexture.getHeight(); @@ -67,4 +71,21 @@ public class TrackingPatternView extends View { x += textureWidth; } } + + private void setTexture() { + boolean useCustomExp = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.NOTIF_EXPANDED_BAR_CUSTOM, 0) == 1; + int expBarColorMask = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.NOTIF_EXPANDED_BAR_COLOR, 0xFF000000); + if (useCustomExp) { + Bitmap tempbm = BitmapFactory.decodeResource(getResources(), + com.android.internal.R.drawable.status_bar_background_cust); + Bitmap mutable = Bitmap.createBitmap(tempbm.getWidth(), tempbm.getHeight(), Bitmap.Config.ARGB_8888); + mutable.eraseColor(expBarColorMask); + mTexture = mutable; + } else { + mTexture = BitmapFactory.decodeResource(getResources(), + com.android.internal.R.drawable.status_bar_background); + } + } } diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java index 55d25a5..3824780 100644 --- a/telephony/java/android/telephony/PhoneNumberUtils.java +++ b/telephony/java/android/telephony/PhoneNumberUtils.java @@ -55,6 +55,12 @@ public class PhoneNumberUtils public static final char WILD = 'N'; /* + * Calling Line Identification Restriction (CLIR) + */ + private static final String CLIR_ON = "*31#+"; + private static final String CLIR_OFF = "#31#+"; + + /* * TOA = TON + NPI * See TS 24.008 section 10.5.4.7 for details. * These are the only really useful TOA values @@ -179,8 +185,6 @@ public class PhoneNumberUtils * Please note that the GSM wild character is allowed in the result. * This must be resolved before dialing. * - * Allows + only in the first position in the result string. - * * Returns null if phoneNumber == null */ public static String @@ -203,6 +207,11 @@ public class PhoneNumberUtils } } + int pos = addPlusChar(phoneNumber); + if (pos >= 0 && ret.length() > pos) { + ret.insert(pos, '+'); + } + return ret.toString(); } @@ -304,6 +313,28 @@ public class PhoneNumberUtils } } + /** GSM codes + * Finds if a GSM code includes the international prefix (+). + * + * @param number the number to dial. + * + * @return the position where the + char will be inserted, -1 if the GSM code was not found. + */ + private static int + addPlusChar(String number) { + int pos = -1; + + if (number.startsWith(CLIR_OFF)) { + pos = CLIR_OFF.length() - 1; + } + + if (number.startsWith(CLIR_ON)) { + pos = CLIR_ON.length() - 1; + } + + return pos; + } + /** * Extracts the post-dial sequence of DTMF control digits, pauses, and * waits. Strips separators. This string may be empty, but will not be null diff --git a/telephony/java/com/android/internal/telephony/AdnRecordCache.java b/telephony/java/com/android/internal/telephony/AdnRecordCache.java index c8c0658..a175d49 100644 --- a/telephony/java/com/android/internal/telephony/AdnRecordCache.java +++ b/telephony/java/com/android/internal/telephony/AdnRecordCache.java @@ -186,7 +186,12 @@ public final class AdnRecordCache extends Handler implements IccConstants { } ArrayList<AdnRecord> oldAdnList; - oldAdnList = getRecordsIfLoaded(efid); + + if (efid == EF_PBR) { + oldAdnList = mUsimPhoneBookManager.loadEfFilesFromUsim(); + } else { + oldAdnList = getRecordsIfLoaded(efid); + } if (oldAdnList == null) { sendErrorResponse(response, "Adn list not exist for EF:" + efid); @@ -208,6 +213,17 @@ public final class AdnRecordCache extends Handler implements IccConstants { return; } + if (efid == EF_PBR) { + AdnRecord foundAdn = oldAdnList.get(index-1); + efid = foundAdn.efid; + extensionEF = foundAdn.extRecord; + index = foundAdn.recordNumber; + + newAdn.efid = efid; + newAdn.extRecord = extensionEF; + newAdn.recordNumber = index; + } + Message pendingResponse = userWriteResponse.get(efid); if (pendingResponse != null) { @@ -331,6 +347,7 @@ public final class AdnRecordCache extends Handler implements IccConstants { if (ar.exception == null) { adnLikeFiles.get(efid).set(index - 1, adn); + mUsimPhoneBookManager.invalidateCache(); } Message response = userWriteResponse.get(efid); diff --git a/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java b/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java index 9f8e57f..176b087 100644 --- a/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java +++ b/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java @@ -144,6 +144,9 @@ public abstract class IccPhoneBookInterfaceManager extends IIccPhoneBook.Stub { if (DBG) logd("updateAdnRecordsInEfBySearch: efid=" + efid + " ("+ oldTag + "," + oldPhoneNumber + ")"+ "==>" + " ("+ newTag + "," + newPhoneNumber + ")"+ " pin2=" + pin2); + + efid = updateEfForIccType(efid); + synchronized(mLock) { checkThread(); success = false; diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java index 627d94d..92a6173 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java @@ -443,7 +443,8 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { if ((state == State.IDLE || state == State.SCANNING) && (gprsState == ServiceState.STATE_IN_SERVICE || noAutoAttach) && mGsmPhone.mSIMRecords.getRecordsLoaded() - && phone.getState() == Phone.State.IDLE + && (mGsmPhone.mSST.isConcurrentVoiceAndData() || + phone.getState() == Phone.State.IDLE ) && isDataAllowed() && !mIsPsRestricted && desiredPowerState ) { diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java b/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java index bcbd127..6a65b87 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java @@ -124,8 +124,12 @@ public final class GsmMmiCode extends Handler implements MmiCode { // See TS 22.030 6.5.2 "Structure of the MMI" + // Note that some nonstandard MMI codes seem to end with an asterisk, eg. "#190*1660#*" + // (http://code.google.com/p/android/issues/detail?id=2226). Make sure this doesn't get + // merged into MATCH_GROUP_DIALING_NUMBER. + static Pattern sPatternSuppService = Pattern.compile( - "((\\*|#|\\*#|\\*\\*|##)(\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*))?)?)?)?#)(.*)"); + "((\\*|#|\\*#|\\*\\*|##)(\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*))?)?)?)?#\\*?)(.*)"); /* 1 2 3 4 5 6 7 8 9 10 11 12 1 = Full string up to and including # @@ -134,7 +138,7 @@ public final class GsmMmiCode extends Handler implements MmiCode { 5 = SIA 7 = SIB 9 = SIC - 10 = dialing number + 12 = dialing number */ static final int MATCH_GROUP_POUND_STRING = 1; diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java index d711a80..b4e7b63 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java +++ b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java @@ -559,6 +559,13 @@ public final class SIMRecords extends IccRecords { break; case EVENT_GET_CPHS_MAILBOX_DONE: case EVENT_GET_MBDN_DONE: + //Resetting the voice mail number and voice mail tag to null + //as these should be updated from the data read from EF_MBDN. + //If they are not reset, incase of invalid data/exception these + //variables are retaining their previous values and are + //causing invalid voice mailbox info display to user. + voiceMailNum = null; + voiceMailTag = null; isRecordLoadResponse = true; ar = (AsyncResult)msg.obj; diff --git a/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java b/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java index 41e527c..b642541 100644..100755 --- a/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java +++ b/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java @@ -53,6 +53,7 @@ public class UsimPhoneBookManager extends Handler implements IccConstants { private ArrayList<byte[]> mIapFileRecord; private ArrayList<byte[]> mEmailFileRecord; private Map<Integer, ArrayList<String>> mEmailsForAdnRec; + private boolean mRefreshCache = false; private static final int EVENT_PBR_LOAD_DONE = 1; private static final int EVENT_USIM_ADN_LOAD_DONE = 2; @@ -91,11 +92,19 @@ public class UsimPhoneBookManager extends Handler implements IccConstants { mEmailFileRecord = null; mPbrFile = null; mIsPbrPresent = true; + mRefreshCache = false; } public ArrayList<AdnRecord> loadEfFilesFromUsim() { synchronized (mLock) { - if (!mPhoneBookRecords.isEmpty()) return mPhoneBookRecords; + if (!mPhoneBookRecords.isEmpty()) { + if (mRefreshCache) { + mRefreshCache = false; + refreshCache(); + } + return mPhoneBookRecords; + } + if (!mIsPbrPresent) return null; // Check if the PBR file is present in the cache, if not read it @@ -116,6 +125,20 @@ public class UsimPhoneBookManager extends Handler implements IccConstants { return mPhoneBookRecords; } + private void refreshCache() { + if (mPbrFile == null) return; + mPhoneBookRecords.clear(); + + int numRecs = mPbrFile.mFileIds.size(); + for (int i = 0; i < numRecs; i++) { + readAdnFileAndWait(i); + } + } + + public void invalidateCache() { + mRefreshCache = true; + } + private void readPbrFileAndWait() { mPhone.getIccFileHandler().loadEFLinearFixedAll(EF_PBR, obtainMessage(EVENT_PBR_LOAD_DONE)); try { diff --git a/tests/CoreTests/android/AndroidManifest.xml b/tests/CoreTests/android/AndroidManifest.xml index f02673c..8331f0c 100644 --- a/tests/CoreTests/android/AndroidManifest.xml +++ b/tests/CoreTests/android/AndroidManifest.xml @@ -24,6 +24,7 @@ <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" /> <uses-permission android:name="android.permission.WRITE_APN_SETTINGS" /> <uses-permission android:name="android.permission.BROADCAST_STICKY" /> + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- location test permissions --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> diff --git a/tests/CoreTests/android/core/HttpHeaderTest.java b/tests/CoreTests/android/core/HttpHeaderTest.java new file mode 100644 index 0000000..a5d4857 --- /dev/null +++ b/tests/CoreTests/android/core/HttpHeaderTest.java @@ -0,0 +1,62 @@ +/* + * 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.core; + +import android.test.AndroidTestCase; +import org.apache.http.util.CharArrayBuffer; + +import android.net.http.Headers; + +public class HttpHeaderTest extends AndroidTestCase { + + static final String LAST_MODIFIED = "Last-Modified: Fri, 18 Jun 2010 09:56:47 GMT"; + static final String CACHE_CONTROL_MAX_AGE = "Cache-Control:max-age=15"; + static final String CACHE_CONTROL_PRIVATE = "Cache-Control: private"; + + /** + * Tests that cache control header supports multiple instances of the header, + * according to HTTP specification. + * + * The HTTP specification states the following about the fields: + * Multiple message-header fields with the same field-name MAY be present + * in a message if and only if the entire field-value for that header field + * is defined as a comma-separated list [i.e., #(values)]. It MUST be + * possible to combine the multiple header fields into one "field-name: + * field-value" pair, without changing the semantics of the message, by + * appending each subsequent field-value to the first, each separated by a + * comma. The order in which header fields with the same field-name are + * received is therefore significant to the interpretation of the combined + * field value, and thus a proxy MUST NOT change the order of these field + * values when a message is forwarded. + */ + public void testCacheControl() throws Exception { + Headers h = new Headers(); + CharArrayBuffer buffer = new CharArrayBuffer(64); + + buffer.append(CACHE_CONTROL_MAX_AGE); + h.parseHeader(buffer); + + buffer.clear(); + buffer.append(LAST_MODIFIED); + h.parseHeader(buffer); + assertEquals("max-age=15", h.getCacheControl()); + + buffer.clear(); + buffer.append(CACHE_CONTROL_PRIVATE); + h.parseHeader(buffer); + assertEquals("max-age=15,private", h.getCacheControl()); + } +} diff --git a/tests/CoreTests/android/core/ProxyTest.java b/tests/CoreTests/android/core/ProxyTest.java new file mode 100644 index 0000000..12acfe8 --- /dev/null +++ b/tests/CoreTests/android/core/ProxyTest.java @@ -0,0 +1,93 @@ +/* + * 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.core; + +import org.apache.http.HttpHost; + +import android.content.Context; +import android.net.Proxy; +import android.test.AndroidTestCase; + +/** + * Proxy tests + */ +public class ProxyTest extends AndroidTestCase { + private Context mContext; + private HttpHost mHttpHost; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + mContext = getContext(); + mHttpHost = null; + String proxyHost = Proxy.getHost(mContext); + int proxyPort = Proxy.getPort(mContext); + if (proxyHost != null) { + mHttpHost = new HttpHost(proxyHost, proxyPort, "http"); + } + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + + /** + * Bad url parameter should not cause any exception. + */ + public void testProxyGetPreferredHttpHost_UrlBad() throws Exception { + assertEquals(mHttpHost, Proxy.getPreferredHttpHost(mContext, null)); + assertEquals(mHttpHost, Proxy.getPreferredHttpHost(mContext, "")); + assertEquals(mHttpHost, Proxy.getPreferredHttpHost(mContext, "bad:")); + assertEquals(mHttpHost, Proxy.getPreferredHttpHost(mContext, "bad")); + assertEquals(mHttpHost, Proxy.getPreferredHttpHost(mContext, "bad:\\")); + assertEquals(mHttpHost, Proxy.getPreferredHttpHost(mContext, "bad://#")); + assertEquals(mHttpHost, Proxy.getPreferredHttpHost(mContext, "://#")); + } + + /** + * Proxy (if available) should be returned when url parameter is not localhost. + */ + public void testProxyGetPreferredHttpHost_UrlNotlLocalhost() throws Exception { + assertEquals(mHttpHost, Proxy.getPreferredHttpHost(mContext, "http://")); + assertEquals(mHttpHost, Proxy.getPreferredHttpHost(mContext, "http://example.com")); + assertEquals(mHttpHost, Proxy.getPreferredHttpHost(mContext, "http://example.com/")); + assertEquals(mHttpHost, Proxy.getPreferredHttpHost(mContext, "http://192.168.0.1/")); + assertEquals(mHttpHost, Proxy.getPreferredHttpHost(mContext, "file:///foo/bar")); + assertEquals(mHttpHost, Proxy.getPreferredHttpHost(mContext, "rtsp://example.com")); + assertEquals(mHttpHost, Proxy.getPreferredHttpHost(mContext, "rtsp://example.com/")); + assertEquals(mHttpHost, Proxy.getPreferredHttpHost(mContext, "javascript:alert(1)")); + } + + /** + * No proxy should be returned when url parameter is localhost. + */ + public void testProxyGetPreferredHttpHost_UrlLocalhost() throws Exception { + assertNull(Proxy.getPreferredHttpHost(mContext, "http://localhost")); + assertNull(Proxy.getPreferredHttpHost(mContext, "http://localhost/")); + assertNull(Proxy.getPreferredHttpHost(mContext, "http://localhost/hej.html")); + assertNull(Proxy.getPreferredHttpHost(mContext, "http://127.0.0.1")); + assertNull(Proxy.getPreferredHttpHost(mContext, "http://127.0.0.1/")); + assertNull(Proxy.getPreferredHttpHost(mContext, "http://127.0.0.1/hej.html")); + assertNull(Proxy.getPreferredHttpHost(mContext, "http://127.0.0.1:80/")); + assertNull(Proxy.getPreferredHttpHost(mContext, "http://127.0.0.1:8080/")); + assertNull(Proxy.getPreferredHttpHost(mContext, "rtsp://127.0.0.1/")); + assertNull(Proxy.getPreferredHttpHost(mContext, "rtsp://localhost/")); + assertNull(Proxy.getPreferredHttpHost(mContext, "https://localhost/")); + } +} diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index a2f085a..a84492d 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -12,6 +12,8 @@ #include <utils/ResourceTypes.h> #include <stdarg.h> +#include <set> + #define NOISY(x) //x status_t compileXmlFile(const sp<AaptAssets>& assets, @@ -2516,6 +2518,34 @@ ResourceFilter::match(const ResTable_config& config) return true; } +class entry_sort_t { +public: + sp<ResourceTable::Package> p; + sp<ResourceTable::Type> t; + sp<ResourceTable::ConfigList> c; + ResourceTable::ConfigDescription config; + sp<ResourceTable::Entry> e; + + entry_sort_t() { } + entry_sort_t( + sp<ResourceTable::Package> p, + sp<ResourceTable::Type> t, + sp<ResourceTable::ConfigList> c, + const ResourceTable::ConfigDescription &config, + sp<ResourceTable::Entry> e) + : p(p), t(t), c(c), config(config), e(e) { } + + bool operator < (const entry_sort_t &o) const { + int cmp; + if ((cmp = compare_type(config, o.config))) return cmp < 0; + if ((cmp = compare_type(t, o.t))) return cmp < 0; + if ((cmp = compare_type(e, o.e))) return cmp < 0; + if ((cmp = compare_type(c, o.c))) return cmp < 0; + if ((cmp = compare_type(p, o.p))) return cmp < 0; + return false; + } +}; + status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) { ResourceFilter filter; @@ -2528,6 +2558,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) size_t pi; bool useUTF8 = !bundle->getWantUTF16() && bundle->isMinSdkAtLeast(SDK_FROYO); + std::set< entry_sort_t > sortedEntries; // Iterate through all data, collecting all values (strings, // references, etc). @@ -2568,10 +2599,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) continue; } e->setNameIndex(keyStrings.add(e->getName(), true)); - status_t err = e->prepareFlatten(&valueStrings, this); - if (err != NO_ERROR) { - return err; - } + sortedEntries.insert(entry_sort_t(p, t, c, config, e)); } } } @@ -2580,6 +2608,16 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) p->setKeyStrings(keyStrings.createStringBlock()); } + for (std::set< entry_sort_t >::iterator ei=sortedEntries.begin(); ei!=sortedEntries.end(); ++ei) { + sp<Entry> e = ei->e; + status_t err = e->prepareFlatten(&valueStrings, this); + if (err != NO_ERROR) { + return err; + } + } + + sortedEntries.clear(); + ssize_t strAmt = 0; // Now build the array of package chunks. diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas.java b/tools/layoutlib/bridge/src/android/graphics/Canvas.java index d5d315e..d75ec79 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Canvas.java +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas.java @@ -49,6 +49,8 @@ import javax.microedition.khronos.opengles.GL; */ public class Canvas extends _Original_Canvas { + private static final char FIRST_RIGHT_TO_LEFT = '\u0590'; + private static final char LAST_RIGHT_TO_LEFT = '\u07b1'; private BufferedImage mBufferedImage; private final Stack<Graphics2D> mGraphicsStack = new Stack<Graphics2D>(); private final ILayoutLog mLogger; @@ -644,11 +646,8 @@ public class Canvas extends _Original_Canvas { getGraphics2d().scale(sx, sy); } - /* (non-Javadoc) - * @see android.graphics.Canvas#drawText(char[], int, int, float, float, android.graphics.Paint) - */ - @Override - public void drawText(char[] text, int index, int count, float x, float y, Paint paint) { + + public void drawText(char[] text, int index, int count, float x, float y, Paint paint, boolean bidi) { // WARNING: the logic in this method is similar to Paint.measureText. // Any change to this method should be reflected in Paint.measureText Graphics2D g = getGraphics2d(); @@ -681,21 +680,29 @@ public class Canvas extends _Original_Canvas { FontInfo mainFont = fonts.get(0); int i = index; int lastIndex = index + count; + char[] bidiText; + if (bidi) { + bidiText=bidiProcess(text,index,count); + i=0; + lastIndex=count; + } else { + bidiText=text; + } while (i < lastIndex) { // always start with the main font. - int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex); + int upTo = mainFont.mFont.canDisplayUpTo(bidiText, i, lastIndex); if (upTo == -1) { // draw all the rest and exit. g.setFont(mainFont.mFont); - g.drawChars(text, i, lastIndex - i, (int)x, (int)y); + g.drawChars(bidiText, i, lastIndex - i, (int)x, (int)y); return; } else if (upTo > 0) { // draw what's possible g.setFont(mainFont.mFont); - g.drawChars(text, i, upTo - i, (int)x, (int)y); + g.drawChars(bidiText, i, upTo - i, (int)x, (int)y); // compute the width that was drawn to increase x - x += mainFont.mMetrics.charsWidth(text, i, upTo - i); + x += mainFont.mMetrics.charsWidth(bidiText, i, upTo - i); // move index to the first non displayed char. i = upTo; @@ -715,15 +722,15 @@ public class Canvas extends _Original_Canvas { // need to check that the font can display the character. We test // differently if the char is a high surrogate. - int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; - upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount); + int charCount = Character.isHighSurrogate(bidiText[i]) ? 2 : 1; + upTo = fontInfo.mFont.canDisplayUpTo(bidiText, i, i + charCount); if (upTo == -1) { // draw that char g.setFont(fontInfo.mFont); - g.drawChars(text, i, charCount, (int)x, (int)y); + g.drawChars(bidiText, i, charCount, (int)x, (int)y); // update x - x += fontInfo.mMetrics.charsWidth(text, i, charCount); + x += fontInfo.mMetrics.charsWidth(bidiText, i, charCount); // update the index in the text, and move on i += charCount; @@ -736,13 +743,13 @@ public class Canvas extends _Original_Canvas { // in case no font can display the char, display it with the main font. // (it'll put a square probably) if (foundFont == false) { - int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; + int charCount = Character.isHighSurrogate(bidiText[i]) ? 2 : 1; g.setFont(mainFont.mFont); - g.drawChars(text, i, charCount, (int)x, (int)y); + g.drawChars(bidiText, i, charCount, (int)x, (int)y); // measure it to advance x - x += mainFont.mMetrics.charsWidth(text, i, charCount); + x += mainFont.mMetrics.charsWidth(bidiText, i, charCount); // and move to the next chars. i += charCount; @@ -755,6 +762,13 @@ public class Canvas extends _Original_Canvas { } /* (non-Javadoc) + * @see android.graphics.Canvas#drawText(char[], int, int, float, float, android.graphics.Paint) + */ + @Override + public void drawText(char[] text, int index, int count, float x, float y, Paint paint) { + drawText(text, index, count, x, y, paint, true); + } + /* (non-Javadoc) * @see android.graphics.Canvas#drawText(java.lang.CharSequence, int, int, float, float, android.graphics.Paint) */ @Override @@ -1140,7 +1154,10 @@ public class Canvas extends _Original_Canvas { public void drawTextOnPath(char[] text, int index, int count, Path path, float offset, float offset2, Paint paint) { // TODO Auto-generated method stub - super.drawTextOnPath(text, index, count, path, offset, offset2, paint); + int i = 0; + char[] bidiText; + bidiText=bidiProcess(text,index,count); + super.drawTextOnPath(bidiText, i, count, path, offset, offset2, paint); } /* (non-Javadoc) @@ -1149,7 +1166,10 @@ public class Canvas extends _Original_Canvas { @Override public void drawTextOnPath(String text, Path path, float offset, float offset2, Paint paint) { // TODO Auto-generated method stub - super.drawTextOnPath(text, path, offset, offset2, paint); + int i = 0; + String bidiText; + bidiText=new String(bidiProcess(text.toCharArray(),0,text.length())); + super.drawTextOnPath(bidiText, path, offset, offset2, paint); } /* (non-Javadoc) diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint.java b/tools/layoutlib/bridge/src/android/graphics/Paint.java index 619ab30..d13b5fe 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Paint.java +++ b/tools/layoutlib/bridge/src/android/graphics/Paint.java @@ -283,6 +283,8 @@ public class Paint extends _Original_Paint { mStyle = src.mStyle; mFlags = src.mFlags; + updateFontObject(); + super.set(src); } } diff --git a/vpn/java/android/net/vpn/OpenvpnProfile.java b/vpn/java/android/net/vpn/OpenvpnProfile.java new file mode 100644 index 0000000..f1fd2f6 --- /dev/null +++ b/vpn/java/android/net/vpn/OpenvpnProfile.java @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.vpn; + +import android.os.Parcel; + +/** + * The profile for Openvpn type of VPN. + * {@hide} + */ +public class OpenvpnProfile extends VpnProfile { + private static final long serialVersionUID = 1L; + + private static final String PROTO_UDP = "udp"; + + private static final String PROTO_TCP = "tcp"; + + private static final String DEVICE_TUN = "tun"; + + private static final String DEVICE_TAP = "tap"; + + // Standard Settings + private boolean mUserAuth = false; + + private String mCA; + + private String mCert; + + // Advanced Settings + private int mPort = 1194; + + private String mProto = PROTO_UDP; + + private boolean mUseCompLzo = false; + + private boolean mSupplyAddr = false; + + private boolean mRedirectGateway = false; + + private String mLocalAddr; + + private String mRemoteAddr; + + private String mDevice = DEVICE_TUN; + + private String mCipher; + + private int mKeySize; + + private String mExtra; + + @Override + public VpnType getType() { + return VpnType.OPENVPN; + } + + public void setPort(String port) { + try { + mPort = Integer.parseInt(port); + } catch (NumberFormatException e) { + // no update + } + } + + public String getPort() { + return Integer.toString(mPort); + } + + public String getProto() { + return mProto; + } + + public CharSequence[] getProtoList() { + String[] s = new String[2]; + s[0] = PROTO_UDP; + s[1] = PROTO_TCP; + return s; + } + + public void setProto(String p) { + if (p.contains(PROTO_TCP)) + mProto = PROTO_TCP; + else if (p.contains(PROTO_UDP)) + mProto = PROTO_UDP; + } + + public String getDevice() { + return mDevice; + } + + public CharSequence[] getDeviceList() { + String[] s = new String[2]; + s[0] = DEVICE_TUN; + s[1] = DEVICE_TAP; + return s; + } + + public void setDevice(String p) { + if (p.contains(DEVICE_TAP)) + mDevice = DEVICE_TAP; + else if (p.contains(DEVICE_TUN)) + mDevice = DEVICE_TUN; + } + + public boolean getUserAuth() { + return mUserAuth; + } + + public void setUserAuth(boolean auth) { + mUserAuth = auth; + } + + public String getCAName() { + return mCA; + } + + public void setCAName(String name) { + mCA = name; + } + + public String getCertName() { + return mCert; + } + + public void setCertName(String name) { + mCert = name; + } + + public void setUseCompLzo(boolean b) { + mUseCompLzo = b; + } + + public boolean getUseCompLzo() { + return mUseCompLzo; + } + + public void setRedirectGateway(boolean b) { + mRedirectGateway = b; + } + + public boolean getRedirectGateway() { + return mRedirectGateway; + } + + public void setSupplyAddr(boolean b) { + mSupplyAddr = b; + } + + public boolean getSupplyAddr() { + return mSupplyAddr; + } + + public void setLocalAddr(String addr) { + mLocalAddr = addr; + } + + public String getLocalAddr() { + return mLocalAddr; + } + + public void setRemoteAddr(String addr) { + mRemoteAddr = addr; + } + + public String getRemoteAddr() { + return mRemoteAddr; + } + + public void setCipher(String cipher) { + mCipher = cipher; + } + + public String getCipher() { + return mCipher; + } + + public void setKeySize(String keysize) { + try { + if (keysize.equals("0")) + mKeySize = 0; + else + mKeySize = Integer.parseInt(keysize); + } catch (NumberFormatException e) { + // no update + } + } + + public String getKeySize() { + return Integer.toString(mKeySize); + } + + public void setExtra(String extra) { + mExtra = extra; + } + + public String getExtra() { + return mExtra; + } + + @Override + protected void readFromParcel(Parcel in) { + super.readFromParcel(in); + mPort = in.readInt(); + mProto = in.readString(); + mUserAuth = in.readInt() == 1; + mCA = in.readString(); + mCert = in.readString(); + mUseCompLzo = in.readInt() == 1; + mRedirectGateway = in.readInt() == 1; + mSupplyAddr = in.readInt() == 1; + mLocalAddr = in.readString(); + mRemoteAddr = in.readString(); + mDevice = in.readString(); + mCipher = in.readString(); + mKeySize = in.readInt(); + mExtra = in.readString(); + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + super.writeToParcel(parcel, flags); + parcel.writeInt(mPort); + parcel.writeString(mProto); + parcel.writeInt(mUserAuth ? 1 : 0); + parcel.writeString(mCA); + parcel.writeString(mCert); + parcel.writeInt(mUseCompLzo ? 1 : 0); + parcel.writeInt(mRedirectGateway ? 1 : 0); + parcel.writeInt(mSupplyAddr ? 1 : 0); + parcel.writeString(mLocalAddr); + parcel.writeString(mRemoteAddr); + parcel.writeString(mDevice); + parcel.writeString(mCipher); + parcel.writeInt(mKeySize); + parcel.writeString(mExtra); + } +} diff --git a/vpn/java/android/net/vpn/VpnManager.java b/vpn/java/android/net/vpn/VpnManager.java index ce522c8..c588076 100644 --- a/vpn/java/android/net/vpn/VpnManager.java +++ b/vpn/java/android/net/vpn/VpnManager.java @@ -69,7 +69,7 @@ public class VpnManager { /** Error code to indicate a successful connection. */ public static final int VPN_ERROR_NO_ERROR = 0; - public static final String PROFILES_PATH = "/misc/vpn/profiles"; + public static final String PROFILES_PATH = "/data/misc/vpn/profiles"; private static final String PACKAGE_PREFIX = VpnManager.class.getPackage().getName() + "."; diff --git a/vpn/java/android/net/vpn/VpnType.java b/vpn/java/android/net/vpn/VpnType.java index 356f8b1..2157067 100644 --- a/vpn/java/android/net/vpn/VpnType.java +++ b/vpn/java/android/net/vpn/VpnType.java @@ -28,7 +28,8 @@ public enum VpnType { L2TP_IPSEC_PSK("L2TP/IPSec PSK", R.string.l2tp_ipsec_psk_vpn_description, L2tpIpsecPskProfile.class), L2TP_IPSEC("L2TP/IPSec CRT", R.string.l2tp_ipsec_crt_vpn_description, - L2tpIpsecProfile.class); + L2tpIpsecProfile.class), + OPENVPN("OpenVPN", R.string.openvpn_vpn_description, OpenvpnProfile.class); private String mDisplayName; private int mDescriptionId; diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java index f98cd28..f4afc36 100644 --- a/wifi/java/android/net/wifi/WifiNative.java +++ b/wifi/java/android/net/wifi/WifiNative.java @@ -16,8 +16,6 @@ package android.net.wifi; -import android.net.DhcpInfo; - /** * Native calls for sending requests to the supplicant daemon, and for * receiving asynchronous events. All methods of the form "xxxxCommand()" @@ -143,10 +141,6 @@ public class WifiNative { public native static boolean clearBlacklistCommand(); - public native static boolean doDhcpRequest(DhcpInfo results); - - public native static String getDhcpError(); - /** * Wait for the supplicant to send an event, returning the event string. * @return the event string sent by the supplicant. diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java index 3813015..542a0ac 100644 --- a/wifi/java/android/net/wifi/WifiStateTracker.java +++ b/wifi/java/android/net/wifi/WifiStateTracker.java @@ -69,7 +69,7 @@ import java.util.concurrent.atomic.AtomicInteger; public class WifiStateTracker extends NetworkStateTracker { private static final boolean LOCAL_LOGD = Config.LOGD || false; - + private static final String TAG = "WifiStateTracker"; // Event log tags (must be in sync with event-log-tags) @@ -216,12 +216,12 @@ public class WifiStateTracker extends NetworkStateTracker { private boolean mUseStaticIp = false; private int mReconnectCount; - // used to store the (non-persisted) num determined during device boot + // used to store the (non-persisted) num determined during device boot // (from mcc or other phone info) before the driver is started. private int mNumAllowedChannels = 0; // 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. @@ -273,7 +273,7 @@ public class WifiStateTracker extends NetworkStateTracker { * Observes the static IP address settings. */ private SettingsObserver mSettingsObserver; - + private boolean mIsScanModeActive; private boolean mEnableRssiPolling; @@ -313,8 +313,6 @@ public class WifiStateTracker extends NetworkStateTracker { private String mInterfaceName; private static String LS = System.getProperty("line.separator"); - private static String[] sDnsPropNames; - /** * A structure for supplying information about a supplicant state * change in the STATE_CHANGE event message that comes from the @@ -350,7 +348,7 @@ public class WifiStateTracker extends NetworkStateTracker { public WifiStateTracker(Context context, Handler target) { super(context, target, ConnectivityManager.TYPE_WIFI, 0, "WIFI", ""); - + mWifiInfo = new WifiInfo(); mWifiMonitor = new WifiMonitor(this); mHaveIpAddress = false; @@ -363,7 +361,7 @@ public class WifiStateTracker extends NetworkStateTracker { mRunState = RUN_STATE_STARTING; // Setting is in seconds - NOTIFICATION_REPEAT_DELAY_MS = Settings.Secure.getInt(context.getContentResolver(), + 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(); @@ -371,9 +369,9 @@ public class WifiStateTracker extends NetworkStateTracker { mSettingsObserver = new SettingsObserver(new Handler()); mInterfaceName = SystemProperties.get("wifi.interface", "tiwlan0"); - sDnsPropNames = new String[] { - "dhcp." + mInterfaceName + ".dns1", - "dhcp." + mInterfaceName + ".dns2" + mDnsPropNames = new String[] { + "net." + mInterfaceName + ".dns1", + "net." + mInterfaceName + ".dns2" }; mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo")); @@ -418,15 +416,6 @@ public class WifiStateTracker extends NetworkStateTracker { } /** - * Return the IP addresses of the DNS servers available for the WLAN - * network interface. - * @return a list of DNS addresses, with no holes. - */ - public String[] getNameServers() { - return getNameServerList(sDnsPropNames); - } - - /** * Return the name of our WLAN network interface. * @return the name of our interface. */ @@ -994,10 +983,10 @@ public class WifiStateTracker extends NetworkStateTracker { // Wi-Fi network state changed: // [31- 6] Reserved for future use - // [ 5- 0] Detailed state ordinal (as defined by NetworkInfo.DetailedState) + // [ 5- 0] Detailed state ordinal (as defined by NetworkInfo.DetailedState) eventLogParam = (result.state.ordinal() & 0x3f); EventLog.writeEvent(EVENTLOG_NETWORK_STATE_CHANGED, eventLogParam); - + if (LOCAL_LOGD) Log.v(TAG, "New network state is " + result.state); /* * If we're in scan-only mode, don't advance the state machine, and @@ -1085,7 +1074,7 @@ public class WifiStateTracker extends NetworkStateTracker { checkPollTimer(); } break; - + case EVENT_DEFERRED_DISCONNECT: if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) { handleDisconnectedState(DetailedState.DISCONNECTED, true); @@ -1137,7 +1126,7 @@ public class WifiStateTracker extends NetworkStateTracker { if (LOCAL_LOGD) Log.v(TAG, "IP configuration: " + mDhcpInfo); // Wi-Fi interface configuration state changed: // [31- 1] Reserved for future use - // [ 0- 0] Interface configuration succeeded (1) or failed (0) + // [ 0- 0] Interface configuration succeeded (1) or failed (0) EventLog.writeEvent(EVENTLOG_INTERFACE_CONFIGURATION_STATE_CHANGED, 1); // We've connected successfully, so allow the notification again in the future @@ -1357,7 +1346,7 @@ public class WifiStateTracker extends NetworkStateTracker { int netId = -1; String[] lines = reply.split("\n"); for (String line : lines) { - String[] prop = line.split(" *= *"); + String[] prop = line.split(" *= *", 2); if (prop.length < 2) continue; String name = prop[0]; @@ -2094,7 +2083,7 @@ public class WifiStateTracker extends NetworkStateTracker { numOpenNetworks++; } } - + if (numOpenNetworks > 0) { if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) { /* @@ -2110,7 +2099,7 @@ public class WifiStateTracker extends NetworkStateTracker { } } } - + // No open networks in range, remove the notification setNotificationVisible(false, 0, false, 0); } @@ -2125,13 +2114,13 @@ public class WifiStateTracker extends NetworkStateTracker { * visible or invisible. */ public 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; @@ -2139,12 +2128,12 @@ public class WifiStateTracker extends NetworkStateTracker { 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 @@ -2163,22 +2152,22 @@ public class WifiStateTracker extends NetworkStateTracker { 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 = mTarget.obtainMessage(EVENT_NOTIFICATION_CHANGED, 1, ICON_NETWORKS_AVAILABLE, mNotification); - + } else { // Remove any pending messages to show the notification mTarget.removeMessages(EVENT_NOTIFICATION_CHANGED, mNotification); - + message = mTarget.obtainMessage(EVENT_NOTIFICATION_CHANGED, 0, ICON_NETWORKS_AVAILABLE); } mTarget.sendMessageDelayed(message, delay); - + mNotificationShown = visible; } @@ -2193,7 +2182,7 @@ public class WifiStateTracker extends NetworkStateTracker { mNotificationRepeatTime = 0; mNumScansSinceNetworkStateChange = 0; } - + @Override public String toString() { StringBuffer sb = new StringBuffer(); @@ -2217,7 +2206,7 @@ public class WifiStateTracker extends NetworkStateTracker { private class DhcpHandler extends Handler { private Handler mTarget; - + /** * Whether to skip the DHCP result callback to the target. For example, * this could be set if the network we were requesting an IP for has @@ -2237,11 +2226,11 @@ public class WifiStateTracker extends NetworkStateTracker { * in an error state and we will not disable coexistence. */ private BluetoothHeadset mBluetoothHeadset; - + public DhcpHandler(Looper looper, Handler target) { super(looper); mTarget = target; - + mBluetoothHeadset = new BluetoothHeadset(mContext, null); } @@ -2250,7 +2239,7 @@ public class WifiStateTracker extends NetworkStateTracker { switch (msg.what) { case EVENT_DHCP_START: - + boolean modifiedBluetoothCoexistenceMode = false; if (shouldDisableCoexistenceMode()) { /* @@ -2319,7 +2308,7 @@ public class WifiStateTracker extends NetworkStateTracker { * 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() { @@ -2327,7 +2316,7 @@ public class WifiStateTracker extends NetworkStateTracker { return state == BluetoothHeadset.STATE_DISCONNECTED; } } - + private void checkUseStaticIp() { mUseStaticIp = false; final ContentResolver cr = mContext.getContentResolver(); |