diff options
172 files changed, 7525 insertions, 2795 deletions
@@ -82,6 +82,7 @@ LOCAL_SRC_FILES += \ core/java/android/app/IWallpaperService.aidl \ core/java/android/app/IWallpaperServiceCallback.aidl \ core/java/android/backup/IBackupManager.aidl \ + core/java/android/backup/IRestoreObserver.aidl \ core/java/android/backup/IRestoreSession.aidl \ core/java/android/bluetooth/IBluetoothA2dp.aidl \ core/java/android/bluetooth/IBluetoothDevice.aidl \ diff --git a/api/current.xml b/api/current.xml index b28780c..3c58ded 100644 --- a/api/current.xml +++ b/api/current.xml @@ -3452,50 +3452,6 @@ visibility="public" > </field> -<field name="donut_resource_pad26" - type="int" - transient="false" - volatile="false" - value="16843398" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="donut_resource_pad27" - type="int" - transient="false" - volatile="false" - value="16843397" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="donut_resource_pad28" - type="int" - transient="false" - volatile="false" - value="16843396" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> -<field name="donut_resource_pad29" - type="int" - transient="false" - volatile="false" - value="16843395" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> <field name="donut_resource_pad3" type="int" transient="false" @@ -3672,6 +3628,17 @@ visibility="public" > </field> +<field name="dropDownHeight" + type="int" + transient="false" + volatile="false" + value="16843395" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="dropDownHintAppearance" type="int" transient="false" @@ -5311,6 +5278,17 @@ visibility="public" > </field> +<field name="largeScreens" + type="int" + transient="false" + volatile="false" + value="16843398" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="launchMode" type="int" transient="false" @@ -6191,6 +6169,17 @@ visibility="public" > </field> +<field name="normalScreens" + type="int" + transient="false" + volatile="false" + value="16843397" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="numColumns" type="int" transient="false" @@ -7577,6 +7566,17 @@ visibility="public" > </field> +<field name="smallScreens" + type="int" + transient="false" + volatile="false" + value="16843396" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="smoothScrollbar" type="int" transient="false" @@ -22088,6 +22088,19 @@ <parameter name="position" type="int"> </parameter> </method> +<method name="itemForPosition" + return="android.app.LauncherActivity.ListItem" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="protected" +> +<parameter name="position" type="int"> +</parameter> +</method> <method name="makeListItems" return="java.util.List<android.app.LauncherActivity.ListItem>" abstract="false" @@ -22196,6 +22209,16 @@ visibility="public" > </field> +<field name="resolveInfo" + type="android.content.pm.ResolveInfo" + transient="false" + volatile="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> </class> <class name="ListActivity" extends="android.app.Activity" @@ -23852,6 +23875,17 @@ visibility="public" > </field> +<field name="USER_QUERY" + type="java.lang.String" + transient="false" + volatile="false" + value=""user_query"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> </class> <interface name="SearchManager.OnCancelListener" abstract="true" @@ -34787,6 +34821,17 @@ visibility="public" > </field> +<field name="CONFIG_SCREEN_LAYOUT" + type="int" + transient="false" + volatile="false" + value="256" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="CONFIG_TOUCHSCREEN" type="int" transient="false" @@ -35255,6 +35300,28 @@ type="int" transient="false" volatile="false" + value="2048" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FLAG_SUPPORTS_NORMAL_SCREENS" + type="int" + transient="false" + volatile="false" + value="1024" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="FLAG_SUPPORTS_SMALL_SCREENS" + type="int" + transient="false" + volatile="false" value="512" static="true" final="true" @@ -39454,6 +39521,50 @@ visibility="public" > </field> +<field name="SCREENLAYOUT_LARGE" + type="int" + transient="false" + volatile="false" + value="3" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="SCREENLAYOUT_NORMAL" + type="int" + transient="false" + volatile="false" + value="2" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="SCREENLAYOUT_SMALL" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="SCREENLAYOUT_UNDEFINED" + type="int" + transient="false" + volatile="false" + value="0" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="TOUCHSCREEN_FINGER" type="int" transient="false" @@ -39588,6 +39699,16 @@ visibility="public" > </field> +<field name="screenLayout" + type="int" + transient="false" + volatile="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="touchscreen" type="int" transient="false" @@ -71778,6 +71899,721 @@ visibility="public" > </field> +<field name="TONE_CDMA_ABBR_ALERT" + type="int" + transient="false" + volatile="false" + value="97" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_ABBR_INTERCEPT" + type="int" + transient="false" + volatile="false" + value="37" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_ABBR_REORDER" + type="int" + transient="false" + volatile="false" + value="39" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_ALERT_AUTOREDIAL_LITE" + type="int" + transient="false" + volatile="false" + value="87" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_ALERT_CALL_GUARD" + type="int" + transient="false" + volatile="false" + value="93" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_ALERT_INCALL_LITE" + type="int" + transient="false" + volatile="false" + value="91" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_ALERT_NETWORK_LITE" + type="int" + transient="false" + volatile="false" + value="86" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_ANSWER" + type="int" + transient="false" + volatile="false" + value="42" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_CALLDROP_LITE" + type="int" + transient="false" + volatile="false" + value="95" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_CALL_SIGNAL_ISDN_INTERGROUP" + type="int" + transient="false" + volatile="false" + value="46" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_CALL_SIGNAL_ISDN_NORMAL" + type="int" + transient="false" + volatile="false" + value="45" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_CALL_SIGNAL_ISDN_PAT3" + type="int" + transient="false" + volatile="false" + value="48" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_CALL_SIGNAL_ISDN_PAT5" + type="int" + transient="false" + volatile="false" + value="50" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_CALL_SIGNAL_ISDN_PAT6" + type="int" + transient="false" + volatile="false" + value="51" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_CALL_SIGNAL_ISDN_PAT7" + type="int" + transient="false" + volatile="false" + value="52" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_CALL_SIGNAL_ISDN_PING_RING" + type="int" + transient="false" + volatile="false" + value="49" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_CALL_SIGNAL_ISDN_SP_PRI" + type="int" + transient="false" + volatile="false" + value="47" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_CONFIRM" + type="int" + transient="false" + volatile="false" + value="41" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_DIAL_TONE_LITE" + type="int" + transient="false" + volatile="false" + value="34" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_EMERGENCY_RINGBACK" + type="int" + transient="false" + volatile="false" + value="92" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_HIGH_L" + type="int" + transient="false" + volatile="false" + value="53" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_HIGH_PBX_L" + type="int" + transient="false" + volatile="false" + value="71" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_HIGH_PBX_SLS" + type="int" + transient="false" + volatile="false" + value="80" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_HIGH_PBX_SS" + type="int" + transient="false" + volatile="false" + value="74" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_HIGH_PBX_SSL" + type="int" + transient="false" + volatile="false" + value="77" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_HIGH_PBX_S_X4" + type="int" + transient="false" + volatile="false" + value="83" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_HIGH_SLS" + type="int" + transient="false" + volatile="false" + value="65" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_HIGH_SS" + type="int" + transient="false" + volatile="false" + value="56" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_HIGH_SSL" + type="int" + transient="false" + volatile="false" + value="59" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_HIGH_SS_2" + type="int" + transient="false" + volatile="false" + value="62" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_HIGH_S_X4" + type="int" + transient="false" + volatile="false" + value="68" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_INTERCEPT" + type="int" + transient="false" + volatile="false" + value="36" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_KEYPAD_VOLUME_KEY_LITE" + type="int" + transient="false" + volatile="false" + value="89" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_LOW_L" + type="int" + transient="false" + volatile="false" + value="55" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_LOW_PBX_L" + type="int" + transient="false" + volatile="false" + value="73" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_LOW_PBX_SLS" + type="int" + transient="false" + volatile="false" + value="82" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_LOW_PBX_SS" + type="int" + transient="false" + volatile="false" + value="76" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_LOW_PBX_SSL" + type="int" + transient="false" + volatile="false" + value="79" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_LOW_PBX_S_X4" + type="int" + transient="false" + volatile="false" + value="85" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_LOW_SLS" + type="int" + transient="false" + volatile="false" + value="67" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_LOW_SS" + type="int" + transient="false" + volatile="false" + value="58" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_LOW_SSL" + type="int" + transient="false" + volatile="false" + value="61" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_LOW_SS_2" + type="int" + transient="false" + volatile="false" + value="64" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_LOW_S_X4" + type="int" + transient="false" + volatile="false" + value="70" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_MED_L" + type="int" + transient="false" + volatile="false" + value="54" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_MED_PBX_L" + type="int" + transient="false" + volatile="false" + value="72" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_MED_PBX_SLS" + type="int" + transient="false" + volatile="false" + value="81" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_MED_PBX_SS" + type="int" + transient="false" + volatile="false" + value="75" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_MED_PBX_SSL" + type="int" + transient="false" + volatile="false" + value="78" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_MED_PBX_S_X4" + type="int" + transient="false" + volatile="false" + value="84" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_MED_SLS" + type="int" + transient="false" + volatile="false" + value="66" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_MED_SS" + type="int" + transient="false" + volatile="false" + value="57" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_MED_SSL" + type="int" + transient="false" + volatile="false" + value="60" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_MED_SS_2" + type="int" + transient="false" + volatile="false" + value="63" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_MED_S_X4" + type="int" + transient="false" + volatile="false" + value="69" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_NETWORK_BUSY" + type="int" + transient="false" + volatile="false" + value="40" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_NETWORK_BUSY_ONE_SHOT" + type="int" + transient="false" + volatile="false" + value="96" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_NETWORK_CALLWAITING" + type="int" + transient="false" + volatile="false" + value="43" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_NETWORK_USA_RINGBACK" + type="int" + transient="false" + volatile="false" + value="35" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_ONE_MIN_BEEP" + type="int" + transient="false" + volatile="false" + value="88" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_PIP" + type="int" + transient="false" + volatile="false" + value="44" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_PRESSHOLDKEY_LITE" + type="int" + transient="false" + volatile="false" + value="90" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_REORDER" + type="int" + transient="false" + volatile="false" + value="38" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_SIGNAL_OFF" + type="int" + transient="false" + volatile="false" + value="98" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_CDMA_SOFT_ERROR_LITE" + type="int" + transient="false" + volatile="false" + value="94" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="TONE_DTMF_0" type="int" transient="false" @@ -161435,6 +162271,17 @@ visibility="public" > </method> +<method name="getDropDownHeight" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getDropDownWidth" return="int" abstract="false" @@ -161658,6 +162505,19 @@ <parameter name="id" type="int"> </parameter> </method> +<method name="setDropDownHeight" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="height" type="int"> +</parameter> +</method> <method name="setDropDownWidth" return="void" abstract="false" @@ -179827,7 +180687,7 @@ <method name="startMethodTracing" return="void" abstract="false" - native="true" + native="false" synchronized="false" static="true" final="false" diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp index 7852c13..105d4d2 100644 --- a/camera/libcameraservice/CameraService.cpp +++ b/camera/libcameraservice/CameraService.cpp @@ -32,6 +32,7 @@ #include <media/AudioSystem.h> #include "CameraService.h" +#include <cutils/atomic.h> #include <cutils/properties.h> namespace android { @@ -81,6 +82,7 @@ CameraService::CameraService() : BnCameraService() { LOGI("CameraService started: pid=%d", getpid()); + mUsers = 0; } CameraService::~CameraService() @@ -96,7 +98,7 @@ sp<ICamera> CameraService::connect(const sp<ICameraClient>& cameraClient) LOGD("CameraService::connect E (pid %d, client %p)", callingPid, cameraClient->asBinder().get()); - Mutex::Autolock lock(mLock); + Mutex::Autolock lock(mServiceLock); sp<Client> client; if (mClient != 0) { sp<Client> currentClient = mClient.promote(); @@ -113,7 +115,7 @@ sp<ICamera> CameraService::connect(const sp<ICameraClient>& cameraClient) "(old pid %d, old client %p)", callingPid, cameraClient->asBinder().get(), currentClient->mClientPid, currentCameraClient->asBinder().get()); - if (kill(currentClient->mClientPid, 0) == ESRCH) { + if (kill(currentClient->mClientPid, 0) == -1 && errno == ESRCH) { LOGD("The old client is dead!"); } return client; @@ -126,6 +128,11 @@ sp<ICamera> CameraService::connect(const sp<ICameraClient>& cameraClient) } } + if (mUsers > 0) { + LOGD("Still have client, rejected"); + return client; + } + // create a new Client object client = new Client(this, cameraClient, callingPid); mClient = client; @@ -146,7 +153,7 @@ void CameraService::removeClient(const sp<ICameraClient>& cameraClient) // destructor won't be called with the lock held. sp<Client> client; - Mutex::Autolock lock(mLock); + Mutex::Autolock lock(mServiceLock); if (mClient == 0) { // This happens when we have already disconnected. @@ -174,6 +181,20 @@ void CameraService::removeClient(const sp<ICameraClient>& cameraClient) LOGD("removeClient (pid %d) done", callingPid); } +// The reason we need this count is a new CameraService::connect() request may +// come in while the previous Client's destructor has not been run or is still +// running. If the last strong reference of the previous Client is gone but +// destructor has not been run, we should not allow the new Client to be created +// because we need to wait for the previous Client to tear down the hardware +// first. +void CameraService::incUsers() { + android_atomic_inc(&mUsers); +} + +void CameraService::decUsers() { + android_atomic_dec(&mUsers); +} + static sp<MediaPlayer> newMediaPlayer(const char *file) { sp<MediaPlayer> mp = new MediaPlayer(); @@ -209,6 +230,7 @@ CameraService::Client::Client(const sp<CameraService>& cameraService, // Callback is disabled by default mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP; + cameraService->incUsers(); LOGD("Client::Client X (pid %d)", callingPid); } @@ -350,7 +372,7 @@ CameraService::Client::~Client() void CameraService::Client::disconnect() { - int callingPid = getCallingPid(); + int callingPid = getCallingPid(); LOGD("Client::disconnect() E (pid %d client %p)", callingPid, getCameraClient()->asBinder().get()); @@ -365,18 +387,23 @@ void CameraService::Client::disconnect() return; } - mCameraService->removeClient(mCameraClient); - if (mHardware != 0) { - LOGD("hardware teardown"); - // Before destroying mHardware, we must make sure it's in the - // idle state. - mHardware->stopPreview(); - // Cancel all picture callbacks. - mHardware->cancelPicture(true, true, true); - // Release the hardware resources. - mHardware->release(); - } + // Make sure disconnect() is done once and once only, whether it is called + // from the user directly, or called by the destructor. + if (mHardware == 0) return; + + LOGD("hardware teardown"); + // Before destroying mHardware, we must make sure it's in the + // idle state. + mHardware->stopPreview(); + // Cancel all picture callbacks. + mHardware->cancelPicture(true, true, true); + // Release the hardware resources. + mHardware->release(); mHardware.clear(); + + mCameraService->removeClient(mCameraClient); + mCameraService->decUsers(); + LOGD("Client::disconnect() X (pid %d)", callingPid); } @@ -634,7 +661,7 @@ sp<CameraService::Client> CameraService::Client::getClientFromCookie(void* user) sp<Client> client = 0; CameraService *service = static_cast<CameraService*>(user); if (service != NULL) { - Mutex::Autolock ourLock(service->mLock); + Mutex::Autolock ourLock(service->mServiceLock); if (service->mClient != 0) { client = service->mClient.promote(); if (client == 0) { @@ -1077,7 +1104,7 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) result.append(buffer); write(fd, result.string(), result.size()); } else { - AutoMutex lock(&mLock); + AutoMutex lock(&mServiceLock); if (mClient != 0) { sp<Client> currentClient = mClient.promote(); sprintf(buffer, "Client (%p) PID: %d\n", diff --git a/camera/libcameraservice/CameraService.h b/camera/libcameraservice/CameraService.h index a421fd3..8b8b54c 100644 --- a/camera/libcameraservice/CameraService.h +++ b/camera/libcameraservice/CameraService.h @@ -194,7 +194,12 @@ private: CameraService(); virtual ~CameraService(); - mutable Mutex mLock; + // We use a count for number of clients (shoule only be 0 or 1). + volatile int32_t mUsers; + virtual void incUsers(); + virtual void decUsers(); + + mutable Mutex mServiceLock; wp<Client> mClient; #if DEBUG_HEAP_LEAKS diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index 2a4a672..3782136 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -26,10 +26,13 @@ import android.content.ComponentName; import android.content.Intent; import android.net.Uri; import android.os.Bundle; +import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceManager; import android.view.IWindowManager; +import java.io.File; +import java.io.FileNotFoundException; import java.util.Iterator; import java.util.Set; @@ -446,6 +449,8 @@ public class Am { return; } + ParcelFileDescriptor fd = null; + String cmd = nextArg(); if ("start".equals(cmd)) { start = true; @@ -455,6 +460,16 @@ public class Am { showUsage(); return; } + try { + fd = ParcelFileDescriptor.open( + new File(profileFile), + ParcelFileDescriptor.MODE_CREATE | + ParcelFileDescriptor.MODE_TRUNCATE | + ParcelFileDescriptor.MODE_READ_WRITE); + } catch (FileNotFoundException e) { + System.err.println("Error: Unable to open file: " + profileFile); + return; + } } else if (!"stop".equals(cmd)) { System.err.println("Error: Profile command " + cmd + " not valid"); showUsage(); @@ -462,8 +477,8 @@ public class Am { } try { - if (!mAm.profileControl(process, start, profileFile)) { - System.out.println("PROFILE FAILED on process " + process); + if (!mAm.profileControl(process, start, profileFile, fd)) { + System.err.println("PROFILE FAILED on process " + process); return; } } catch (IllegalArgumentException e) { diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java index 841e3df..c299bff 100644 --- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java +++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java @@ -17,6 +17,7 @@ package com.android.commands.bmgr; import android.backup.IBackupManager; +import android.backup.IRestoreObserver; import android.backup.IRestoreSession; import android.backup.RestoreSet; import android.os.RemoteException; @@ -170,9 +171,7 @@ public final class Bmgr { if (sets == null || sets.length == 0) { System.out.println("No restore sets available"); } else { - for (RestoreSet s : sets) { - System.out.println(" " + s.token + " : " + s.name); - } + printRestoreSets(sets); } } catch (RemoteException e) { System.err.println(e.toString()); @@ -180,16 +179,44 @@ public final class Bmgr { } } + private void printRestoreSets(RestoreSet[] sets) { + for (RestoreSet s : sets) { + System.out.println(" " + s.token + " : " + s.name); + } + } + + class RestoreObserver extends IRestoreObserver.Stub { + boolean done; + public void restoreStarting(int numPackages) { + System.out.println("restoreStarting: " + numPackages + " packages"); + } + + public void onUpdate(int nowBeingRestored) { + System.out.println("onUpdate: " + nowBeingRestored); + } + + public void restoreFinished(int error) { + System.out.println("restoreFinished: " + error); + synchronized (this) { + done = true; + this.notify(); + } + } + } + private void doRestore() { - int token; + long token; try { - token = Integer.parseInt(nextArg()); + token = Long.parseLong(nextArg()); } catch (NumberFormatException e) { showUsage(); return; } + RestoreObserver observer = new RestoreObserver(); + try { + boolean didRestore = false; int curTransport = mBmgr.getCurrentTransport(); mRestore = mBmgr.beginRestoreSession(curTransport); if (mRestore == null) { @@ -200,15 +227,35 @@ public final class Bmgr { for (RestoreSet s : sets) { if (s.token == token) { System.out.println("Scheduling restore: " + s.name); - mRestore.performRestore(token); + mRestore.performRestore(token, observer); + didRestore = true; break; } } + if (!didRestore) { + if (sets == null || sets.length == 0) { + System.out.println("No available restore sets; no restore performed"); + } else { + System.out.println("No matching restore set token. Available sets:"); + printRestoreSets(sets); + } + } mRestore.endRestoreSession(); } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(BMGR_NOT_RUNNING_ERR); } + + // now wait for it to be done + synchronized (observer) { + while (!observer.done) { + try { + observer.wait(); + } catch (InterruptedException ex) { + } + } + } + System.out.println("done"); } private String nextArg() { @@ -229,4 +276,4 @@ public final class Bmgr { System.err.println(" bmgr restore token#"); System.err.println(" bmgr run"); } -}
\ No newline at end of file +} diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index b6f855a..dfa8139 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -986,7 +986,9 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM String process = data.readString(); boolean start = data.readInt() != 0; String path = data.readString(); - boolean res = profileControl(process, start, path); + ParcelFileDescriptor fd = data.readInt() != 0 + ? data.readFileDescriptor() : null; + boolean res = profileControl(process, start, path, fd); reply.writeNoException(); reply.writeInt(res ? 1 : 0); return true; @@ -2232,7 +2234,7 @@ class ActivityManagerProxy implements IActivityManager } public boolean profileControl(String process, boolean start, - String path) throws RemoteException + String path, ParcelFileDescriptor fd) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); @@ -2240,6 +2242,12 @@ class ActivityManagerProxy implements IActivityManager data.writeString(process); data.writeInt(start ? 1 : 0); data.writeString(path); + if (fd != null) { + data.writeInt(1); + fd.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE); + } else { + data.writeInt(0); + } mRemote.transact(PROFILE_CONTROL_TRANSACTION, data, reply, 0); reply.readException(); boolean res = reply.readInt() != 0; diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 98bd45a..62dc651 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -48,6 +48,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.MessageQueue; +import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; @@ -74,6 +75,7 @@ import org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl; import java.io.File; import java.io.FileDescriptor; import java.io.FileOutputStream; +import java.io.IOException; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -1236,6 +1238,11 @@ public final class ActivityThread { String who; } + private static final class ProfilerControlData { + String path; + ParcelFileDescriptor fd; + } + private final class ApplicationThread extends ApplicationThreadNative { private static final String HEAP_COLUMN = "%17s %8s %8s %8s %8s"; private static final String ONE_COUNT_COLUMN = "%17s %8d"; @@ -1494,8 +1501,11 @@ public final class ActivityThread { } } - public void profilerControl(boolean start, String path) { - queueOrSendMessage(H.PROFILER_CONTROL, path, start ? 1 : 0); + public void profilerControl(boolean start, String path, ParcelFileDescriptor fd) { + ProfilerControlData pcd = new ProfilerControlData(); + pcd.path = path; + pcd.fd = fd; + queueOrSendMessage(H.PROFILER_CONTROL, pcd, start ? 1 : 0); } public void setSchedulingGroup(int group) { @@ -1838,7 +1848,7 @@ public final class ActivityThread { handleActivityConfigurationChanged((IBinder)msg.obj); break; case PROFILER_CONTROL: - handleProfilerControl(msg.arg1 != 0, (String)msg.obj); + handleProfilerControl(msg.arg1 != 0, (ProfilerControlData)msg.obj); break; case CREATE_BACKUP_AGENT: handleCreateBackupAgent((CreateBackupAgentData)msg.obj); @@ -3618,15 +3628,20 @@ public final class ActivityThread { performConfigurationChanged(r.activity, mConfiguration); } - final void handleProfilerControl(boolean start, String path) { + final void handleProfilerControl(boolean start, ProfilerControlData pcd) { if (start) { - File file = new File(path); - file.getParentFile().mkdirs(); try { - Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024); + Debug.startMethodTracing(pcd.path, pcd.fd.getFileDescriptor(), + 8 * 1024 * 1024, 0); } catch (RuntimeException e) { - Log.w(TAG, "Profiling failed on path " + path + Log.w(TAG, "Profiling failed on path " + pcd.path + " -- can the process access this path?"); + } finally { + try { + pcd.fd.close(); + } catch (IOException e) { + Log.w(TAG, "Failure closing profile fd", e); + } } } else { Debug.stopMethodTracing(); @@ -3682,6 +3697,13 @@ public final class ActivityThread { */ Locale.setDefault(data.config.locale); + /* + * Update the system configuration since its preloaded and might not + * reflect configuration changes. The configuration object passed + * in AppBindData can be safely assumed to be up to date + */ + Resources.getSystem().updateConfiguration(mConfiguration, null); + data.info = getPackageInfoNoCheck(data.appInfo); if (data.debugMode != IApplicationThread.DEBUG_OFF) { diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index 4b64c94..b052c99 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -26,6 +26,7 @@ import android.content.pm.ServiceInfo; import android.content.res.Configuration; import android.os.Binder; import android.os.Bundle; +import android.os.Parcelable; import android.os.RemoteException; import android.os.IBinder; import android.os.Parcel; @@ -331,7 +332,9 @@ public abstract class ApplicationThreadNative extends Binder data.enforceInterface(IApplicationThread.descriptor); boolean start = data.readInt() != 0; String path = data.readString(); - profilerControl(start, path); + ParcelFileDescriptor fd = data.readInt() != 0 + ? data.readFileDescriptor() : null; + profilerControl(start, path, fd); return true; } @@ -711,11 +714,18 @@ class ApplicationThreadProxy implements IApplicationThread { data.recycle(); } - public void profilerControl(boolean start, String path) throws RemoteException { + public void profilerControl(boolean start, String path, + ParcelFileDescriptor fd) throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); data.writeInt(start ? 1 : 0); data.writeString(path); + if (fd != null) { + data.writeInt(1); + fd.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE); + } else { + data.writeInt(0); + } mRemote.transact(PROFILER_CONTROL_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); data.recycle(); diff --git a/core/java/android/app/BackupAgent.java b/core/java/android/app/BackupAgent.java index e810775..0ac8a1e 100644 --- a/core/java/android/app/BackupAgent.java +++ b/core/java/android/app/BackupAgent.java @@ -67,7 +67,7 @@ public abstract class BackupAgent extends ContextWrapper { * here after writing the requested data to dataFd. */ public abstract void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, - ParcelFileDescriptor newState); + ParcelFileDescriptor newState) throws IOException; /** * The application is being restored from backup, and should replace any @@ -120,6 +120,9 @@ public abstract class BackupAgent extends ContextWrapper { BackupDataOutput output = new BackupDataOutput(data.getFileDescriptor()); try { BackupAgent.this.onBackup(oldState, output, newState); + } catch (IOException ex) { + Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex); + throw new RuntimeException(ex); } catch (RuntimeException ex) { Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex); throw ex; diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 66bc85b..3ec7938 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -250,7 +250,7 @@ public interface IActivityManager extends IInterface { // Turn on/off profiling in a particular process. public boolean profileControl(String process, boolean start, - String path) throws RemoteException; + String path, ParcelFileDescriptor fd) throws RemoteException; public boolean shutdown(int timeout) throws RemoteException; diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java index 029c650..c0bc2a0 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -25,6 +25,7 @@ import android.content.pm.ProviderInfo; import android.content.pm.ServiceInfo; import android.content.res.Configuration; import android.os.Bundle; +import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.IBinder; import android.os.IInterface; @@ -92,7 +93,8 @@ public interface IApplicationThread extends IInterface { void scheduleLowMemory() throws RemoteException; void scheduleActivityConfigurationChanged(IBinder token) throws RemoteException; void requestPss() throws RemoteException; - void profilerControl(boolean start, String path) throws RemoteException; + void profilerControl(boolean start, String path, ParcelFileDescriptor fd) + throws RemoteException; void setSchedulingGroup(int group) throws RemoteException; String descriptor = "android.app.IApplicationThread"; diff --git a/core/java/android/app/LauncherActivity.java b/core/java/android/app/LauncherActivity.java index 8d249da..accdda9 100644 --- a/core/java/android/app/LauncherActivity.java +++ b/core/java/android/app/LauncherActivity.java @@ -60,26 +60,20 @@ public abstract class LauncherActivity extends ListActivity { * An item in the list */ public static class ListItem { + public ResolveInfo resolveInfo; public CharSequence label; - //public CharSequence description; public Drawable icon; public String packageName; public String className; public Bundle extras; ListItem(PackageManager pm, ResolveInfo resolveInfo, IconResizer resizer) { + this.resolveInfo = resolveInfo; label = resolveInfo.loadLabel(pm); if (label == null && resolveInfo.activityInfo != null) { label = resolveInfo.activityInfo.name; } - /* - if (resolveInfo.activityInfo != null && - resolveInfo.activityInfo.applicationInfo != null) { - description = resolveInfo.activityInfo.applicationInfo.loadDescription(pm); - } - */ - icon = resizer.createIconThumbnail(resolveInfo.loadIcon(pm)); packageName = resolveInfo.activityInfo.applicationInfo.packageName; className = resolveInfo.activityInfo.name; @@ -122,6 +116,14 @@ public abstract class LauncherActivity extends ListActivity { return intent; } + public ListItem itemForPosition(int position) { + if (mActivitiesList == null) { + return null; + } + + return mActivitiesList.get(position); + } + public int getCount() { return mActivitiesList != null ? mActivitiesList.size() : 0; } @@ -354,6 +356,16 @@ public abstract class LauncherActivity extends ListActivity { } /** + * Return the {@link ListItem} for a specific position in our + * {@link android.widget.ListView}. + * @param position The item to return + */ + protected ListItem itemForPosition(int position) { + ActivityAdapter adapter = (ActivityAdapter) mAdapter; + return adapter.itemForPosition(position); + } + + /** * Get the base intent to use when running * {@link PackageManager#queryIntentActivities(Intent, int)}. */ diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index 5d5a277..6ddf50f 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -88,13 +88,11 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS private static final String INSTANCE_KEY_COMPONENT = "comp"; private static final String INSTANCE_KEY_APPDATA = "data"; private static final String INSTANCE_KEY_GLOBALSEARCH = "glob"; - private static final String INSTANCE_KEY_DISPLAY_QUERY = "dQry"; - private static final String INSTANCE_KEY_DISPLAY_SEL_START = "sel1"; - private static final String INSTANCE_KEY_DISPLAY_SEL_END = "sel2"; - private static final String INSTANCE_KEY_SELECTED_ELEMENT = "slEl"; - private static final int INSTANCE_SELECTED_BUTTON = -2; - private static final int INSTANCE_SELECTED_QUERY = -1; - + private static final String INSTANCE_KEY_STORED_COMPONENT = "sComp"; + private static final String INSTANCE_KEY_STORED_APPDATA = "sData"; + private static final String INSTANCE_KEY_PREVIOUS_COMPONENTS = "sPrev"; + private static final String INSTANCE_KEY_USER_QUERY = "uQry"; + private static final int SEARCH_PLATE_LEFT_PADDING_GLOBAL = 12; private static final int SEARCH_PLATE_LEFT_PADDING_NON_GLOBAL = 7; @@ -450,8 +448,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS /** * Save the minimal set of data necessary to recreate the search * - * TODO: go through this and make sure that it saves everything that is needed - * * @return A bundle with the state of the dialog. */ @Override @@ -462,20 +458,11 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS bundle.putParcelable(INSTANCE_KEY_COMPONENT, mLaunchComponent); bundle.putBundle(INSTANCE_KEY_APPDATA, mAppSearchData); bundle.putBoolean(INSTANCE_KEY_GLOBALSEARCH, mGlobalSearchMode); - - // UI state - bundle.putString(INSTANCE_KEY_DISPLAY_QUERY, mSearchAutoComplete.getText().toString()); - bundle.putInt(INSTANCE_KEY_DISPLAY_SEL_START, mSearchAutoComplete.getSelectionStart()); - bundle.putInt(INSTANCE_KEY_DISPLAY_SEL_END, mSearchAutoComplete.getSelectionEnd()); - - int selectedElement = INSTANCE_SELECTED_QUERY; - if (mGoButton.isFocused()) { - selectedElement = INSTANCE_SELECTED_BUTTON; - } else if (mSearchAutoComplete.isPopupShowing()) { - selectedElement = 0; // TODO mSearchTextField.getListSelection() // 0..n - } - bundle.putInt(INSTANCE_KEY_SELECTED_ELEMENT, selectedElement); - + bundle.putParcelable(INSTANCE_KEY_STORED_COMPONENT, mStoredComponentName); + bundle.putBundle(INSTANCE_KEY_STORED_APPDATA, mStoredAppSearchData); + bundle.putParcelableArrayList(INSTANCE_KEY_PREVIOUS_COMPONENTS, mPreviousComponents); + bundle.putString(INSTANCE_KEY_USER_QUERY, mUserQuery); + return bundle; } @@ -489,45 +476,27 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS */ @Override public void onRestoreInstanceState(Bundle savedInstanceState) { - // Get the launch info ComponentName launchComponent = savedInstanceState.getParcelable(INSTANCE_KEY_COMPONENT); Bundle appSearchData = savedInstanceState.getBundle(INSTANCE_KEY_APPDATA); boolean globalSearch = savedInstanceState.getBoolean(INSTANCE_KEY_GLOBALSEARCH); - - // get the UI state - String displayQuery = savedInstanceState.getString(INSTANCE_KEY_DISPLAY_QUERY); - int querySelStart = savedInstanceState.getInt(INSTANCE_KEY_DISPLAY_SEL_START, -1); - int querySelEnd = savedInstanceState.getInt(INSTANCE_KEY_DISPLAY_SEL_END, -1); - int selectedElement = savedInstanceState.getInt(INSTANCE_KEY_SELECTED_ELEMENT); - - // show the dialog. skip any show/hide animation, we want to go fast. - // send the text that actually generates the suggestions here; we'll replace the display - // text as necessary in a moment. - if (!show(displayQuery, false, launchComponent, appSearchData, globalSearch)) { + ComponentName storedComponentName = + savedInstanceState.getParcelable(INSTANCE_KEY_STORED_COMPONENT); + Bundle storedAppSearchData = + savedInstanceState.getBundle(INSTANCE_KEY_STORED_APPDATA); + ArrayList<ComponentName> previousComponents = + savedInstanceState.getParcelableArrayList(INSTANCE_KEY_PREVIOUS_COMPONENTS); + String userQuery = savedInstanceState.getString(INSTANCE_KEY_USER_QUERY); + + // Set stored state + mStoredComponentName = storedComponentName; + mStoredAppSearchData = storedAppSearchData; + mPreviousComponents = previousComponents; + + // show the dialog. + if (!doShow(userQuery, false, launchComponent, appSearchData, globalSearch)) { // for some reason, we couldn't re-instantiate return; } - - mSearchAutoComplete.setText(displayQuery); - - // clean up the selection state - switch (selectedElement) { - case INSTANCE_SELECTED_BUTTON: - mGoButton.setEnabled(true); - mGoButton.setFocusable(true); - mGoButton.requestFocus(); - break; - case INSTANCE_SELECTED_QUERY: - if (querySelStart >= 0 && querySelEnd >= 0) { - mSearchAutoComplete.requestFocus(); - mSearchAutoComplete.setSelection(querySelStart, querySelEnd); - } - break; - default: - // TODO: defer selecting a list element until suggestion list appears -// mSearchAutoComplete.setListSelection(selectedElement) - break; - } } /** @@ -591,12 +560,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS if (mGlobalSearchMode) { mSearchAutoComplete.setDropDownAlwaysVisible(true); // fill space until results come in - mSearchAutoComplete.setDropDownBackgroundResource( - com.android.internal.R.drawable.search_dropdown_background); } else { mSearchAutoComplete.setDropDownAlwaysVisible(false); - mSearchAutoComplete.setDropDownBackgroundResource( - com.android.internal.R.drawable.search_dropdown_background_apps); } // attach the suggestions adapter, if suggestions are available @@ -1357,6 +1322,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS String query = intent.getStringExtra(SearchManager.QUERY); setUserQuery(query); + mSearchAutoComplete.showDropDown(); } /** @@ -1505,6 +1471,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS if (data != null) { intent.setData(data); } + intent.putExtra(SearchManager.USER_QUERY, mUserQuery); if (query != null) { intent.putExtra(SearchManager.QUERY, query); } diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index fd69ba4..e5ba6a4 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -1136,6 +1136,20 @@ public class SearchManager public final static String QUERY = "query"; /** + * Intent extra data key: Use this key with + * {@link android.content.Intent#getStringExtra + * content.Intent.getStringExtra()} + * to obtain the query string typed in by the user. + * This may be different from the value of {@link #QUERY} + * if the intent is the result of selecting a suggestion. + * In that case, {@link #QUERY} will contain the value of + * {@link #SUGGEST_COLUMN_QUERY} for the suggestion, and + * {@link #USER_QUERY} will contain the string typed by the + * user. + */ + public final static String USER_QUERY = "user_query"; + + /** * Intent extra data key: Use this key with Intent.ACTION_SEARCH and * {@link android.content.Intent#getBundleExtra * content.Intent.getBundleExtra()} diff --git a/core/java/android/app/SuggestionsAdapter.java b/core/java/android/app/SuggestionsAdapter.java index 0bdb10b..49c94d1 100644 --- a/core/java/android/app/SuggestionsAdapter.java +++ b/core/java/android/app/SuggestionsAdapter.java @@ -147,7 +147,7 @@ class SuggestionsAdapter extends ResourceCursorAdapter { final Cursor cursor = mSearchManager.getSuggestions(mSearchable, query); // trigger fill window so the spinner stays up until the results are copied over and // closer to being ready - if (!mGlobalSearchMode) cursor.getCount(); + if (!mGlobalSearchMode && cursor != null) cursor.getCount(); return cursor; } catch (RuntimeException e) { Log.w(LOG_TAG, "Search suggestions query threw an exception.", e); diff --git a/core/java/android/backup/AbsoluteFileBackupHelper.java b/core/java/android/backup/AbsoluteFileBackupHelper.java new file mode 100644 index 0000000..ab24675 --- /dev/null +++ b/core/java/android/backup/AbsoluteFileBackupHelper.java @@ -0,0 +1,66 @@ +/* + * 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.backup; + +import android.content.Context; +import android.os.ParcelFileDescriptor; +import android.util.Log; + +import java.io.File; +import java.io.FileDescriptor; + +/** + * Like FileBackupHelper, but takes absolute paths for the files instead of + * subpaths of getFilesDir() + * + * @hide + */ +public class AbsoluteFileBackupHelper extends FileBackupHelperBase implements BackupHelper { + private static final String TAG = "AbsoluteFileBackupHelper"; + + Context mContext; + String[] mFiles; + + public AbsoluteFileBackupHelper(Context context, String... files) { + super(context); + + mContext = context; + mFiles = files; + } + + /** + * Based on oldState, determine which of the files from the application's data directory + * need to be backed up, write them to the data stream, and fill in newState with the + * state as it exists now. + */ + public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, + ParcelFileDescriptor newState) { + // use the file paths as the keys, too + performBackup_checked(oldState, data, newState, mFiles, mFiles); + } + + public void restoreEntity(BackupDataInputStream data) { + // TODO: turn this off before ship + Log.d(TAG, "got entity '" + data.getKey() + "' size=" + data.size()); + String key = data.getKey(); + if (isKeyInList(key, mFiles)) { + File f = new File(key); + writeFile(f, data); + } + } +} + diff --git a/core/java/android/backup/BackupHelperAgent.java b/core/java/android/backup/BackupHelperAgent.java index 3720d50..5d0c4a2 100644 --- a/core/java/android/backup/BackupHelperAgent.java +++ b/core/java/android/backup/BackupHelperAgent.java @@ -34,7 +34,7 @@ public class BackupHelperAgent extends BackupAgent { @Override public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, - ParcelFileDescriptor newState) { + ParcelFileDescriptor newState) throws IOException { mDispatcher.performBackup(oldState, data, newState); } diff --git a/core/java/android/backup/BackupHelperDispatcher.java b/core/java/android/backup/BackupHelperDispatcher.java index b25c3e3..6ccb83e 100644 --- a/core/java/android/backup/BackupHelperDispatcher.java +++ b/core/java/android/backup/BackupHelperDispatcher.java @@ -19,7 +19,10 @@ package android.backup; import android.os.ParcelFileDescriptor; import android.util.Log; +import java.io.DataInputStream; +import java.io.DataOutputStream; import java.io.IOException; +import java.io.FileDescriptor; import java.util.TreeMap; import java.util.Map; @@ -27,6 +30,11 @@ import java.util.Map; public class BackupHelperDispatcher { private static final String TAG = "BackupHelperDispatcher"; + private static class Header { + int chunkSize; // not including the header + String keyPrefix; + } + TreeMap<String,BackupHelper> mHelpers = new TreeMap<String,BackupHelper>(); public BackupHelperDispatcher() { @@ -36,13 +44,63 @@ public class BackupHelperDispatcher { mHelpers.put(keyPrefix, helper); } - /** TODO: Make this save and restore the key prefix. */ public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, - ParcelFileDescriptor newState) { - // Write out the state files -- mHelpers is a TreeMap, so the order is well defined. - for (Map.Entry<String,BackupHelper> entry: mHelpers.entrySet()) { - data.setKeyPrefix(entry.getKey()); - entry.getValue().performBackup(oldState, data, newState); + ParcelFileDescriptor newState) throws IOException { + // First, do the helpers that we've already done, since they're already in the state + // file. + int err; + Header header = new Header(); + TreeMap<String,BackupHelper> helpers = (TreeMap<String,BackupHelper>)mHelpers.clone(); + FileDescriptor oldStateFD = null; + FileDescriptor newStateFD = newState.getFileDescriptor(); + + if (oldState != null) { + oldStateFD = oldState.getFileDescriptor(); + while ((err = readHeader_native(header, oldStateFD)) >= 0) { + if (err == 0) { + BackupHelper helper = helpers.get(header.keyPrefix); + Log.d(TAG, "handling existing helper '" + header.keyPrefix + "' " + helper); + if (helper != null) { + doOneBackup(oldState, data, newState, header, helper); + helpers.remove(header.keyPrefix); + } else { + skipChunk_native(oldStateFD, header.chunkSize); + } + } + } + } + + // Then go through and do the rest that we haven't done. + for (Map.Entry<String,BackupHelper> entry: helpers.entrySet()) { + header.keyPrefix = entry.getKey(); + Log.d(TAG, "handling new helper '" + header.keyPrefix + "'"); + BackupHelper helper = entry.getValue(); + doOneBackup(oldState, data, newState, header, helper); + } + } + + private void doOneBackup(ParcelFileDescriptor oldState, BackupDataOutput data, + ParcelFileDescriptor newState, Header header, BackupHelper helper) + throws IOException { + int err; + FileDescriptor newStateFD = newState.getFileDescriptor(); + + // allocate space for the header in the file + int pos = allocateHeader_native(header, newStateFD); + if (pos < 0) { + throw new IOException("allocateHeader_native failed (error " + pos + ")"); + } + + data.setKeyPrefix(header.keyPrefix); + + // do the backup + helper.performBackup(oldState, data, newState); + + // fill in the header (seeking back to pos). The file pointer will be returned to + // where it was at the end of performBackup. Header.chunkSize will not be filled in. + err = writeHeader_native(header, newStateFD, pos); + if (err != 0) { + throw new IOException("writeHeader_native failed (error " + err + ")"); } } @@ -83,5 +141,11 @@ public class BackupHelperDispatcher { helper.writeRestoreSnapshot(newState); } } + + private static native int readHeader_native(Header h, FileDescriptor fd); + private static native int skipChunk_native(FileDescriptor fd, int bytesToSkip); + + private static native int allocateHeader_native(Header h, FileDescriptor fd); + private static native int writeHeader_native(Header h, FileDescriptor fd, int pos); } diff --git a/core/java/android/backup/BackupManager.java b/core/java/android/backup/BackupManager.java index 8df7eae..79e2c03 100644 --- a/core/java/android/backup/BackupManager.java +++ b/core/java/android/backup/BackupManager.java @@ -68,9 +68,11 @@ public class BackupManager { * {@link android.app.BackupAgent} subclass will be scheduled when you call this method. */ public void dataChanged() { - try { - mService.dataChanged(mContext.getPackageName()); - } catch (RemoteException e) { + if (mService != null) { + try { + mService.dataChanged(mContext.getPackageName()); + } catch (RemoteException e) { + } } } @@ -83,9 +85,11 @@ public class BackupManager { */ public IRestoreSession beginRestoreSession(int transportID) { IRestoreSession binder = null; - try { - binder = mService.beginRestoreSession(transportID); - } catch (RemoteException e) { + if (mService != null) { + try { + binder = mService.beginRestoreSession(transportID); + } catch (RemoteException e) { + } } return binder; } diff --git a/core/java/android/backup/IRestoreObserver.aidl b/core/java/android/backup/IRestoreObserver.aidl new file mode 100644 index 0000000..59e59fc --- /dev/null +++ b/core/java/android/backup/IRestoreObserver.aidl @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.backup; + +/** + * Callback class for receiving progress reports during a restore operation. + * + * @hide + */ +interface IRestoreObserver { + /** + * The restore operation has begun. + * + * @param numPackages The total number of packages being processed in + * this restore operation. + */ + void restoreStarting(int numPackages); + + /** + * An indication of which package is being restored currently, out of the + * total number provided in the restoreStarting() callback. This method + * is not guaranteed to be called. + * + * @param nowBeingRestored The index, between 1 and the numPackages parameter + * to the restoreStarting() callback, of the package now being restored. + */ + void onUpdate(int nowBeingRestored); + + /** + * The restore operation has completed. + * + * @param error Zero on success; a nonzero error code if the restore operation + * as a whole failed. + */ + void restoreFinished(int error); +} diff --git a/core/java/android/backup/IRestoreSession.aidl b/core/java/android/backup/IRestoreSession.aidl index 6bca865..2a1fbc1 100644 --- a/core/java/android/backup/IRestoreSession.aidl +++ b/core/java/android/backup/IRestoreSession.aidl @@ -17,6 +17,7 @@ package android.backup; import android.backup.RestoreSet; +import android.backup.IRestoreObserver; /** * Binder interface used by clients who wish to manage a restore operation. Every @@ -41,8 +42,10 @@ interface IRestoreSession { * * @param token The token from {@link getAvailableRestoreSets()} corresponding to * the restore set that should be used. + * @param observer If non-null, this binder points to an object that will receive + * progress callbacks during the restore operation. */ - int performRestore(int token); + int performRestore(long token, IRestoreObserver observer); /** * End this restore session. After this method is called, the IRestoreSession binder diff --git a/core/java/android/backup/RestoreSet.java b/core/java/android/backup/RestoreSet.java index 96a99ae..eeca148 100644 --- a/core/java/android/backup/RestoreSet.java +++ b/core/java/android/backup/RestoreSet.java @@ -43,14 +43,14 @@ public class RestoreSet implements Parcelable { * transport. This is guaranteed to be valid for the duration of a restore * session, but is meaningless once the session has ended. */ - public int token; + public long token; public RestoreSet() { // Leave everything zero / null } - public RestoreSet(String _name, String _dev, int _token) { + public RestoreSet(String _name, String _dev, long _token) { name = _name; device = _dev; token = _token; @@ -65,7 +65,7 @@ public class RestoreSet implements Parcelable { public void writeToParcel(Parcel out, int flags) { out.writeString(name); out.writeString(device); - out.writeInt(token); + out.writeLong(token); } public static final Parcelable.Creator<RestoreSet> CREATOR @@ -82,6 +82,6 @@ public class RestoreSet implements Parcelable { private RestoreSet(Parcel in) { name = in.readString(); device = in.readString(); - token = in.readInt(); + token = in.readLong(); } -}
\ No newline at end of file +} diff --git a/core/java/android/backup/SharedPreferencesBackupHelper.java b/core/java/android/backup/SharedPreferencesBackupHelper.java index f492629..4a7b399 100644 --- a/core/java/android/backup/SharedPreferencesBackupHelper.java +++ b/core/java/android/backup/SharedPreferencesBackupHelper.java @@ -30,7 +30,7 @@ public class SharedPreferencesBackupHelper extends FileBackupHelperBase implemen private Context mContext; private String[] mPrefGroups; - public SharedPreferencesBackupHelper(Context context, String[] prefGroups) { + public SharedPreferencesBackupHelper(Context context, String... prefGroups) { super(context); mContext = context; diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 609fa74..6fe5506 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1578,6 +1578,16 @@ public class Intent implements Parcelable { public static final String ACTION_REBOOT = "android.intent.action.REBOOT"; + /** + * @hide + * TODO: This will be unhidden in a later CL. + * Broadcast Action: The TextToSpeech synthesizer has completed processing + * all of the text in the speech queue. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_TTS_QUEUE_PROCESSING_COMPLETED = + "android.intent.action.TTS_QUEUE_PROCESSING_COMPLETED"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Standard intent categories (see addCategory()). diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 85d877a..27783ef 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -235,6 +235,12 @@ public class ActivityInfo extends ComponentInfo public static final int CONFIG_ORIENTATION = 0x0080; /** * Bit in {@link #configChanges} that indicates that the activity + * can itself handle changes to the screen layout. Set from the + * {@link android.R.attr#configChanges} attribute. + */ + public static final int CONFIG_SCREEN_LAYOUT = 0x0100; + /** + * Bit in {@link #configChanges} that indicates that the activity * can itself handle changes to the font scaling factor. Set from the * {@link android.R.attr#configChanges} attribute. This is * not a core resource configutation, but a higher-level value, so its @@ -248,8 +254,8 @@ public class ActivityInfo extends ComponentInfo * Contains any combination of {@link #CONFIG_FONT_SCALE}, * {@link #CONFIG_MCC}, {@link #CONFIG_MNC}, * {@link #CONFIG_LOCALE}, {@link #CONFIG_TOUCHSCREEN}, - * {@link #CONFIG_KEYBOARD}, {@link #CONFIG_NAVIGATION}, and - * {@link #CONFIG_ORIENTATION}. Set from the + * {@link #CONFIG_KEYBOARD}, {@link #CONFIG_NAVIGATION}, + * {@link #CONFIG_ORIENTATION}, and {@link #CONFIG_SCREEN_LAYOUT}. Set from the * {@link android.R.attr#configChanges} attribute. */ public int configChanges; diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 2a2cf93..bcf95b6 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -138,10 +138,27 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { /** * Value for {@link #flags}: true when the application's window can be - * expanded over default window size in target density (320x480 for - * 1.0 density, 480x720 for 1.5 density etc) + * reduced in size for smaller screens. Corresponds to + * {@link android.R.styleable#AndroidManifestSupportsScreens_smallScreens + * android:smallScreens}. */ - public static final int FLAG_SUPPORTS_LARGE_SCREENS = 1<<9; + public static final int FLAG_SUPPORTS_SMALL_SCREENS = 1<<9; + + /** + * Value for {@link #flags}: true when the application's window can be + * displayed on normal screens. Corresponds to + * {@link android.R.styleable#AndroidManifestSupportsScreens_normalScreens + * android:normalScreens}. + */ + public static final int FLAG_SUPPORTS_NORMAL_SCREENS = 1<<10; + + /** + * Value for {@link #flags}: true when the application's window can be + * increased in size for larger screens. Corresponds to + * {@link android.R.styleable#AndroidManifestSupportsScreens_largeScreens + * android:smallScreens}. + */ + public static final int FLAG_SUPPORTS_LARGE_SCREENS = 1<<11; /** * Value for {@link #flags}: this is false if the application has set @@ -149,7 +166,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * * {@hide} */ - public static final int FLAG_ALLOW_BACKUP = 1<<10; + public static final int FLAG_ALLOW_BACKUP = 1<<12; /** * Indicates that the application supports any densities; @@ -164,7 +181,9 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * {@link #FLAG_PERSISTENT}, {@link #FLAG_FACTORY_TEST}, and * {@link #FLAG_ALLOW_TASK_REPARENTING} * {@link #FLAG_ALLOW_CLEAR_USER_DATA}, {@link #FLAG_UPDATED_SYSTEM_APP}, - * {@link #FLAG_TEST_ONLY}. + * {@link #FLAG_TEST_ONLY}, {@link #FLAG_SUPPORTS_SMALL_SCREENS}, + * {@link #FLAG_SUPPORTS_NORMAL_SCREENS}, + * {@link #FLAG_SUPPORTS_LARGE_SCREENS}. */ public int flags = 0; diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index ab9518e..558b0c3 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -668,6 +668,11 @@ public class PackageParser { } sa.recycle(); + // Resource boolean are -1, so 1 means we don't know the value. + int supportsSmallScreens = 1; + int supportsNormalScreens = 1; + int supportsLargeScreens = 1; + int outerDepth = parser.getDepth(); while ((type=parser.next()) != parser.END_DOCUMENT && (type != parser.END_TAG || parser.getDepth() > outerDepth)) { @@ -876,8 +881,24 @@ public class PackageParser { XmlUtils.skipCurrentTag(parser); - } else if (tagName.equals("expandable")) { - pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS; + } else if (tagName.equals("supports-screens")) { + sa = res.obtainAttributes(attrs, + com.android.internal.R.styleable.AndroidManifestSupportsScreens); + + // This is a trick to get a boolean and still able to detect + // if a value was actually set. + supportsSmallScreens = sa.getInteger( + com.android.internal.R.styleable.AndroidManifestSupportsScreens_smallScreens, + supportsSmallScreens); + supportsNormalScreens = sa.getInteger( + com.android.internal.R.styleable.AndroidManifestSupportsScreens_normalScreens, + supportsNormalScreens); + supportsLargeScreens = sa.getInteger( + com.android.internal.R.styleable.AndroidManifestSupportsScreens_largeScreens, + supportsLargeScreens); + + sa.recycle(); + XmlUtils.skipCurrentTag(parser); } else { Log.w(TAG, "Bad element under <manifest>: " @@ -910,7 +931,20 @@ public class PackageParser { pkg.usesLibraryFiles = new String[pkg.usesLibraries.size()]; pkg.usesLibraries.toArray(pkg.usesLibraryFiles); } - // TODO: enable all density & expandable if target sdk is higher than donut + + if (supportsSmallScreens < 0 || (supportsSmallScreens > 0 + && pkg.applicationInfo.targetSdkVersion + >= android.os.Build.VERSION_CODES.CUR_DEVELOPMENT)) { + pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS; + } + if (supportsNormalScreens != 0) { + pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS; + } + if (supportsLargeScreens < 0 || (supportsLargeScreens > 0 + && pkg.applicationInfo.targetSdkVersion + >= android.os.Build.VERSION_CODES.CUR_DEVELOPMENT)) { + pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS; + } int size = pkg.supportsDensityList.size(); if (size > 0) { diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 1c91736..5c7b01f 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -601,7 +601,7 @@ public final class AssetManager { public native final void setConfiguration(int mcc, int mnc, String locale, int orientation, int touchscreen, int density, int keyboard, int keyboardHidden, int navigation, int screenWidth, int screenHeight, - int majorVersion); + int screenLayout, int majorVersion); /** * Retrieve the resource identifier for the given resource name. diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java index 680fef8..4e6fe07 100644 --- a/core/java/android/content/res/CompatibilityInfo.java +++ b/core/java/android/content/res/CompatibilityInfo.java @@ -17,8 +17,15 @@ package android.content.res; import android.content.pm.ApplicationInfo; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.Region; import android.util.DisplayMetrics; +import android.util.Log; import android.view.Gravity; +import android.view.MotionEvent; +import android.view.WindowManager; +import android.view.WindowManager.LayoutParams; /** * CompatibilityInfo class keeps the information about compatibility mode that the application is @@ -27,6 +34,9 @@ import android.view.Gravity; * {@hide} */ public class CompatibilityInfo { + private static final boolean DBG = false; + private static final String TAG = "CompatibilityInfo"; + /** default compatibility info object for compatible applications */ public static final CompatibilityInfo DEFAULT_COMPATIBILITY_INFO = new CompatibilityInfo(); @@ -41,36 +51,81 @@ public class CompatibilityInfo { public static final int DEFAULT_PORTRAIT_HEIGHT = 480; /** - * Application's scale. + * The x-shift mode that controls the position of the content or the window under + * compatibility mode. + * {@see getTranslator} + * {@see Translator#mShiftMode} */ - public final float mApplicationScale; + private static final int X_SHIFT_NONE = 0; + private static final int X_SHIFT_CONTENT = 1; + private static final int X_SHIFT_AND_CLIP_CONTENT = 2; + private static final int X_SHIFT_WINDOW = 3; + /** - * Application's inverted scale. + * A compatibility flags */ - public final float mApplicationInvertedScale; + private int mCompatibilityFlags; /** - * A boolean flag to indicates that the application can expand over the original size. + * A flag mask to tell if the application needs scaling (when mApplicationScale != 1.0f) + * {@see compatibilityFlag} + */ + private static final int SCALING_REQUIRED = 1; + + /** + * A flag mask to indicates that the application can expand over the original size. * The flag is set to true if * 1) Application declares its expandable in manifest file using <expandable /> or * 2) The screen size is same as (320 x 480) * density. + * {@see compatibilityFlag} */ - public boolean mExpandable; + private static final int EXPANDABLE = 2; + + /** + * A flag mask to tell if the application is configured to be expandable. This differs + * from EXPANDABLE in that the application that is not expandable will be + * marked as expandable if it runs in (320x 480) * density screen size. + */ + private static final int CONFIGURED_EXPANDABLE = 4; + + private static final int SCALING_EXPANDABLE_MASK = SCALING_REQUIRED | EXPANDABLE; /** - * A expandable flag in the configuration. + * Application's scale. */ - public final boolean mConfiguredExpandable; + public final float applicationScale; + + /** + * Application's inverted scale. + */ + public final float applicationInvertedScale; + + /** + * The flags from ApplicationInfo. + */ + public final int appFlags; + + /** + * Window size in Compatibility Mode, in real pixels. This is updated by + * {@link DisplayMetrics#updateMetrics}. + */ + private int mWidth; + private int mHeight; /** - * A boolean flag to tell if the application needs scaling (when mApplicationScale != 1.0f) + * The x offset to center the window content. In X_SHIFT_WINDOW mode, the offset is added + * to the window's layout. In X_SHIFT_CONTENT/X_SHIFT_AND_CLIP_CONTENT mode, the offset + * is used to translate the Canvas. */ - public final boolean mScalingRequired; + private int mXOffset; public CompatibilityInfo(ApplicationInfo appInfo) { - mExpandable = mConfiguredExpandable = - (appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0; + appFlags = appInfo.flags; + + if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) { + mCompatibilityFlags = EXPANDABLE | CONFIGURED_EXPANDABLE; + } float packageDensityScale = -1.0f; if (appInfo.supportsDensities != null) { @@ -93,23 +148,326 @@ public class CompatibilityInfo { } } if (packageDensityScale > 0.0f) { - mApplicationScale = packageDensityScale; + applicationScale = packageDensityScale; } else { - mApplicationScale = DisplayMetrics.DEVICE_DENSITY / (float) DisplayMetrics.DEFAULT_DENSITY; + applicationScale = + DisplayMetrics.DEVICE_DENSITY / (float) DisplayMetrics.DEFAULT_DENSITY; + } + applicationInvertedScale = 1.0f / applicationScale; + if (applicationScale != 1.0f) { + mCompatibilityFlags |= SCALING_REQUIRED; } - mApplicationInvertedScale = 1.0f / mApplicationScale; - mScalingRequired = mApplicationScale != 1.0f; } private CompatibilityInfo() { - mApplicationScale = mApplicationInvertedScale = 1.0f; - mExpandable = mConfiguredExpandable = true; - mScalingRequired = false; + appFlags = ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS + | ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS + | ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS; + applicationScale = applicationInvertedScale = 1.0f; + mCompatibilityFlags = EXPANDABLE | CONFIGURED_EXPANDABLE; } + /** + * Sets the application's visible rect in compatibility mode. + * @param xOffset the application's x offset that is added to center the content. + * @param widthPixels the application's width in real pixels on the screen. + * @param heightPixels the application's height in real pixels on the screen. + */ + public void setVisibleRect(int xOffset, int widthPixels, int heightPixels) { + this.mXOffset = xOffset; + mWidth = widthPixels; + mHeight = heightPixels; + } + + /** + * Sets expandable bit in the compatibility flag. + */ + public void setExpandable(boolean expandable) { + if (expandable) { + mCompatibilityFlags |= CompatibilityInfo.EXPANDABLE; + } else { + mCompatibilityFlags &= ~CompatibilityInfo.EXPANDABLE; + } + } + + /** + * @return true if the application is configured to be expandable. + */ + public boolean isConfiguredExpandable() { + return (mCompatibilityFlags & CompatibilityInfo.CONFIGURED_EXPANDABLE) != 0; + } + + /** + * @return true if the scaling is required + */ + public boolean isScalingRequired() { + return (mCompatibilityFlags & SCALING_REQUIRED) != 0; + } + @Override public String toString() { - return "CompatibilityInfo{scale=" + mApplicationScale + - ", expandable=" + mExpandable + "}"; + return "CompatibilityInfo{scale=" + applicationScale + + ", compatibility flag=" + mCompatibilityFlags + "}"; + } + + /** + * Returns the translator which can translate the coordinates of the window. + * There are five different types of Translator. + * + * 1) {@link CompatibilityInfo#X_SHIFT_AND_CLIP_CONTENT} + * Shift and clip the content of the window at drawing time. Used for activities' + * main window (with no gravity). + * 2) {@link CompatibilityInfo#X_SHIFT_CONTENT} + * Shift the content of the window at drawing time. Used for windows that is created by + * an application and expected to be aligned with the application window. + * 3) {@link CompatibilityInfo#X_SHIFT_WINDOW} + * Create the window with adjusted x- coordinates. This is typically used + * in popup window, where it has to be placed relative to main window. + * 4) {@link CompatibilityInfo#X_SHIFT_NONE} + * No adjustment required, such as dialog. + * 5) Same as X_SHIFT_WINDOW, but no scaling. This is used by {@link SurfaceView}, which + * does not require scaling, but its window's location has to be adjusted. + * + * @param params the window's parameter + */ + public Translator getTranslator(WindowManager.LayoutParams params) { + if ( (mCompatibilityFlags & CompatibilityInfo.SCALING_EXPANDABLE_MASK) + == CompatibilityInfo.EXPANDABLE) { + if (DBG) Log.d(TAG, "no translation required"); + return null; + } + + if ((mCompatibilityFlags & CompatibilityInfo.EXPANDABLE) == 0) { + if ((params.flags & WindowManager.LayoutParams.FLAG_NO_COMPATIBILITY_SCALING) != 0) { + if (DBG) Log.d(TAG, "translation for surface view selected"); + return new Translator(X_SHIFT_WINDOW, false, 1.0f, 1.0f); + } else { + int shiftMode; + if (params.gravity == Gravity.NO_GRAVITY) { + // For Regular Application window + shiftMode = X_SHIFT_AND_CLIP_CONTENT; + if (DBG) Log.d(TAG, "shift and clip translator"); + } else if (params.width == WindowManager.LayoutParams.FILL_PARENT) { + // For Regular Application window + shiftMode = X_SHIFT_CONTENT; + if (DBG) Log.d(TAG, "shift content translator"); + } else if ((params.gravity & Gravity.LEFT) != 0 && params.x > 0) { + shiftMode = X_SHIFT_WINDOW; + if (DBG) Log.d(TAG, "shift window translator"); + } else { + shiftMode = X_SHIFT_NONE; + if (DBG) Log.d(TAG, "no content/window translator"); + } + return new Translator(shiftMode); + } + } else if (isScalingRequired()) { + return new Translator(); + } else { + return null; + } + } + + /** + * A helper object to translate the screen and window coordinates back and forth. + * @hide + */ + public class Translator { + final private int mShiftMode; + final public boolean scalingRequired; + final public float applicationScale; + final public float applicationInvertedScale; + + private Rect mContentInsetsBuffer = null; + private Rect mVisibleInsets = null; + + Translator(int shiftMode, boolean scalingRequired, float applicationScale, + float applicationInvertedScale) { + mShiftMode = shiftMode; + this.scalingRequired = scalingRequired; + this.applicationScale = applicationScale; + this.applicationInvertedScale = applicationInvertedScale; + } + + Translator(int shiftMode) { + this(shiftMode, + isScalingRequired(), + CompatibilityInfo.this.applicationScale, + CompatibilityInfo.this.applicationInvertedScale); + } + + Translator() { + this(X_SHIFT_NONE); + } + + /** + * Translate the screen rect to the application frame. + */ + public void translateRectInScreenToAppWinFrame(Rect rect) { + if (rect.isEmpty()) return; // skip if the window size is empty. + switch (mShiftMode) { + case X_SHIFT_AND_CLIP_CONTENT: + rect.intersect(0, 0, mWidth, mHeight); + break; + case X_SHIFT_CONTENT: + rect.intersect(0, 0, mWidth + mXOffset, mHeight); + break; + case X_SHIFT_WINDOW: + case X_SHIFT_NONE: + break; + } + if (scalingRequired) { + rect.scale(applicationInvertedScale); + } + } + + /** + * Translate the region in window to screen. + */ + public void translateRegionInWindowToScreen(Region transparentRegion) { + switch (mShiftMode) { + case X_SHIFT_AND_CLIP_CONTENT: + case X_SHIFT_CONTENT: + transparentRegion.scale(applicationScale); + transparentRegion.translate(mXOffset, 0); + break; + case X_SHIFT_WINDOW: + case X_SHIFT_NONE: + transparentRegion.scale(applicationScale); + } + } + + /** + * Apply translation to the canvas that is necessary to draw the content. + */ + public void translateCanvas(Canvas canvas) { + if (mShiftMode == X_SHIFT_CONTENT || + mShiftMode == X_SHIFT_AND_CLIP_CONTENT) { + // TODO: clear outside when rotation is changed. + + // Translate x-offset only when the content is shifted. + canvas.translate(mXOffset, 0); + } + if (scalingRequired) { + canvas.scale(applicationScale, applicationScale); + } + } + + /** + * Translate the motion event captured on screen to the application's window. + */ + public void translateEventInScreenToAppWindow(MotionEvent event) { + if (mShiftMode == X_SHIFT_CONTENT || + mShiftMode == X_SHIFT_AND_CLIP_CONTENT) { + event.translate(-mXOffset, 0); + } + if (scalingRequired) { + event.scale(applicationInvertedScale); + } + } + + /** + * Translate the window's layout parameter, from application's view to + * Screen's view. + */ + public void translateWindowLayout(WindowManager.LayoutParams params) { + switch (mShiftMode) { + case X_SHIFT_NONE: + case X_SHIFT_AND_CLIP_CONTENT: + case X_SHIFT_CONTENT: + params.scale(applicationScale); + break; + case X_SHIFT_WINDOW: + params.scale(applicationScale); + params.x += mXOffset; + break; + } + } + + /** + * Translate a Rect in application's window to screen. + */ + public void translateRectInAppWindowToScreen(Rect rect) { + // TODO Auto-generated method stub + if (scalingRequired) { + rect.scale(applicationScale); + } + switch(mShiftMode) { + case X_SHIFT_NONE: + case X_SHIFT_WINDOW: + break; + case X_SHIFT_CONTENT: + case X_SHIFT_AND_CLIP_CONTENT: + rect.offset(mXOffset, 0); + break; + } + } + + /** + * Translate a Rect in screen coordinates into the app window's coordinates. + */ + public void translateRectInScreenToAppWindow(Rect rect) { + switch (mShiftMode) { + case X_SHIFT_NONE: + case X_SHIFT_WINDOW: + break; + case X_SHIFT_CONTENT: { + rect.intersects(mXOffset, 0, rect.right, rect.bottom); + int dx = Math.min(mXOffset, rect.left); + rect.offset(-dx, 0); + break; + } + case X_SHIFT_AND_CLIP_CONTENT: { + rect.intersects(mXOffset, 0, mWidth + mXOffset, mHeight); + int dx = Math.min(mXOffset, rect.left); + rect.offset(-dx, 0); + break; + } + } + if (scalingRequired) { + rect.scale(applicationInvertedScale); + } + } + + /** + * Translate the location of the sub window. + * @param params + */ + public void translateLayoutParamsInAppWindowToScreen(LayoutParams params) { + if (scalingRequired) { + params.scale(applicationScale); + } + switch (mShiftMode) { + // the window location on these mode does not require adjustmenet. + case X_SHIFT_NONE: + case X_SHIFT_WINDOW: + break; + case X_SHIFT_CONTENT: + case X_SHIFT_AND_CLIP_CONTENT: + params.x += mXOffset; + break; + } + } + + /** + * Translate the content insets in application window to Screen. This uses + * the internal buffer for content insets to avoid extra object allocation. + */ + public Rect getTranslatedContentInsets(Rect contentInsets) { + if (mContentInsetsBuffer == null) mContentInsetsBuffer = new Rect(); + mContentInsetsBuffer.set(contentInsets); + translateRectInAppWindowToScreen(mContentInsetsBuffer); + return mContentInsetsBuffer; + } + + /** + * Translate the visible insets in application window to Screen. This uses + * the internal buffer for content insets to avoid extra object allocation. + */ + public Rect getTranslatedVisbileInsets(Rect visibleInsets) { + if (mVisibleInsets == null) mVisibleInsets = new Rect(); + mVisibleInsets.set(visibleInsets); + translateRectInAppWindowToScreen(mVisibleInsets); + return mVisibleInsets; + } } -} +}
\ No newline at end of file diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index bb3486c..577aa60 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -116,6 +116,18 @@ public final class Configuration implements Parcelable, Comparable<Configuration */ public int orientation; + public static final int SCREENLAYOUT_UNDEFINED = 0; + public static final int SCREENLAYOUT_SMALL = 1; + public static final int SCREENLAYOUT_NORMAL = 2; + public static final int SCREENLAYOUT_LARGE = 3; + + /** + * Overall layout of the screen. May be one of + * {@link #SCREENLAYOUT_SMALL}, {@link #SCREENLAYOUT_NORMAL}, + * or {@link #SCREENLAYOUT_LARGE}. + */ + public int screenLayout; + /** * Construct an invalid Configuration. You must call {@link #setToDefaults} * for this object to be valid. {@more} @@ -141,6 +153,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration hardKeyboardHidden = o.hardKeyboardHidden; navigation = o.navigation; orientation = o.orientation; + screenLayout = o.screenLayout; } public String toString() { @@ -165,6 +178,8 @@ public final class Configuration implements Parcelable, Comparable<Configuration sb.append(navigation); sb.append(" orien="); sb.append(orientation); + sb.append(" layout="); + sb.append(screenLayout); sb.append('}'); return sb.toString(); } @@ -183,6 +198,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration hardKeyboardHidden = HARDKEYBOARDHIDDEN_UNDEFINED; navigation = NAVIGATION_UNDEFINED; orientation = ORIENTATION_UNDEFINED; + screenLayout = SCREENLAYOUT_UNDEFINED; } /** {@hide} */ @@ -253,6 +269,11 @@ public final class Configuration implements Parcelable, Comparable<Configuration changed |= ActivityInfo.CONFIG_ORIENTATION; orientation = delta.orientation; } + if (delta.screenLayout != SCREENLAYOUT_UNDEFINED + && screenLayout != delta.screenLayout) { + changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT; + screenLayout = delta.screenLayout; + } return changed; } @@ -276,9 +297,11 @@ public final class Configuration implements Parcelable, Comparable<Configuration * {@link android.content.pm.ActivityInfo#CONFIG_KEYBOARD * PackageManager.ActivityInfo.CONFIG_KEYBOARD}, * {@link android.content.pm.ActivityInfo#CONFIG_NAVIGATION - * PackageManager.ActivityInfo.CONFIG_NAVIGATION}, or + * PackageManager.ActivityInfo.CONFIG_NAVIGATION}, * {@link android.content.pm.ActivityInfo#CONFIG_ORIENTATION - * PackageManager.ActivityInfo.CONFIG_ORIENTATION}. + * PackageManager.ActivityInfo.CONFIG_ORIENTATION}, or + * {@link android.content.pm.ActivityInfo#CONFIG_SCREEN_LAYOUT + * PackageManager.ActivityInfo.CONFIG_SCREEN_LAYOUT}. */ public int diff(Configuration delta) { int changed = 0; @@ -319,6 +342,10 @@ public final class Configuration implements Parcelable, Comparable<Configuration && orientation != delta.orientation) { changed |= ActivityInfo.CONFIG_ORIENTATION; } + if (delta.screenLayout != SCREENLAYOUT_UNDEFINED + && screenLayout != delta.screenLayout) { + changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT; + } return changed; } @@ -368,6 +395,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration dest.writeInt(hardKeyboardHidden); dest.writeInt(navigation); dest.writeInt(orientation); + dest.writeInt(screenLayout); } public static final Parcelable.Creator<Configuration> CREATOR @@ -399,6 +427,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration hardKeyboardHidden = source.readInt(); navigation = source.readInt(); orientation = source.readInt(); + screenLayout = source.readInt(); } public int compareTo(Configuration that) { @@ -428,6 +457,8 @@ public final class Configuration implements Parcelable, Comparable<Configuration n = this.navigation - that.navigation; if (n != 0) return n; n = this.orientation - that.orientation; + if (n != 0) return n; + n = this.screenLayout - that.screenLayout; //if (n != 0) return n; return n; } @@ -450,6 +481,6 @@ public final class Configuration implements Parcelable, Comparable<Configuration return ((int)this.fontScale) + this.mcc + this.mnc + this.locale.hashCode() + this.touchscreen + this.keyboard + this.keyboardHidden + this.hardKeyboardHidden - + this.navigation + this.orientation; + + this.navigation + this.orientation + this.screenLayout; } } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 71dbd38..d7512bb 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -158,10 +158,10 @@ public class Resources { } updateConfiguration(config, metrics); assets.ensureStringBlocks(); - if (!mCompatibilityInfo.mScalingRequired) { - mPreloadedDrawables = sPreloadedDrawables; - } else { + if (mCompatibilityInfo.isScalingRequired()) { mPreloadedDrawables = emptySparseArray(); + } else { + mPreloadedDrawables = sPreloadedDrawables; } } @@ -1267,7 +1267,8 @@ public class Resources { } if (metrics != null) { mMetrics.setTo(metrics); - mMetrics.updateMetrics(mCompatibilityInfo, mConfiguration.orientation); + mMetrics.updateMetrics(mCompatibilityInfo, + mConfiguration.orientation, mConfiguration.screenLayout); } mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale; @@ -1299,7 +1300,7 @@ public class Resources { mConfiguration.touchscreen, (int)(mMetrics.density*160), mConfiguration.keyboard, keyboardHidden, mConfiguration.navigation, width, height, - sSdkVersion); + mConfiguration.screenLayout, sSdkVersion); int N = mDrawableCache.size(); if (DEBUG_CONFIG) { Log.d(TAG, "Cleaning up drawables config changes: 0x" diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index ca579b6..09fbc97 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -39,13 +39,16 @@ import android.os.Message; public class Camera { private static final String TAG = "Camera"; - // These match the enum in libs/android_runtime/android_hardware_Camera.cpp - private static final int SHUTTER_CALLBACK = 0; - private static final int RAW_PICTURE_CALLBACK = 1; - private static final int JPEG_PICTURE_CALLBACK = 2; - private static final int PREVIEW_CALLBACK = 3; - private static final int AUTOFOCUS_CALLBACK = 4; - private static final int ERROR_CALLBACK = 5; + // These match the enums in frameworks/base/include/ui/Camera.h + private static final int CAMERA_MSG_ERROR = 0; + private static final int CAMERA_MSG_SHUTTER = 1; + private static final int CAMERA_MSG_FOCUS = 2; + private static final int CAMERA_MSG_ZOOM = 3; + private static final int CAMERA_MSG_PREVIEW_FRAME = 4; + private static final int CAMERA_MSG_VIDEO_FRAME = 5; + private static final int CAMERA_MSG_POSTVIEW_FRAME = 6; + private static final int CAMERA_MSG_RAW_IMAGE = 7; + private static final int CAMERA_MSG_COMPRESSED_IMAGE = 8; private int mNativeContext; // accessed by native methods private EventHandler mEventHandler; @@ -231,22 +234,23 @@ public class Camera { @Override public void handleMessage(Message msg) { switch(msg.what) { - case SHUTTER_CALLBACK: + case CAMERA_MSG_SHUTTER: if (mShutterCallback != null) { mShutterCallback.onShutter(); } return; - case RAW_PICTURE_CALLBACK: + + case CAMERA_MSG_RAW_IMAGE: if (mRawImageCallback != null) mRawImageCallback.onPictureTaken((byte[])msg.obj, mCamera); return; - case JPEG_PICTURE_CALLBACK: + case CAMERA_MSG_COMPRESSED_IMAGE: if (mJpegCallback != null) mJpegCallback.onPictureTaken((byte[])msg.obj, mCamera); return; - case PREVIEW_CALLBACK: + case CAMERA_MSG_PREVIEW_FRAME: if (mPreviewCallback != null) { mPreviewCallback.onPreviewFrame((byte[])msg.obj, mCamera); if (mOneShot) { @@ -255,12 +259,12 @@ public class Camera { } return; - case AUTOFOCUS_CALLBACK: + case CAMERA_MSG_FOCUS: if (mAutoFocusCallback != null) mAutoFocusCallback.onAutoFocus(msg.arg1 == 0 ? false : true, mCamera); return; - case ERROR_CALLBACK: + case CAMERA_MSG_ERROR : Log.e(TAG, "Error " + msg.arg1); if (mErrorCallback != null) mErrorCallback.onError(msg.arg1, mCamera); diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java index 6c13582..abfb274 100644 --- a/core/java/android/os/AsyncTask.java +++ b/core/java/android/os/AsyncTask.java @@ -127,12 +127,12 @@ import java.util.concurrent.atomic.AtomicInteger; public abstract class AsyncTask<Params, Progress, Result> { private static final String LOG_TAG = "AsyncTask"; - private static final int CORE_POOL_SIZE = 1; - private static final int MAXIMUM_POOL_SIZE = 10; + private static final int CORE_POOL_SIZE = 5; + private static final int MAXIMUM_POOL_SIZE = 128; private static final int KEEP_ALIVE = 10; private static final BlockingQueue<Runnable> sWorkQueue = - new LinkedBlockingQueue<Runnable>(MAXIMUM_POOL_SIZE); + new LinkedBlockingQueue<Runnable>(10); private static final ThreadFactory sThreadFactory = new ThreadFactory() { private final AtomicInteger mCount = new AtomicInteger(1); diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index 8fcb4d7..d40ea6b 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -21,6 +21,7 @@ import com.android.internal.util.TypedProperties; import android.util.Config; import android.util.Log; +import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; @@ -378,6 +379,20 @@ 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 + */ + public static void startMethodTracing(String traceName, FileDescriptor fd, + int bufferSize, int flags) { + VMDebug.startMethodTracing(traceName, fd, bufferSize, flags); + } + + /** * Determine whether method tracing is currently active. * @hide */ diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 333c7cb..1214abc 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -573,7 +573,21 @@ public class Process { * directly to a gid. */ public static final native int getGidForName(String name); - + + /** + * Returns a uid for a currently running process. + * @param pid the process id + * @return the uid of the process, or -1 if the process is not running. + * @hide pending API council review + */ + public static final int getUidForPid(int pid) { + String[] procStatusLabels = { "Uid:" }; + long[] procStatusValues = new long[1]; + procStatusValues[0] = -1; + Process.readProcLines("/proc/" + pid + "/status", procStatusLabels, procStatusValues); + return (int) procStatusValues[0]; + } + /** * Set the priority of a thread, based on Linux priorities. * diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 5dc03eb..7356326 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1274,6 +1274,50 @@ public final class Settings { public static final String DTMF_TONE_WHEN_DIALING = "dtmf_tone"; /** + * CDMA only settings + * DTMF tone type played by the dialer when dialing. + * 0 = Normal + * 1 = Long + * @hide + */ + public static final String DTMF_TONE_TYPE_WHEN_DIALING = "dtmf_tone_type"; + + /** + * CDMA only settings + * Emergency Tone 0 = Off + * 1 = Alert + * 2 = Vibrate + * @hide + */ + public static final String EMERGENCY_TONE = "emergency_tone"; + + /** + * CDMA only settings + * Whether the auto retry is enabled. The value is + * boolean (1 or 0). + * @hide + */ + public static final String CALL_AUTO_RETRY = "call_auto_retry"; + + /** + * Whether the hearing aid is enabled. The value is + * boolean (1 or 0). + * @hide + */ + public static final String HEARING_AID = "hearing_aid"; + + /** + * CDMA only settings + * TTY Mode + * 0 = OFF + * 1 = FULL + * 2 = VCO + * 3 = HCO + * @hide + */ + public static final String TTY_MODE = "tty_mode"; + + /** * Whether the sounds effects (key clicks, lid open ...) are enabled. The value is * boolean (1 or 0). */ diff --git a/core/java/android/server/search/Searchables.java b/core/java/android/server/search/Searchables.java index a27667b..c7cc8ed 100644 --- a/core/java/android/server/search/Searchables.java +++ b/core/java/android/server/search/Searchables.java @@ -57,6 +57,11 @@ public class Searchables { private SearchableInfo mDefaultSearchable = null; private SearchableInfo mDefaultSearchableForWebSearch = null; + public static String GOOGLE_SEARCH_COMPONENT_NAME = + "com.android.googlesearch/.GoogleSearch"; + public static String ENHANCED_GOOGLE_SEARCH_COMPONENT_NAME = + "com.google.android.providers.enhancedgooglesearch/.Launcher"; + /** * * @param context Context to use for looking up activities etc. @@ -341,6 +346,18 @@ public class Searchables { return component; } } + } else { + // If the current preferred activity is GoogleSearch, and we detect + // EnhancedGoogleSearch installed as well, set the latter as preferred since that + // is a superset and provides more functionality. + ComponentName cn = new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name); + if (cn.flattenToShortString().equals(GOOGLE_SEARCH_COMPONENT_NAME)) { + ComponentName enhancedGoogleSearch = ComponentName.unflattenFromString( + ENHANCED_GOOGLE_SEARCH_COMPONENT_NAME); + if (setPreferredActivity(enhancedGoogleSearch, Intent.ACTION_WEB_SEARCH)) { + return enhancedGoogleSearch; + } + } } if (ri == null) return null; diff --git a/core/java/android/speech/tts/ITts.aidl b/core/java/android/speech/tts/ITts.aidl index 02211fd..47976e5 100755 --- a/core/java/android/speech/tts/ITts.aidl +++ b/core/java/android/speech/tts/ITts.aidl @@ -29,8 +29,12 @@ import android.content.Intent; interface ITts {
void setSpeechRate(in int speechRate);
+ void setPitch(in int pitch);
+
void speak(in String text, in int queueMode, in String[] params);
+ void speakIpa(in String ipaText, in int queueMode, in String[] params);
+
boolean isSpeaking();
void stop();
@@ -41,7 +45,9 @@ interface ITts { void setLanguage(in String language, in String country, in String variant);
- boolean synthesizeToFile(in String text, in String[] params, in String outputDirectory);
+ boolean synthesizeToFile(in String text, in String[] params, in String outputDirectory); +
+ boolean synthesizeIpaToFile(in String ipaText, in String[] params, in String outputDirectory);
void playEarcon(in String earcon, in int queueMode, in String[] params);
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index 9fc143d..1502d98 100755 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -46,10 +46,6 @@ public class TextToSpeech { * Denotes a generic operation failure. */ public static final int TTS_ERROR = -1; - /** - * Denotes a failure due to a missing resource. - */ - public static final int TTS_ERROR_MISSING_RESOURCE = -2; /** * Queue mode where all entries in the playback queue (media to be played @@ -61,6 +57,37 @@ public class TextToSpeech { */ public static final int TTS_QUEUE_ADD = 1; + + /** + * Denotes the language is available exactly as specified by the locale + */ + public static final int TTS_LANG_COUNTRY_VAR_AVAILABLE = 2; + + + /** + * Denotes the language is available for the language and country specified + * by the locale, but not the variant. + */ + public static final int TTS_LANG_COUNTRY_AVAILABLE = 1; + + + /** + * Denotes the language is available for the language by the locale, + * but not the country and variant. + */ + public static final int TTS_LANG_AVAILABLE = 0; + + /** + * Denotes the language data is missing. + */ + public static final int TTS_LANG_MISSING_DATA = -1; + + /** + * Denotes the language is not supported by the current TTS engine. + */ + public static final int TTS_LANG_NOT_SUPPORTED = -2; + + /** * Called when the TTS has initialized. * @@ -72,15 +99,6 @@ public class TextToSpeech { } /** - * Called when the TTS has finished speaking by itself (speaking - * finished without being canceled). - * - */ - public interface OnSpeechCompletedListener { - public void onSpeechCompleted(); - } - - /** * Internal constants for the TTS functionality * * {@hide} @@ -100,6 +118,16 @@ public class TextToSpeech { public static final int CHECK_VOICE_DATA_BAD_DATA = -1; public static final int CHECK_VOICE_DATA_MISSING_DATA = -2; public static final int CHECK_VOICE_DATA_MISSING_DATA_NO_SDCARD = -3; + + // keys for the parameters passed with speak commands + public static final String TTS_KEY_PARAM_RATE = "rate"; + public static final String TTS_KEY_PARAM_LANGUAGE = "language"; + public static final String TTS_KEY_PARAM_COUNTRY = "country"; + public static final String TTS_KEY_PARAM_VARIANT = "variant"; + public static final int TTS_PARAM_POSITION_RATE = 0; + public static final int TTS_PARAM_POSITION_LANGUAGE = 2; + public static final int TTS_PARAM_POSITION_COUNTRY = 4; + public static final int TTS_PARAM_POSITION_VARIANT = 6; } /** @@ -112,11 +140,11 @@ public class TextToSpeech { private OnInitListener mInitListener = null; private boolean mStarted = false; private final Object mStartLock = new Object(); - private ITtsCallback mITtsCallback; - private OnSpeechCompletedListener mSpeechCompListener = null; - private final Object mSpeechCompListenerLock = new Object(); - - + private int mCachedRate = Engine.FALLBACK_TTS_DEFAULT_RATE; + private String mCachedLang = Engine.FALLBACK_TTS_DEFAULT_LANG; + private String mCachedCountry = Engine.FALLBACK_TTS_DEFAULT_COUNTRY; + private String mCachedVariant = Engine.FALLBACK_TTS_DEFAULT_VARIANT; + private String[] mCachedParams; /** * The constructor for the TTS. @@ -130,24 +158,23 @@ public class TextToSpeech { public TextToSpeech(Context context, OnInitListener listener) { mContext = context; mInitListener = listener; - initTts(); - } + mCachedParams = new String[2*4]; //4 parameters, store key and value + mCachedParams[Engine.TTS_PARAM_POSITION_RATE] = Engine.TTS_KEY_PARAM_RATE; + mCachedParams[Engine.TTS_PARAM_POSITION_LANGUAGE] = Engine.TTS_KEY_PARAM_LANGUAGE; + mCachedParams[Engine.TTS_PARAM_POSITION_COUNTRY] = Engine.TTS_KEY_PARAM_COUNTRY; + mCachedParams[Engine.TTS_PARAM_POSITION_VARIANT] = Engine.TTS_KEY_PARAM_VARIANT; + updateCachedParamArray(); - public void setOnSpeechCompletedListener(final OnSpeechCompletedListener listener) { - synchronized(mSpeechCompListenerLock) { - mSpeechCompListener = listener; - } + initTts(); } - private boolean dataFilesCheck() { - // TODO #TTS# config manager will be in settings - Log.i("TTS_FIXME", "FIXME in Tts: config manager will be in settings"); - // TODO #TTS# implement checking of the correct installation of - // the data files. - - return true; + private void updateCachedParamArray() { + mCachedParams[Engine.TTS_PARAM_POSITION_RATE+1] = String.valueOf(mCachedRate); + mCachedParams[Engine.TTS_PARAM_POSITION_LANGUAGE+1] = mCachedLang; + mCachedParams[Engine.TTS_PARAM_POSITION_COUNTRY+1] = mCachedCountry; + mCachedParams[Engine.TTS_PARAM_POSITION_VARIANT+1] = mCachedVariant; } @@ -159,34 +186,7 @@ public class TextToSpeech { public void onServiceConnected(ComponentName name, IBinder service) { synchronized(mStartLock) { mITts = ITts.Stub.asInterface(service); - try { - mITtsCallback = new ITtsCallback.Stub() { - public void markReached(String mark) - throws RemoteException { - // call the listener of that event, but not - // while locked. - OnSpeechCompletedListener listener = null; - synchronized(mSpeechCompListenerLock) { - listener = mSpeechCompListener; - } - if (listener != null) { - listener.onSpeechCompleted(); - } - } - }; - mITts.registerCallback(mITtsCallback); - - } catch (RemoteException e) { - initTts(); - return; - } - mStarted = true; - // The callback can become null if the Android OS decides to - // restart the TTS process as well as whatever is using it. - // In such cases, do nothing - the error handling from the - // speaking calls will kick in and force a proper restart of - // the TTS. if (mInitListener != null) { // TODO manage failures and missing resources mInitListener.onInit(TTS_SUCCESS); @@ -333,8 +333,8 @@ public class TextToSpeech { return; } try { - // TODO support extra parameters, passing null for the moment - mITts.speak(text, queueMode, null); + // TODO support extra parameters, passing cache of current parameters for the moment + mITts.speak(text, queueMode, mCachedParams); } catch (RemoteException e) { // TTS died; restart it. mStarted = false; @@ -353,6 +353,27 @@ public class TextToSpeech { /** + * Speaks the IPA string using the specified queuing strategy and speech + * parameters. Note that the speech parameters are not universally supported + * by all engines and will be treated as a hint. The TTS library will try to + * fulfill these parameters as much as possible, but there is no guarantee + * that the voice used will have the properties specified. + * + * @param ipaText + * The string of IPA text to be spoken. + * @param queueMode + * The queuing strategy to use. + * See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH. + * @param params + * The hashmap of speech parameters to be used. + */ + public void speakIpa(String ipaText, int queueMode, HashMap<String,String> params) + { + //TODO: Implement speakIpa + } + + + /** * Plays the earcon using the specified queueing mode and parameters. * * @param earcon @@ -450,7 +471,6 @@ public class TextToSpeech { } - /** * Sets the speech rate for the TTS engine. * @@ -471,7 +491,40 @@ public class TextToSpeech { } try { if (speechRate > 0) { - mITts.setSpeechRate((int)(speechRate*100)); + mCachedRate = (int)(speechRate*100); + updateCachedParamArray(); + mITts.setSpeechRate(mCachedRate); + } + } catch (RemoteException e) { + // TTS died; restart it. + mStarted = false; + initTts(); + } + } + } + + + /** + * Sets the speech pitch for the TTS engine. + * + * Note that the pitch is not universally supported by all engines and + * will be treated as a hint. The TTS library will try to use the specified + * pitch, but there is no guarantee. + * This has no effect on any pre-recorded speech. + * + * @param pitch + * The pitch for the TTS engine. 1 is the normal pitch, + * lower values lower the tone of the synthesized voice, + * greater values increase it. + */ + public void setPitch(float pitch) { + synchronized (mStartLock) { + if (!mStarted) { + return; + } + try { + if (pitch > 0) { + mITts.setPitch((int)(pitch*100)); } } catch (RemoteException e) { // TTS died; restart it. @@ -498,7 +551,11 @@ public class TextToSpeech { return; } try { - mITts.setLanguage(loc.getISO3Language(), loc.getISO3Country(), loc.getVariant()); + mCachedLang = loc.getISO3Language(); + mCachedCountry = loc.getISO3Country(); + mCachedVariant = loc.getVariant(); + updateCachedParamArray(); + mITts.setLanguage(mCachedLang, mCachedCountry, mCachedVariant); } catch (RemoteException e) { // TTS died; restart it. mStarted = false; @@ -507,6 +564,20 @@ public class TextToSpeech { } } + /** + * Checks if the specified language as represented by the locale is available. + * + * @param loc + * The locale describing the language to be used. + * @return one of TTS_LANG_NOT_SUPPORTED, TTS_LANG_MISSING_DATA, TTS_LANG_AVAILABLE, + TTS_LANG_COUNTRY_AVAILABLE, TTS_LANG_COUNTRY_VAR_AVAILABLE. + */ + public int isLanguageAvailable(Locale loc) { + //TODO: Implement isLanguageAvailable + return TTS_LANG_NOT_SUPPORTED; + } + + /** * Speaks the given text using the specified queueing mode and parameters. diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java index a095913..4179edb 100644 --- a/core/java/android/util/DisplayMetrics.java +++ b/core/java/android/util/DisplayMetrics.java @@ -103,58 +103,70 @@ public class DisplayMetrics { /** * Update the display metrics based on the compatibility info and orientation + * NOTE: DO NOT EXPOSE THIS API! It is introducing a circular dependency + * with the higher-level android.res package. * {@hide} */ - public void updateMetrics(CompatibilityInfo compatibilityInfo, int orientation) { - if (compatibilityInfo.mScalingRequired) { - float invertedRatio = compatibilityInfo.mApplicationInvertedScale; - density *= invertedRatio; - scaledDensity *= invertedRatio; - xdpi *= invertedRatio; - ydpi *= invertedRatio; - widthPixels *= invertedRatio; - heightPixels *= invertedRatio; - } - if (!compatibilityInfo.mConfiguredExpandable) { + public void updateMetrics(CompatibilityInfo compatibilityInfo, int orientation, + int screenLayout) { + int xOffset = 0; + if (!compatibilityInfo.isConfiguredExpandable()) { // Note: this assume that configuration is updated before calling // updateMetrics method. - int defaultWidth; - int defaultHeight; - switch (orientation) { - case Configuration.ORIENTATION_LANDSCAPE: { - defaultWidth = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_HEIGHT * density); - defaultHeight = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_WIDTH * density); - break; - } - case Configuration.ORIENTATION_PORTRAIT: - case Configuration.ORIENTATION_SQUARE: - default: { - defaultWidth = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_WIDTH * density); - defaultHeight = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_HEIGHT * density); - break; - } - case Configuration.ORIENTATION_UNDEFINED: { - // don't change - return; + if (screenLayout == Configuration.SCREENLAYOUT_LARGE) { + // This is a large screen device and the app is not + // compatible with large screens, to diddle it. + + compatibilityInfo.setExpandable(false); + // Figure out the compatibility width and height of the screen. + int defaultWidth; + int defaultHeight; + switch (orientation) { + case Configuration.ORIENTATION_LANDSCAPE: { + defaultWidth = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_HEIGHT * density); + defaultHeight = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_WIDTH * density); + break; + } + case Configuration.ORIENTATION_PORTRAIT: + case Configuration.ORIENTATION_SQUARE: + default: { + defaultWidth = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_WIDTH * density); + defaultHeight = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_HEIGHT * density); + break; + } + case Configuration.ORIENTATION_UNDEFINED: { + // don't change + return; + } } - } - - if (defaultWidth == widthPixels && defaultHeight == heightPixels) { - // the screen size is same as expected size. make it expandable - compatibilityInfo.mExpandable = true; - } else { - compatibilityInfo.mExpandable = false; - // adjust the size only when the device's screen is bigger. + if (defaultWidth < widthPixels) { + // content/window's x offset in original pixels + xOffset = ((widthPixels - defaultWidth) / 2); widthPixels = defaultWidth; } if (defaultHeight < heightPixels) { heightPixels = defaultHeight; } + + } else { + // the screen size is same as expected size. make it expandable + compatibilityInfo.setExpandable(true); } } + compatibilityInfo.setVisibleRect(xOffset, widthPixels, heightPixels); + if (compatibilityInfo.isScalingRequired()) { + float invertedRatio = compatibilityInfo.applicationInvertedScale; + density *= invertedRatio; + scaledDensity *= invertedRatio; + xdpi *= invertedRatio; + ydpi *= invertedRatio; + widthPixels *= invertedRatio; + heightPixels *= invertedRatio; + } } + @Override public String toString() { return "DisplayMetrics{density=" + density + ", width=" + widthPixels + ", height=" + heightPixels + ", scaledDensity=" + scaledDensity + diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index ca01448..a224ed3 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -212,28 +212,47 @@ public final class MotionEvent implements Parcelable { } /** - * Scales down the cood of this event by the given scale. + * Scales down the coordination of this event by the given scale. * * @hide */ public void scale(float scale) { - if (scale != 1.0f) { - mX *= scale; - mY *= scale; - mRawX *= scale; - mRawY *= scale; - mSize *= scale; - mXPrecision *= scale; - mYPrecision *= scale; - if (mHistory != null) { - float[] history = mHistory; - int length = history.length; - for (int i = 0; i < length; i += 4) { - history[i] *= scale; // X - // history[i + 2] == pressure - history[i + 1] *= scale; // Y - history[i + 3] *= scale; // Size, TODO: square this? - } + mX *= scale; + mY *= scale; + mRawX *= scale; + mRawY *= scale; + mSize *= scale; + mXPrecision *= scale; + mYPrecision *= scale; + if (mHistory != null) { + float[] history = mHistory; + int length = history.length; + for (int i = 0; i < length; i += 4) { + history[i] *= scale; // X + history[i + 1] *= scale; // Y + // no need to scale pressure ([i+2]) + history[i + 3] *= scale; // Size, TODO: square this? + } + } + } + + /** + * Translate the coordination of the event by given x and y. + * + * @hide + */ + public void translate(float dx, float dy) { + mX += dx; + mY += dy; + mRawX += dx; + mRawY += dx; + if (mHistory != null) { + float[] history = mHistory; + int length = history.length; + for (int i = 0; i < length; i += 4) { + history[i] += dx; // X + history[i + 1] += dy; // Y + // no need to translate pressure (i+2) and size (i+3) } } } diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 082cca2..45b0f0a 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -18,6 +18,7 @@ package android.view; import android.content.Context; import android.content.res.CompatibilityInfo; +import android.content.res.CompatibilityInfo.Translator; import android.graphics.Canvas; import android.graphics.PixelFormat; import android.graphics.PorterDuff; @@ -138,24 +139,21 @@ public class SurfaceView extends View { int mFormat = -1; int mType = -1; final Rect mSurfaceFrame = new Rect(); - private final CompatibilityInfo mCompatibilityInfo; + private Translator mTranslator; public SurfaceView(Context context) { super(context); setWillNotDraw(true); - mCompatibilityInfo = context.getResources().getCompatibilityInfo(); } public SurfaceView(Context context, AttributeSet attrs) { super(context, attrs); setWillNotDraw(true); - mCompatibilityInfo = context.getResources().getCompatibilityInfo(); } public SurfaceView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setWillNotDraw(true); - mCompatibilityInfo = context.getResources().getCompatibilityInfo(); } /** @@ -258,9 +256,9 @@ public class SurfaceView extends View { public boolean dispatchTouchEvent(MotionEvent event) { // SurfaceView uses pre-scaled size unless fixed size is requested. This hook // scales the event back to the pre-scaled coordinates for such surface. - if (mRequestedWidth < 0 && mCompatibilityInfo.mScalingRequired) { + if (mRequestedWidth < 0 && mTranslator != null) { MotionEvent scaledBack = MotionEvent.obtain(event); - scaledBack.scale(mCompatibilityInfo.mApplicationScale); + scaledBack.scale(mTranslator.applicationScale); try { return super.dispatchTouchEvent(scaledBack); } finally { @@ -297,15 +295,18 @@ public class SurfaceView extends View { if (!mHaveFrame) { return; } - float appScale = mCompatibilityInfo.mApplicationScale; + mTranslator = ((ViewRoot)getRootView().getParent()).mTranslator; + + float appScale = mTranslator == null ? 1.0f : mTranslator.applicationScale; int myWidth = mRequestedWidth; if (myWidth <= 0) myWidth = getWidth(); int myHeight = mRequestedHeight; if (myHeight <= 0) myHeight = getHeight(); - // Use original size for surface unless fixed size is requested. - if (mRequestedWidth <= 0 && mCompatibilityInfo.mScalingRequired) { + // Use original size if the app specified the size of the view, + // and let the flinger to scale up. + if (mRequestedWidth <= 0 && mTranslator != null && mTranslator.scalingRequired) { myWidth *= appScale; myHeight *= appScale; } @@ -325,7 +326,7 @@ public class SurfaceView extends View { + " visible=" + visibleChanged + " left=" + (mLeft != mLocation[0]) + " top=" + (mTop != mLocation[1])); - + try { final boolean visible = mVisible = mRequestedVisible; mLeft = mLocation[0]; @@ -335,16 +336,23 @@ public class SurfaceView extends View { mFormat = mRequestedFormat; mType = mRequestedType; - // Scaling window's layout here because mLayout is not used elsewhere. - mLayout.x = (int) (mLeft * appScale); - mLayout.y = (int) (mTop * appScale); - mLayout.width = (int) (getWidth() * appScale); - mLayout.height = (int) (getHeight() * appScale); + // Scaling/Translate window's layout here because mLayout is not used elsewhere. + + // Places the window relative + mLayout.x = mLeft; + mLayout.y = mTop; + mLayout.width = getWidth(); + mLayout.height = getHeight(); + if (mTranslator != null) { + mTranslator.translateLayoutParamsInAppWindowToScreen(mLayout); + } + mLayout.format = mRequestedFormat; mLayout.flags |=WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS | WindowManager.LayoutParams.FLAG_SCALED | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE + | WindowManager.LayoutParams.FLAG_NO_COMPATIBILITY_SCALING ; mLayout.memoryType = mRequestedType; @@ -371,13 +379,6 @@ public class SurfaceView extends View { visible ? VISIBLE : GONE, false, mWinFrame, mContentInsets, mVisibleInsets, mSurface); - if (mCompatibilityInfo.mScalingRequired) { - float invertedScale = mCompatibilityInfo.mApplicationInvertedScale; - mContentInsets.scale(invertedScale); - mVisibleInsets.scale(invertedScale); - mWinFrame.scale(invertedScale); - } - if (localLOGV) Log.i(TAG, "New surface: " + mSurface + ", vis=" + visible + ", frame=" + mWinFrame); mSurfaceFrame.left = 0; @@ -446,24 +447,14 @@ public class SurfaceView extends View { private static class MyWindow extends IWindow.Stub { private final WeakReference<SurfaceView> mSurfaceView; - private final CompatibilityInfo mCompatibilityInfo; public MyWindow(SurfaceView surfaceView) { mSurfaceView = new WeakReference<SurfaceView>(surfaceView); - mCompatibilityInfo = surfaceView.getContext().getResources().getCompatibilityInfo(); } public void resized(int w, int h, Rect coveredInsets, Rect visibleInsets, boolean reportDraw) { SurfaceView surfaceView = mSurfaceView.get(); - if (mCompatibilityInfo.mScalingRequired) { - float scale = mCompatibilityInfo.mApplicationInvertedScale; - w *= scale; - h *= scale; - coveredInsets.scale(scale); - visibleInsets.scale(scale); - } - if (surfaceView != null) { if (localLOGV) Log.v( "SurfaceView", surfaceView + " got resized: w=" + @@ -626,9 +617,6 @@ public class SurfaceView extends View { Canvas c = null; if (!mDrawingStopped && mWindow != null) { Rect frame = dirty != null ? dirty : mSurfaceFrame; - if (mCompatibilityInfo.mScalingRequired) { - frame.scale(mCompatibilityInfo.mApplicationScale); - } try { c = mSurface.lockCanvas(frame); } catch (Exception e) { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index ec8fd96..3bfdde8 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -1934,11 +1934,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility viewFlagValues &= ~SOUND_EFFECTS_ENABLED; viewFlagMasks |= SOUND_EFFECTS_ENABLED; } + break; case com.android.internal.R.styleable.View_hapticFeedbackEnabled: if (!a.getBoolean(attr, true)) { viewFlagValues &= ~HAPTIC_FEEDBACK_ENABLED; viewFlagMasks |= HAPTIC_FEEDBACK_ENABLED; } + break; case R.styleable.View_scrollbars: final int scrollbars = a.getInt(attr, SCROLLBARS_NONE); if (scrollbars != SCROLLBARS_NONE) { @@ -6541,7 +6543,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility boolean changed = false; if (DBG) { - System.out.println(this + " View.setFrame(" + left + "," + top + "," + Log.d("View", this + " View.setFrame(" + left + "," + top + "," + right + "," + bottom + ")"); } diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index d35b048..65457c5 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -128,13 +128,12 @@ public final class ViewRoot extends Handler implements ViewParent, Rect mDirty; // will be a graphics.Region soon boolean mIsAnimating; - private CompatibilityInfo mCompatibilityInfo; + CompatibilityInfo.Translator mTranslator; final View.AttachInfo mAttachInfo; final Rect mTempRect; // used in the transaction to not thrash the heap. final Rect mVisRect; // used to retrieve visible rect of focused view. - final Point mVisPoint; // used to retrieve global offset of focused view. boolean mTraversalScheduled; boolean mWillDrawSoon; @@ -218,7 +217,6 @@ public final class ViewRoot extends Handler implements ViewParent, mDirty = new Rect(); mTempRect = new Rect(); mVisRect = new Rect(); - mVisPoint = new Point(); mWinFrame = new Rect(); mWindow = new W(this, context); mInputMethodCallback = new InputMethodCallback(this); @@ -387,20 +385,25 @@ public final class ViewRoot extends Handler implements ViewParent, if (mView == null) { mView = view; mWindowAttributes.copyFrom(attrs); - mCompatibilityInfo = mView.getContext().getResources().getCompatibilityInfo(); + + CompatibilityInfo compatibilityInfo = + mView.getContext().getResources().getCompatibilityInfo(); + mTranslator = compatibilityInfo.getTranslator(attrs); boolean restore = false; - if (mCompatibilityInfo.mScalingRequired || !mCompatibilityInfo.mExpandable) { + if (attrs != null && mTranslator != null) { restore = true; - mWindowAttributes.backup(); - } - if (!mCompatibilityInfo.mExpandable) { - adjustWindowAttributesForCompatibleMode(mWindowAttributes); + attrs.backup(); + mTranslator.translateWindowLayout(attrs); } + if (DEBUG_LAYOUT) Log.d(TAG, "WindowLayout in setView:" + attrs); + mSoftInputMode = attrs.softInputMode; mWindowAttributesChanged = true; mAttachInfo.mRootView = view; - mAttachInfo.mScalingRequired = mCompatibilityInfo.mScalingRequired; - mAttachInfo.mApplicationScale = mCompatibilityInfo.mApplicationScale; + mAttachInfo.mScalingRequired = + mTranslator == null ? false : mTranslator.scalingRequired; + mAttachInfo.mApplicationScale = + mTranslator == null ? 1.0f : mTranslator.applicationScale; if (panelParentView != null) { mAttachInfo.mPanelParentWindowToken = panelParentView.getApplicationWindowToken(); @@ -421,15 +424,14 @@ public final class ViewRoot extends Handler implements ViewParent, mAttachInfo.mRootView = null; unscheduleTraversals(); throw new RuntimeException("Adding window failed", e); + } finally { + if (restore) { + attrs.restore(); + } } - if (restore) { - mWindowAttributes.restore(); - } - - if (mCompatibilityInfo.mScalingRequired) { - mAttachInfo.mContentInsets.scale( - mCompatibilityInfo.mApplicationInvertedScale); + if (mTranslator != null) { + mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets); } mPendingContentInsets.set(mAttachInfo.mContentInsets); mPendingVisibleInsets.set(0, 0, 0, 0); @@ -541,14 +543,14 @@ public final class ViewRoot extends Handler implements ViewParent, public void invalidateChild(View child, Rect dirty) { checkThread(); - if (LOCAL_LOGV) Log.v(TAG, "Invalidate child: " + dirty); - if (mCurScrollY != 0 || mCompatibilityInfo.mScalingRequired) { + if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty); + if (mCurScrollY != 0 || mTranslator != null) { mTempRect.set(dirty); if (mCurScrollY != 0) { mTempRect.offset(0, -mCurScrollY); } - if (mCompatibilityInfo.mScalingRequired) { - mTempRect.scale(mCompatibilityInfo.mApplicationScale); + if (mTranslator != null) { + mTranslator.translateRectInAppWindowToScreen(mTempRect); } dirty = mTempRect; } @@ -567,7 +569,7 @@ public final class ViewRoot extends Handler implements ViewParent, return null; } - public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) { + public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) { if (child != mView) { throw new RuntimeException("child is not mine, honest!"); } @@ -628,14 +630,14 @@ public final class ViewRoot extends Handler implements ViewParent, boolean viewVisibilityChanged = mViewVisibility != viewVisibility || mNewSurfaceNeeded; - float appScale = mCompatibilityInfo.mApplicationScale; + float appScale = mAttachInfo.mApplicationScale; WindowManager.LayoutParams params = null; if (mWindowAttributesChanged) { mWindowAttributesChanged = false; params = lp; } - + Rect frame = mWinFrame; if (mFirst) { fullRedrawNeeded = true; mLayoutRequested = true; @@ -660,11 +662,11 @@ public final class ViewRoot extends Handler implements ViewParent, //Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn); } else { - desiredWindowWidth = mWinFrame.width(); - desiredWindowHeight = mWinFrame.height(); + desiredWindowWidth = frame.width(); + desiredWindowHeight = frame.height(); if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) { if (DEBUG_ORIENTATION) Log.v("ViewRoot", - "View " + host + " resized to: " + mWinFrame); + "View " + host + " resized to: " + frame); fullRedrawNeeded = true; mLayoutRequested = true; windowResizesToFitContent = true; @@ -810,7 +812,6 @@ public final class ViewRoot extends Handler implements ViewParent, } } - final Rect frame = mWinFrame; boolean initialized = false; boolean contentInsetsChanged = false; boolean visibleInsetsChanged; @@ -883,7 +884,7 @@ public final class ViewRoot extends Handler implements ViewParent, } catch (RemoteException e) { } if (DEBUG_ORIENTATION) Log.v( - "ViewRoot", "Relayout returned: frame=" + mWinFrame + ", surface=" + mSurface); + "ViewRoot", "Relayout returned: frame=" + frame + ", surface=" + mSurface); attachInfo.mWindowLeft = frame.left; attachInfo.mWindowTop = frame.top; @@ -958,7 +959,6 @@ public final class ViewRoot extends Handler implements ViewParent, if (Config.DEBUG && ViewDebug.profileLayout) { startTime = SystemClock.elapsedRealtime(); } - host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight); if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) { @@ -985,7 +985,10 @@ public final class ViewRoot extends Handler implements ViewParent, mTmpLocation[1] + host.mBottom - host.mTop); host.gatherTransparentRegion(mTransparentRegion); - mTransparentRegion.scale(appScale); + if (mTranslator != null) { + mTranslator.translateRegionInWindowToScreen(mTransparentRegion); + } + if (!mTransparentRegion.equals(mPreviousTransparentRegion)) { mPreviousTransparentRegion.set(mTransparentRegion); // reconfigure window manager @@ -1016,15 +1019,17 @@ public final class ViewRoot extends Handler implements ViewParent, = givenContent.bottom = givenVisible.left = givenVisible.top = givenVisible.right = givenVisible.bottom = 0; attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets); - if (mCompatibilityInfo.mScalingRequired) { - insets.contentInsets.scale(appScale); - insets.visibleInsets.scale(appScale); + Rect contentInsets = insets.contentInsets; + Rect visibleInsets = insets.visibleInsets; + if (mTranslator != null) { + contentInsets = mTranslator.getTranslatedContentInsets(contentInsets); + visibleInsets = mTranslator.getTranslatedVisbileInsets(visibleInsets); } if (insetsPending || !mLastGivenInsets.equals(insets)) { mLastGivenInsets.set(insets); try { sWindowSession.setInsets(mWindow, insets.mTouchableInsets, - insets.contentInsets, insets.visibleInsets); + contentInsets, visibleInsets); } catch (RemoteException e) { } } @@ -1167,8 +1172,8 @@ public final class ViewRoot extends Handler implements ViewParent, mCurScrollY = yoff; fullRedrawNeeded = true; } - float appScale = mCompatibilityInfo.mApplicationScale; - boolean scalingRequired = mCompatibilityInfo.mScalingRequired; + float appScale = mAttachInfo.mApplicationScale; + boolean scalingRequired = mAttachInfo.mScalingRequired; Rect dirty = mDirty; if (mUseGL) { @@ -1187,8 +1192,8 @@ public final class ViewRoot extends Handler implements ViewParent, int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); try { canvas.translate(0, -yoff); - if (scalingRequired) { - canvas.scale(appScale, appScale); + if (mTranslator != null) { + mTranslator.translateCanvas(canvas); } mView.draw(canvas); if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) { @@ -1239,7 +1244,6 @@ public final class ViewRoot extends Handler implements ViewParent, int top = dirty.top; int right = dirty.right; int bottom = dirty.bottom; - canvas = surface.lockCanvas(dirty); if (left != dirty.left || top != dirty.top || right != dirty.right || @@ -1295,8 +1299,8 @@ public final class ViewRoot extends Handler implements ViewParent, int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG); try { canvas.translate(0, -yoff); - if (scalingRequired) { - canvas.scale(appScale, appScale); + if (mTranslator != null) { + mTranslator.translateCanvas(canvas); } mView.draw(canvas); } finally { @@ -1599,10 +1603,9 @@ public final class ViewRoot extends Handler implements ViewParent, } else { didFinish = event.getAction() == MotionEvent.ACTION_OUTSIDE; } - if (event != null && mCompatibilityInfo.mScalingRequired) { - event.scale(mCompatibilityInfo.mApplicationInvertedScale); + if (event != null && mTranslator != null) { + mTranslator.translateEventInScreenToAppWindow(event); } - try { boolean handled; if (mView != null && mAdded && event != null) { @@ -1688,6 +1691,7 @@ public final class ViewRoot extends Handler implements ViewParent, case RESIZED: Rect coveredInsets = ((Rect[])msg.obj)[0]; Rect visibleInsets = ((Rect[])msg.obj)[1]; + if (mWinFrame.width() == msg.arg1 && mWinFrame.height() == msg.arg2 && mPendingContentInsets.equals(coveredInsets) && mPendingVisibleInsets.equals(visibleInsets)) { @@ -1722,7 +1726,7 @@ public final class ViewRoot extends Handler implements ViewParent, if (mGlWanted && !mUseGL) { initializeGL(); if (mGlCanvas != null) { - float appScale = mCompatibilityInfo.mApplicationScale; + float appScale = mAttachInfo.mApplicationScale; mGlCanvas.setViewport( (int) (mWidth * appScale), (int) (mHeight * appScale)); } @@ -2356,18 +2360,16 @@ public final class ViewRoot extends Handler implements ViewParent, private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, boolean insetsPending) throws RemoteException { + + float appScale = mAttachInfo.mApplicationScale; boolean restore = false; - float appScale = mCompatibilityInfo.mApplicationScale; - boolean scalingRequired = mCompatibilityInfo.mScalingRequired; - if (params != null && !mCompatibilityInfo.mExpandable) { + if (params != null && mTranslator != null) { restore = true; params.backup(); - adjustWindowAttributesForCompatibleMode(params); + mTranslator.translateWindowLayout(params); } - if (params != null && scalingRequired) { - if (!restore) params.backup(); - restore = true; - params.scale(appScale); + if (params != null) { + if (DBG) Log.d(TAG, "WindowLayout in layoutWindow:" + params); } int relayoutResult = sWindowSession.relayout( mWindow, params, @@ -2378,44 +2380,16 @@ public final class ViewRoot extends Handler implements ViewParent, if (restore) { params.restore(); } - if (scalingRequired) { - float invertedScale = mCompatibilityInfo.mApplicationInvertedScale; - mPendingContentInsets.scale(invertedScale); - mPendingVisibleInsets.scale(invertedScale); - mWinFrame.scale(invertedScale); + + if (mTranslator != null) { + mTranslator.translateRectInScreenToAppWinFrame(mWinFrame); + mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets); + mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets); } return relayoutResult; } /** - * Adjust the window's layout parameter for compatibility mode. It replaces FILL_PARENT - * with the default window size, and centers if the window wanted to fill - * horizontally. - * - * @param attrs the window's layout params to adjust - */ - private void adjustWindowAttributesForCompatibleMode(WindowManager.LayoutParams attrs) { - // fix app windows only - if (attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { - DisplayMetrics metrics = mView.getContext().getResources().getDisplayMetrics(); - // TODO: improve gravity logic - if (attrs.width == ViewGroup.LayoutParams.FILL_PARENT) { - attrs.width = metrics.widthPixels; - attrs.gravity |= Gravity.CENTER_HORIZONTAL; - mWindowAttributesChanged = attrs == mWindowAttributes; - } - if (attrs.height == ViewGroup.LayoutParams.FILL_PARENT) { - attrs.height = metrics.heightPixels; - attrs.gravity |= Gravity.TOP; - mWindowAttributesChanged = attrs == mWindowAttributes; - } - if (DEBUG_LAYOUT) { - Log.d(TAG, "Adjusted Attributes for compatibility : " + attrs); - } - } - } - - /** * {@inheritDoc} */ public void playSoundEffect(int effectId) { @@ -2518,16 +2492,14 @@ public final class ViewRoot extends Handler implements ViewParent, + " visibleInsets=" + visibleInsets.toShortString() + " reportDraw=" + reportDraw); Message msg = obtainMessage(reportDraw ? RESIZED_REPORT :RESIZED); - if (mCompatibilityInfo.mScalingRequired) { - float invertedScale = mCompatibilityInfo.mApplicationInvertedScale; - coveredInsets.scale(invertedScale); - visibleInsets.scale(invertedScale); - msg.arg1 = (int) (w * invertedScale); - msg.arg2 = (int) (h * invertedScale); - } else { - msg.arg1 = w; - msg.arg2 = h; - } + if (mTranslator != null) { + mTranslator.translateRectInScreenToAppWindow(coveredInsets); + mTranslator.translateRectInScreenToAppWindow(visibleInsets); + w *= mTranslator.applicationInvertedScale; + h *= mTranslator.applicationInvertedScale; + } + msg.arg1 = w; + msg.arg2 = h; msg.obj = new Rect[] { new Rect(coveredInsets), new Rect(visibleInsets) }; sendMessage(msg); } diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index e295d15..bdb86d7 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -483,6 +483,12 @@ public interface WindowManager extends ViewManager { * {@hide} */ public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000; + /** Window flag: special flag to let a window ignore the compatibility scaling. + * This is used by SurfaceView to create a window that does not scale the content. + * + * {@hide} */ + public static final int FLAG_NO_COMPATIBILITY_SCALING = 0x00100000; + /** Window flag: a special option intended for system dialogs. When * this flag is set, the window will demand focus unconditionally when * it is created. @@ -978,8 +984,9 @@ public interface WindowManager extends ViewManager { /** * Scale the layout params' coordinates and size. + * @hide */ - void scale(float scale) { + public void scale(float scale) { x *= scale; y *= scale; if (width > 0) { @@ -997,14 +1004,13 @@ public interface WindowManager extends ViewManager { void backup() { int[] backup = mCompatibilityParamsBackup; if (backup == null) { - // we backup 5 elements, x, y, width, height and gravity. - backup = mCompatibilityParamsBackup = new int[5]; + // we backup 4 elements, x, y, width, height + backup = mCompatibilityParamsBackup = new int[4]; } backup[0] = x; backup[1] = y; backup[2] = width; backup[3] = height; - backup[4] = gravity; } /** @@ -1018,7 +1024,6 @@ public interface WindowManager extends ViewManager { y = backup[1]; width = backup[2]; height = backup[3]; - gravity = backup[4]; } } diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index dcba943..ec671d5 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -69,7 +69,24 @@ public class WebSettings { } int value; } - + + /** + * Enum for specifying the WebView's desired density. + * FAR makes 100% looking like in 240dpi + * MEDIUM makes 100% looking like in 160dpi + * CLOSE makes 100% looking like in 120dpi + * @hide Pending API council approval + */ + public enum ZoomDensity { + FAR(150), // 240dpi + MEDIUM(100), // 160dpi + CLOSE(75); // 120dpi + ZoomDensity(int size) { + value = size; + } + int value; + } + /** * Default cache usage pattern Use with {@link #setCacheMode}. */ @@ -105,6 +122,8 @@ public class WebSettings { LOW } + // WebView associated with this WebSettings. + private WebView mWebView; // BrowserFrame used to access the native frame pointer. private BrowserFrame mBrowserFrame; // Flag to prevent multiple SYNC messages at one time. @@ -145,6 +164,7 @@ public class WebSettings { // Don't need to synchronize the get/set methods as they // are basic types, also none of these values are used in // native WebCore code. + private ZoomDensity mDefaultZoom = ZoomDensity.MEDIUM; private RenderPriority mRenderPriority = RenderPriority.NORMAL; private int mOverrideCacheMode = LOAD_DEFAULT; private boolean mSaveFormData = true; @@ -237,9 +257,10 @@ public class WebSettings { * Package constructor to prevent clients from creating a new settings * instance. */ - WebSettings(Context context) { + WebSettings(Context context, WebView webview) { mEventHandler = new EventHandler(); mContext = context; + mWebView = webview; mDefaultTextEncoding = context.getString(com.android.internal. R.string.default_text_encoding); @@ -447,6 +468,31 @@ public class WebSettings { } /** + * Set the default zoom density of the page. This should be called from UI + * thread. + * @param zoom A ZoomDensity value + * @see WebSettings.ZoomDensity + * @hide Pending API council approval + */ + public void setDefaultZoom(ZoomDensity zoom) { + if (mDefaultZoom != zoom) { + mDefaultZoom = zoom; + mWebView.updateDefaultZoomDensity(zoom.value); + } + } + + /** + * Get the default zoom density of the page. This should be called from UI + * thread. + * @return A ZoomDensity value + * @see WebSettings.ZoomDensity + * @hide Pending API council approval + */ + public ZoomDensity getDefaultZoom() { + return mDefaultZoom; + } + + /** * Enables using light touches to make a selection and activate mouseovers. */ public void setLightTouchEnabled(boolean enabled) { diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 1179c1c..429f0f9 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -408,7 +408,7 @@ public class WebView extends AbsoluteLayout // default scale. Depending on the display density. static int DEFAULT_SCALE_PERCENT; - private float DEFAULT_SCALE; + private float mDefaultScale; // set to true temporarily while the zoom control is being dragged private boolean mPreviewZoomOnly = false; @@ -640,7 +640,7 @@ public class WebView extends AbsoluteLayout mZoomFitPageButton.setOnClickListener( new View.OnClickListener() { public void onClick(View v) { - zoomWithPreview(DEFAULT_SCALE); + zoomWithPreview(mDefaultScale); updateZoomButtonsEnabled(); } }); @@ -663,7 +663,7 @@ public class WebView extends AbsoluteLayout // or out. mZoomButtonsController.setZoomInEnabled(canZoomIn); mZoomButtonsController.setZoomOutEnabled(canZoomOut); - mZoomFitPageButton.setEnabled(mActualScale != DEFAULT_SCALE); + mZoomFitPageButton.setEnabled(mActualScale != mDefaultScale); } mZoomOverviewButton.setVisibility(canZoomScrollOut() ? View.VISIBLE: View.GONE); @@ -685,7 +685,7 @@ public class WebView extends AbsoluteLayout mNavSlop = (int) (16 * density); // density adjusted scale factors DEFAULT_SCALE_PERCENT = (int) (100 * density); - DEFAULT_SCALE = density; + mDefaultScale = density; mActualScale = density; mInvActualScale = 1 / density; DEFAULT_MAX_ZOOM_SCALE = 4.0f * density; @@ -694,6 +694,23 @@ public class WebView extends AbsoluteLayout mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE; } + /* package */void updateDefaultZoomDensity(int zoomDensity) { + final float density = getContext().getResources().getDisplayMetrics().density + * 100 / zoomDensity; + if (Math.abs(density - mDefaultScale) > 0.01) { + float scaleFactor = density / mDefaultScale; + // adjust the limits + mNavSlop = (int) (16 * density); + DEFAULT_SCALE_PERCENT = (int) (100 * density); + DEFAULT_MAX_ZOOM_SCALE = 4.0f * density; + DEFAULT_MIN_ZOOM_SCALE = 0.25f * density; + mDefaultScale = density; + mMaxZoomScale *= scaleFactor; + mMinZoomScale *= scaleFactor; + setNewZoomScale(mActualScale * scaleFactor, false); + } + } + /* package */ boolean onSavePassword(String schemePlusHost, String username, String password, final Message resumeMsg) { boolean rVal = false; @@ -4172,8 +4189,8 @@ public class WebView extends AbsoluteLayout float oldScale = mActualScale; // snap to DEFAULT_SCALE if it is close - if (scale > (DEFAULT_SCALE - 0.05) && scale < (DEFAULT_SCALE + 0.05)) { - scale = DEFAULT_SCALE; + if (scale > (mDefaultScale - 0.05) && scale < (mDefaultScale + 0.05)) { + scale = mDefaultScale; } setNewZoomScale(scale, false); @@ -4554,9 +4571,11 @@ public class WebView extends AbsoluteLayout break; } case SWITCH_TO_LONGPRESS: { - mTouchMode = TOUCH_DONE_MODE; - performLongClick(); - updateTextEntry(); + if (!mPreventDrag) { + mTouchMode = TOUCH_DONE_MODE; + performLongClick(); + updateTextEntry(); + } break; } case SWITCH_TO_ENTER: @@ -4689,7 +4708,7 @@ public class WebView extends AbsoluteLayout int initialScale = msg.arg1; int viewportWidth = msg.arg2; // start a new page with DEFAULT_SCALE zoom scale. - float scale = DEFAULT_SCALE; + float scale = mDefaultScale; if (mInitialScale > 0) { scale = mInitialScale / 100.0f; } else { diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index e2efb43..a5fa41e 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -139,7 +139,7 @@ final class WebViewCore { // ready. mEventHub = new EventHub(); // Create a WebSettings object for maintaining all settings - mSettings = new WebSettings(mContext); + mSettings = new WebSettings(mContext, mWebView); // The WebIconDatabase needs to be initialized within the UI thread so // just request the instance here. WebIconDatabase.getInstance(); @@ -1560,6 +1560,20 @@ final class WebViewCore { // set the viewport settings from WebKit setViewportSettingsFromNative(); + // adjust the default scale to match the density + if (WebView.DEFAULT_SCALE_PERCENT != 100) { + float adjust = (float) WebView.DEFAULT_SCALE_PERCENT / 100.0f; + if (mViewportInitialScale > 0) { + mViewportInitialScale *= adjust; + } + if (mViewportMinimumScale > 0) { + mViewportMinimumScale *= adjust; + } + if (mViewportMaximumScale > 0) { + mViewportMaximumScale *= adjust; + } + } + // infer the values if they are not defined. if (mViewportWidth == 0) { if (mViewportInitialScale == 0) { @@ -1586,7 +1600,7 @@ final class WebViewCore { mViewportMaximumScale = mViewportInitialScale; } else if (mViewportInitialScale == 0) { mViewportInitialScale = mViewportMaximumScale; - } + } } if (mViewportWidth < 0 && mViewportInitialScale == WebView.DEFAULT_SCALE_PERCENT) { diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java index 5dd3ec4..70749d1 100644 --- a/core/java/android/widget/AutoCompleteTextView.java +++ b/core/java/android/widget/AutoCompleteTextView.java @@ -80,6 +80,7 @@ import com.android.internal.R; * @attr ref android.R.styleable#AutoCompleteTextView_dropDownSelector * @attr ref android.R.styleable#AutoCompleteTextView_dropDownAnchor * @attr ref android.R.styleable#AutoCompleteTextView_dropDownWidth + * @attr ref android.R.styleable#AutoCompleteTextView_dropDownHeight */ public class AutoCompleteTextView extends EditText implements Filter.FilterListener { static final boolean DEBUG = false; @@ -101,6 +102,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe private int mDropDownAnchorId; private View mDropDownAnchorView; // view is retrieved lazily from id once needed private int mDropDownWidth; + private int mDropDownHeight; private Drawable mDropDownListHighlight; @@ -166,6 +168,8 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe // (for full screen width) or WRAP_CONTENT (to match the width of the anchored view). mDropDownWidth = a.getLayoutDimension(R.styleable.AutoCompleteTextView_dropDownWidth, ViewGroup.LayoutParams.WRAP_CONTENT); + mDropDownHeight = a.getLayoutDimension(R.styleable.AutoCompleteTextView_dropDownHeight, + ViewGroup.LayoutParams.WRAP_CONTENT); mHintResource = a.getResourceId(R.styleable.AutoCompleteTextView_completionHintView, R.layout.simple_dropdown_hint); @@ -254,6 +258,34 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe public void setDropDownWidth(int width) { mDropDownWidth = width; } + + /** + * <p>Returns the current height for the auto-complete drop down list. This can + * be a fixed height, or {@link ViewGroup.LayoutParams#FILL_PARENT} to fill + * the screen, or {@link ViewGroup.LayoutParams#WRAP_CONTENT} to fit the height + * of the drop down's content.</p> + * + * @return the height for the drop down list + * + * @attr ref android.R.styleable#AutoCompleteTextView_dropDownHeight + */ + public int getDropDownHeight() { + return mDropDownHeight; + } + + /** + * <p>Sets the current height for the auto-complete drop down list. This can + * be a fixed height, or {@link ViewGroup.LayoutParams#FILL_PARENT} to fill + * the screen, or {@link ViewGroup.LayoutParams#WRAP_CONTENT} to fit the height + * of the drop down's content.</p> + * + * @param height the height to use + * + * @attr ref android.R.styleable#AutoCompleteTextView_dropDownHeight + */ + public void setDropDownHeight(int height) { + mDropDownHeight = height; + } /** * <p>Returns the id for the view that the auto-complete drop down list is anchored to.</p> @@ -635,7 +667,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe // event to prevent focus from moving. clearListSelection(); mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); - mPopup.update(); + showDropDown(); return true; } else { // WARNING: Please read the comment where mListSelectionHidden @@ -655,7 +687,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe // by ensuring it has focus and getting its window out // of touch mode. mDropDownList.requestFocusFromTouch(); - mPopup.update(); + showDropDown(); switch (keyCode) { // avoid passing the focus from the text view to the @@ -1052,8 +1084,13 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe */ public void showDropDown() { int height = buildDropDown(); + + int widthSpec = 0; + int heightSpec = 0; + + boolean noInputMethod = mPopup.getInputMethodMode() == PopupWindow.INPUT_METHOD_NOT_NEEDED; + if (mPopup.isShowing()) { - int widthSpec; if (mDropDownWidth == ViewGroup.LayoutParams.FILL_PARENT) { // The call to PopupWindow's update method below can accept -1 for any // value you do not want to update. @@ -1063,20 +1100,51 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe } else { widthSpec = mDropDownWidth; } + + if (mDropDownHeight == ViewGroup.LayoutParams.FILL_PARENT) { + // The call to PopupWindow's update method below can accept -1 for any + // value you do not want to update. + heightSpec = noInputMethod ? height : ViewGroup.LayoutParams.FILL_PARENT; + if (noInputMethod) { + mPopup.setWindowLayoutMode( + mDropDownWidth == ViewGroup.LayoutParams.FILL_PARENT ? + ViewGroup.LayoutParams.FILL_PARENT : 0, 0); + } else { + mPopup.setWindowLayoutMode( + mDropDownWidth == ViewGroup.LayoutParams.FILL_PARENT ? + ViewGroup.LayoutParams.FILL_PARENT : 0, + ViewGroup.LayoutParams.FILL_PARENT); + } + } else if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) { + heightSpec = height; + } else { + heightSpec = mDropDownHeight; + } + mPopup.update(getDropDownAnchorView(), mDropDownHorizontalOffset, - mDropDownVerticalOffset, widthSpec, height); + mDropDownVerticalOffset, widthSpec, heightSpec); } else { if (mDropDownWidth == ViewGroup.LayoutParams.FILL_PARENT) { - mPopup.setWindowLayoutMode(ViewGroup.LayoutParams.FILL_PARENT, 0); + widthSpec = ViewGroup.LayoutParams.FILL_PARENT; } else { - mPopup.setWindowLayoutMode(0, 0); if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) { mPopup.setWidth(getDropDownAnchorView().getWidth()); } else { mPopup.setWidth(mDropDownWidth); } } - mPopup.setHeight(height); + + if (mDropDownHeight == ViewGroup.LayoutParams.FILL_PARENT) { + heightSpec = ViewGroup.LayoutParams.FILL_PARENT; + } else { + if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) { + mPopup.setHeight(height); + } else { + mPopup.setHeight(mDropDownHeight); + } + } + + mPopup.setWindowLayoutMode(widthSpec, heightSpec); mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); // use outside touchable to dismiss drop down when touching outside of it, so @@ -1195,10 +1263,12 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe final int maxHeight = mPopup.getMaxAvailableHeight( getDropDownAnchorView(), mDropDownVerticalOffset, ignoreBottomDecorations); - final int measuredHeight = mDropDownList.measureHeightOfChildren(MeasureSpec.UNSPECIFIED, - 0, ListView.NO_POSITION, maxHeight - otherHeights, 2) + otherHeights; + if (mDropDownAlwaysVisible) { + return maxHeight; + } - return mDropDownAlwaysVisible ? maxHeight : measuredHeight; + return mDropDownList.measureHeightOfChildren(MeasureSpec.UNSPECIFIED, + 0, ListView.NO_POSITION, maxHeight - otherHeights, 2) + otherHeights; } private View getHintView(Context context) { diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index 6532125..f8a6f89 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -3264,9 +3264,13 @@ public class ListView extends AbsListView { if (mChoiceMode == CHOICE_MODE_MULTIPLE) { mCheckStates.put(position, value); } else { - boolean oldValue = mCheckStates.get(position, false); + // Clear the old value: if something was selected and value == false + // then it is unselected mCheckStates.clear(); - if (!oldValue) { + // If value == true, select the appropriate position + // this may end up selecting the value we just cleared but this way + // we don't have to first to a get(position) + if (value) { mCheckStates.put(position, true); } } diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index b188c31..0c2cd55 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -49,7 +49,7 @@ import java.lang.ref.WeakReference; */ public class PopupWindow { /** - * Mode for {@link #setInputMethodMode(int): the requirements for the + * Mode for {@link #setInputMethodMode(int)}: the requirements for the * input method should be based on the focusability of the popup. That is * if it is focusable than it needs to work with the input method, else * it doesn't. @@ -57,16 +57,15 @@ public class PopupWindow { public static final int INPUT_METHOD_FROM_FOCUSABLE = 0; /** - * Mode for {@link #setInputMethodMode(int): this popup always needs to + * Mode for {@link #setInputMethodMode(int)}: this popup always needs to * work with an input method, regardless of whether it is focusable. This * means that it will always be displayed so that the user can also operate * the input method while it is shown. */ - public static final int INPUT_METHOD_NEEDED = 1; /** - * Mode for {@link #setInputMethodMode(int): this popup never needs to + * Mode for {@link #setInputMethodMode(int)}: this popup never needs to * work with an input method, regardless of whether it is focusable. This * means that it will always be displayed to use as much space on the * screen as needed, regardless of whether this covers the input method. @@ -991,7 +990,7 @@ public class PopupWindow { int bottomEdge = displayFrame.bottom; if (ignoreBottomDecorations) { - bottomEdge = WindowManagerImpl.getDefault().getDefaultDisplay().getHeight(); + bottomEdge = anchor.getContext().getResources().getDisplayMetrics().heightPixels; } final int distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset; final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset; @@ -1131,8 +1130,7 @@ public class PopupWindow { return; } - WindowManager.LayoutParams p = (WindowManager.LayoutParams) - mPopupView.getLayoutParams(); + WindowManager.LayoutParams p = (WindowManager.LayoutParams) mPopupView.getLayoutParams(); boolean update = force; @@ -1219,8 +1217,7 @@ public class PopupWindow { registerForScrollChanged(anchor, xoff, yoff); } - WindowManager.LayoutParams p = (WindowManager.LayoutParams) - mPopupView.getLayoutParams(); + WindowManager.LayoutParams p = (WindowManager.LayoutParams) mPopupView.getLayoutParams(); if (updateDimension) { if (width == -1) { diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index 84cf2c8..12bb01c 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -20,8 +20,15 @@ import com.android.internal.R; import android.content.Context; import android.content.res.TypedArray; +import android.content.res.Resources; import android.graphics.Rect; import android.util.AttributeSet; +import android.util.SparseArray; +import android.util.Poolable; +import android.util.Pool; +import android.util.Pools; +import android.util.PoolableManager; +import static android.util.Log.d; import android.view.Gravity; import android.view.View; import android.view.ViewDebug; @@ -32,6 +39,9 @@ import android.widget.RemoteViews.RemoteView; import java.util.Comparator; import java.util.SortedSet; import java.util.TreeSet; +import java.util.LinkedList; +import java.util.HashSet; +import java.util.ArrayList; /** * A Layout where the positions of the children can be described in relation to each other or to the @@ -55,6 +65,10 @@ import java.util.TreeSet; */ @RemoteView public class RelativeLayout extends ViewGroup { + private static final String LOG_TAG = "RelativeLayout"; + + private static final boolean DEBUG_GRAPH = false; + public static final int TRUE = -1; /** @@ -142,7 +156,12 @@ public class RelativeLayout extends ViewGroup { private final Rect mSelfBounds = new Rect(); private int mIgnoreGravity; - private static SortedSet<View> mTopToBottomLeftToRightSet = null; + private SortedSet<View> mTopToBottomLeftToRightSet = null; + + private boolean mDirtyHierarchy; + private View[] mSortedHorizontalChildren = new View[0]; + private View[] mSortedVerticalChildren = new View[0]; + private final DependencyGraph mGraph = new DependencyGraph(); public RelativeLayout(Context context) { super(context); @@ -232,7 +251,54 @@ public class RelativeLayout extends ViewGroup { } @Override + public void requestLayout() { + super.requestLayout(); + mDirtyHierarchy = true; + } + + private void sortChildren() { + int count = getChildCount(); + if (mSortedVerticalChildren.length != count) mSortedVerticalChildren = new View[count]; + if (mSortedHorizontalChildren.length != count) mSortedHorizontalChildren = new View[count]; + + final DependencyGraph graph = mGraph; + graph.clear(); + + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + graph.add(child); + } + + if (DEBUG_GRAPH) { + d(LOG_TAG, "=== Sorted vertical children"); + graph.log(getResources(), ABOVE, BELOW, ALIGN_BASELINE, ALIGN_TOP, ALIGN_BOTTOM); + d(LOG_TAG, "=== Sorted horizontal children"); + graph.log(getResources(), LEFT_OF, RIGHT_OF, ALIGN_LEFT, ALIGN_RIGHT); + } + + graph.getSortedViews(mSortedVerticalChildren, ABOVE, BELOW, ALIGN_BASELINE, + ALIGN_TOP, ALIGN_BOTTOM); + graph.getSortedViews(mSortedHorizontalChildren, LEFT_OF, RIGHT_OF, ALIGN_LEFT, ALIGN_RIGHT); + + if (DEBUG_GRAPH) { + d(LOG_TAG, "=== Ordered list of vertical children"); + for (View view : mSortedVerticalChildren) { + DependencyGraph.printViewId(getResources(), view); + } + d(LOG_TAG, "=== Ordered list of horizontal children"); + for (View view : mSortedHorizontalChildren) { + DependencyGraph.printViewId(getResources(), view); + } + } + } + + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (mDirtyHierarchy) { + mDirtyHierarchy = false; + sortChildren(); + } + int myWidth = -1; int myHeight = -1; @@ -261,7 +327,6 @@ public class RelativeLayout extends ViewGroup { height = myHeight; } - int len = this.getChildCount(); mHasBaselineAlignedChild = false; View ignore = null; @@ -275,22 +340,50 @@ public class RelativeLayout extends ViewGroup { int right = Integer.MIN_VALUE; int bottom = Integer.MIN_VALUE; + boolean offsetHorizontalAxis = false; + boolean offsetVerticalAxis = false; + if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) { ignore = findViewById(mIgnoreGravity); } - for (int i = 0; i < len; i++) { - View child = getChildAt(i); + final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY; + final boolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY; + + View[] views = mSortedHorizontalChildren; + int count = views.length; + for (int i = 0; i < count; i++) { + View child = views[i]; + if (child.getVisibility() != GONE) { + LayoutParams params = (LayoutParams) child.getLayoutParams(); + + applyHorizontalSizeRules(params, myWidth); + measureChildHorizontal(child, params, myWidth, myHeight); + if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) { + offsetHorizontalAxis = true; + } + } + } + + views = mSortedVerticalChildren; + count = views.length; + + for (int i = 0; i < count; i++) { + View child = views[i]; if (child.getVisibility() != GONE) { LayoutParams params = (LayoutParams) child.getLayoutParams(); - applySizeRules(params, myWidth, myHeight); + + applyVerticalSizeRules(params, myHeight); measureChild(child, params, myWidth, myHeight); - positionChild(child, params, myWidth, myHeight); + if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) { + offsetVerticalAxis = true; + } - if (widthMode != MeasureSpec.EXACTLY) { + if (isWrapContentWidth) { width = Math.max(width, params.mRight); } - if (heightMode != MeasureSpec.EXACTLY) { + + if (isWrapContentHeight) { height = Math.max(height, params.mBottom); } @@ -307,15 +400,15 @@ public class RelativeLayout extends ViewGroup { } if (mHasBaselineAlignedChild) { - for (int i = 0; i < len; i++) { + for (int i = 0; i < count; i++) { View child = getChildAt(i); if (child.getVisibility() != GONE) { LayoutParams params = (LayoutParams) child.getLayoutParams(); alignBaseline(child, params); if (child != ignore || verticalGravity) { - left = Math.min(left, params.mLeft - params.leftMargin); - top = Math.min(top, params.mTop - params.topMargin); + left = Math.min(left, params.mLeft - params.leftMargin); + top = Math.min(top, params.mTop - params.topMargin); } if (child != ignore || horizontalGravity) { @@ -326,7 +419,7 @@ public class RelativeLayout extends ViewGroup { } } - if (widthMode != MeasureSpec.EXACTLY) { + if (isWrapContentWidth) { // Width already has left padding in it since it was calculated by looking at // the right of each child view width += mPaddingRight; @@ -337,8 +430,22 @@ public class RelativeLayout extends ViewGroup { width = Math.max(width, getSuggestedMinimumWidth()); width = resolveSize(width, widthMeasureSpec); + + if (offsetHorizontalAxis) { + for (int i = 0; i < count; i++) { + View child = getChildAt(i); + if (child.getVisibility() != GONE) { + LayoutParams params = (LayoutParams) child.getLayoutParams(); + final int[] rules = params.getRules(); + if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) { + centerHorizontal(child, params, width); + } + } + } + } } - if (heightMode != MeasureSpec.EXACTLY) { + + if (isWrapContentHeight) { // Height already has top padding in it since it was calculated by looking at // the bottom of each child view height += mPaddingBottom; @@ -349,6 +456,19 @@ public class RelativeLayout extends ViewGroup { height = Math.max(height, getSuggestedMinimumHeight()); height = resolveSize(height, heightMeasureSpec); + + if (offsetVerticalAxis) { + for (int i = 0; i < count; i++) { + View child = getChildAt(i); + if (child.getVisibility() != GONE) { + LayoutParams params = (LayoutParams) child.getLayoutParams(); + final int[] rules = params.getRules(); + if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) { + centerVertical(child, params, height); + } + } + } + } } if (horizontalGravity || verticalGravity) { @@ -362,7 +482,7 @@ public class RelativeLayout extends ViewGroup { final int horizontalOffset = contentBounds.left - left; final int verticalOffset = contentBounds.top - top; if (horizontalOffset != 0 || verticalOffset != 0) { - for (int i = 0; i < len; i++) { + for (int i = 0; i < count; i++) { View child = getChildAt(i); if (child.getVisibility() != GONE && child != ignore) { LayoutParams params = (LayoutParams) child.getLayoutParams(); @@ -416,9 +536,7 @@ public class RelativeLayout extends ViewGroup { * @param myWidth Width of the the RelativeLayout * @param myHeight Height of the RelativeLayout */ - private void measureChild(View child, LayoutParams params, int myWidth, - int myHeight) { - + private void measureChild(View child, LayoutParams params, int myWidth, int myHeight) { int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft, params.mRight, params.width, params.leftMargin, params.rightMargin, @@ -432,6 +550,21 @@ public class RelativeLayout extends ViewGroup { child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } + private void measureChildHorizontal(View child, LayoutParams params, int myWidth, int myHeight) { + int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft, + params.mRight, params.width, + params.leftMargin, params.rightMargin, + mPaddingLeft, mPaddingRight, + myWidth); + int childHeightMeasureSpec; + if (params.width == LayoutParams.FILL_PARENT) { + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY, myHeight); + } else { + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + } + child.measure(childWidthMeasureSpec, childHeightMeasureSpec); + } + /** * Get a measure spec that accounts for all of the constraints on this view. * This includes size contstraints imposed by the RelativeLayout as well as @@ -511,19 +644,9 @@ public class RelativeLayout extends ViewGroup { return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode); } - /** - * After the child has been measured, assign it a position. Some views may - * already have final values for l,t,r,b. Others may have one or both edges - * unfixed (i.e. set to -1) in each dimension. These will get positioned - * based on which edge is fixed, the view's desired dimension, and whether - * or not it is centered. - * - * @param child Child to position - * @param params LayoutParams associated with child - * @param myWidth Width of the the RelativeLayout - * @param myHeight Height of the RelativeLayout - */ - private void positionChild(View child, LayoutParams params, int myWidth, int myHeight) { + private boolean positionChildHorizontal(View child, LayoutParams params, int myWidth, + boolean wrapContent) { + int[] rules = params.getRules(); if (params.mLeft < 0 && params.mRight >= 0) { @@ -534,13 +657,26 @@ public class RelativeLayout extends ViewGroup { params.mRight = params.mLeft + child.getMeasuredWidth(); } else if (params.mLeft < 0 && params.mRight < 0) { // Both left and right vary - if (0 != rules[CENTER_IN_PARENT] || 0 != rules[CENTER_HORIZONTAL]) { - centerHorizontal(child, params, myWidth); + if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) { + if (!wrapContent) { + centerHorizontal(child, params, myWidth); + } else { + params.mLeft = mPaddingLeft + params.leftMargin; + params.mRight = params.mLeft + child.getMeasuredWidth(); + } + return true; } else { params.mLeft = mPaddingLeft + params.leftMargin; params.mRight = params.mLeft + child.getMeasuredWidth(); } } + return false; + } + + private boolean positionChildVertical(View child, LayoutParams params, int myHeight, + boolean wrapContent) { + + int[] rules = params.getRules(); if (params.mTop < 0 && params.mBottom >= 0) { // Bottom is fixed, but top varies @@ -550,26 +686,23 @@ public class RelativeLayout extends ViewGroup { params.mBottom = params.mTop + child.getMeasuredHeight(); } else if (params.mTop < 0 && params.mBottom < 0) { // Both top and bottom vary - if (0 != rules[CENTER_IN_PARENT] || 0 != rules[CENTER_VERTICAL]) { - centerVertical(child, params, myHeight); + if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) { + if (!wrapContent) { + centerVertical(child, params, myHeight); + } else { + params.mTop = mPaddingTop + params.topMargin; + params.mBottom = params.mTop + child.getMeasuredHeight(); + } + return true; } else { params.mTop = mPaddingTop + params.topMargin; params.mBottom = params.mTop + child.getMeasuredHeight(); } } + return false; } - /** - * Set l,t,r,b values in the LayoutParams for one view based on its layout rules. - * Big assumption #1: All antecedents of this view have been sized & positioned - * Big assumption #2: The dimensions of the parent view (the RelativeLayout) - * are already known if they are needed. - * - * @param childParams LayoutParams for the view being positioned - * @param myWidth Width of the the RelativeLayout - * @param myHeight Height of the RelativeLayout - */ - private void applySizeRules(LayoutParams childParams, int myWidth, int myHeight) { + private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth) { int[] rules = childParams.getRules(); RelativeLayout.LayoutParams anchorParams; @@ -629,6 +762,11 @@ public class RelativeLayout extends ViewGroup { // FIXME uh oh... } } + } + + private void applyVerticalSizeRules(LayoutParams childParams, int myHeight) { + int[] rules = childParams.getRules(); + RelativeLayout.LayoutParams anchorParams; childParams.mTop = -1; childParams.mBottom = -1; @@ -691,18 +829,16 @@ public class RelativeLayout extends ViewGroup { private View getRelatedView(int[] rules, int relation) { int id = rules[relation]; if (id != 0) { - View v = findViewById(id); - if (v == null) { - return null; - } + DependencyGraph.Node node = mGraph.mKeyNodes.get(id); + if (node == null) return null; + View v = node.view; // Find the first non-GONE view up the chain while (v.getVisibility() == View.GONE) { rules = ((LayoutParams) v.getLayoutParams()).getRules(); - v = v.findViewById(rules[relation]); - if (v == null) { - return null; - } + node = mGraph.mKeyNodes.get((rules[relation])); + if (node == null) return null; + v = node.view; } return v; @@ -1033,4 +1169,282 @@ public class RelativeLayout extends ViewGroup { return mRules; } } + + private static class DependencyGraph { + /** + * List of all views in the graph. + */ + private ArrayList<Node> mNodes = new ArrayList<Node>(); + + /** + * List of nodes in the graph. Each node is identified by its + * view id (see View#getId()). + */ + private SparseArray<Node> mKeyNodes = new SparseArray<Node>(); + + /** + * Temporary data structure used to build the list of roots + * for this graph. + */ + private LinkedList<Node> mRoots = new LinkedList<Node>(); + + /** + * Clears the graph. + */ + void clear() { + final ArrayList<Node> nodes = mNodes; + final int count = nodes.size(); + + for (int i = 0; i < count; i++) { + nodes.get(i).release(); + } + nodes.clear(); + + mKeyNodes.clear(); + mRoots.clear(); + } + + /** + * Adds a view to the graph. + * + * @param view The view to be added as a node to the graph. + */ + void add(View view) { + final int id = view.getId(); + final Node node = Node.acquire(view); + + if (id != View.NO_ID) { + mKeyNodes.put(id, node); + } + + mNodes.add(node); + } + + /** + * Builds a sorted list of views. The sorting order depends on the dependencies + * between the view. For instance, if view C needs view A to be processed first + * and view A needs view B to be processed first, the dependency graph + * is: B -> A -> C. The sorted array will contain views B, A and C in this order. + * + * @param sorted The sorted list of views. The length of this array must + * be equal to getChildCount(). + * @param rules The list of rules to take into account. + */ + void getSortedViews(View[] sorted, int... rules) { + final LinkedList<Node> roots = findRoots(rules); + int index = 0; + + while (roots.size() > 0) { + final Node node = roots.removeFirst(); + final View view = node.view; + final int key = view.getId(); + + sorted[index++] = view; + + final HashSet<Node> dependents = node.dependents; + for (Node dependent : dependents) { + final SparseArray<Node> dependencies = dependent.dependencies; + + dependencies.remove(key); + if (dependencies.size() == 0) { + roots.add(dependent); + } + } + } + + if (index < sorted.length) { + throw new IllegalStateException("Circular dependencies cannot exist" + + " in RelativeLayout"); + } + } + + /** + * Finds the roots of the graph. A root is a node with no dependency and + * with [0..n] dependents. + * + * @param rulesFilter The list of rules to consider when building the + * dependencies + * + * @return A list of node, each being a root of the graph + */ + private LinkedList<Node> findRoots(int[] rulesFilter) { + final SparseArray<Node> keyNodes = mKeyNodes; + final ArrayList<Node> nodes = mNodes; + final int count = nodes.size(); + + // Find roots can be invoked several times, so make sure to clear + // all dependents and dependencies before running the algorithm + for (int i = 0; i < count; i++) { + final Node node = nodes.get(i); + node.dependents.clear(); + node.dependencies.clear(); + } + + // Builds up the dependents and dependencies for each node of the graph + for (int i = 0; i < count; i++) { + final Node node = nodes.get(i); + + final LayoutParams layoutParams = (LayoutParams) node.view.getLayoutParams(); + final int[] rules = layoutParams.mRules; + final int rulesCount = rulesFilter.length; + + // Look only the the rules passed in parameter, this way we build only the + // dependencies for a specific set of rules + for (int j = 0; j < rulesCount; j++) { + final int rule = rules[rulesFilter[j]]; + if (rule > 0) { + // The node this node depends on + final Node dependency = keyNodes.get(rule); + if (dependency == node) { + throw new IllegalStateException("A view cannot have a dependency" + + " on itself"); + } + if (dependency == null) { + continue; + } + // Add the current node as a dependent + dependency.dependents.add(node); + // Add a dependency to the current node + node.dependencies.put(rule, dependency); + } + } + } + + final LinkedList<Node> roots = mRoots; + roots.clear(); + + // Finds all the roots in the graph: all nodes with no dependencies + for (int i = 0; i < count; i++) { + final Node node = nodes.get(i); + if (node.dependencies.size() == 0) roots.add(node); + } + + return roots; + } + + /** + * Prints the dependency graph for the specified rules. + * + * @param resources The context's resources to print the ids. + * @param rules The list of rules to take into account. + */ + void log(Resources resources, int... rules) { + final LinkedList<Node> roots = findRoots(rules); + for (Node node : roots) { + printNode(resources, node); + } + } + + static void printViewId(Resources resources, View view) { + if (view.getId() != View.NO_ID) { + d(LOG_TAG, resources.getResourceEntryName(view.getId())); + } else { + d(LOG_TAG, "NO_ID"); + } + } + + private static void appendViewId(Resources resources, Node node, StringBuilder buffer) { + if (node.view.getId() != View.NO_ID) { + buffer.append(resources.getResourceEntryName(node.view.getId())); + } else { + buffer.append("NO_ID"); + } + } + + private static void printNode(Resources resources, Node node) { + if (node.dependents.size() == 0) { + printViewId(resources, node.view); + } else { + for (Node dependent : node.dependents) { + StringBuilder buffer = new StringBuilder(); + appendViewId(resources, node, buffer); + printdependents(resources, dependent, buffer); + } + } + } + + private static void printdependents(Resources resources, Node node, StringBuilder buffer) { + buffer.append(" -> "); + appendViewId(resources, node, buffer); + + if (node.dependents.size() == 0) { + d(LOG_TAG, buffer.toString()); + } else { + for (Node dependent : node.dependents) { + StringBuilder subBuffer = new StringBuilder(buffer); + printdependents(resources, dependent, subBuffer); + } + } + } + + /** + * A node in the dependency graph. A node is a view, its list of dependencies + * and its list of dependents. + * + * A node with no dependent is considered a root of the graph. + */ + static class Node implements Poolable<Node> { + /** + * The view representing this node in the layout. + */ + View view; + + /** + * The list of dependents for this node; a dependent is a node + * that needs this node to be processed first. + */ + final HashSet<Node> dependents = new HashSet<Node>(); + + /** + * The list of dependencies for this node. + */ + final SparseArray<Node> dependencies = new SparseArray<Node>(); + + /* + * START POOL IMPLEMENTATION + */ + private static final int POOL_LIMIT = 12; + private static final Pool<Node> sPool = Pools.synchronizedPool( + Pools.finitePool(new PoolableManager<Node>() { + public Node newInstance() { + return new Node(); + } + + public void onAcquired(Node element) { + } + + public void onReleased(Node element) { + } + }, POOL_LIMIT) + ); + + private Node mNext; + + public void setNextPoolable(Node element) { + mNext = element; + } + + public Node getNextPoolable() { + return mNext; + } + + static Node acquire(View view) { + final Node node = sPool.acquire(); + node.view = view; + + return node; + } + + void release() { + view = null; + dependents.clear(); + dependencies.clear(); + + sPool.release(this); + } + /* + * END POOL IMPLEMENTATION + */ + } + } } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 9479f9e..d8ed4f0 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -3681,12 +3681,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override protected boolean isPaddingOffsetRequired() { - return mShadowRadius != 0; + return mShadowRadius != 0 || mDrawables != null; } @Override protected int getLeftPaddingOffset() { - return (int) Math.min(0, mShadowDx - mShadowRadius); + return getCompoundPaddingLeft() - mPaddingLeft + + (int) Math.min(0, mShadowDx - mShadowRadius); } @Override @@ -3701,7 +3702,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override protected int getRightPaddingOffset() { - return (int) Math.max(0, mShadowDx + mShadowRadius); + return -(getCompoundPaddingRight() - mPaddingRight) + + (int) Math.max(0, mShadowDx + mShadowRadius); } @Override @@ -6665,9 +6667,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } else if (getLineCount() == 1) { switch (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { case Gravity.LEFT: - return (mLayout.getLineRight(0) - mScrollX - (mRight - mLeft) - - getCompoundPaddingLeft() - getCompoundPaddingRight()) / - getHorizontalFadingEdgeLength(); + final int textWidth = (mRight - mLeft) - getCompoundPaddingLeft() - + getCompoundPaddingRight(); + final float lineWidth = mLayout.getLineWidth(0); + return (lineWidth - textWidth) / getHorizontalFadingEdgeLength(); case Gravity.RIGHT: return 0.0f; case Gravity.CENTER_HORIZONTAL: diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index ce32754..4bac593 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -37,6 +37,7 @@ interface IBatteryStats { void notePhoneOff(); void notePhoneSignalStrength(in SignalStrength signalStrength); void notePhoneDataConnectionState(int dataType, boolean hasData); + void noteAirplaneMode(boolean isAirplaneMode); void noteWifiOn(int uid); void noteWifiOff(int uid); void noteWifiRunning(); diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl index 84ed729..4bef265 100644 --- a/core/java/com/android/internal/backup/IBackupTransport.aidl +++ b/core/java/com/android/internal/backup/IBackupTransport.aidl @@ -41,6 +41,20 @@ interface IBackupTransport { - adb: close the file */ /** + * Ask the transport where, on local device storage, to keep backup state blobs. + * This is per-transport so that mock transports used for testing can coexist with + * "live" backup services without interfering with the live bookkeeping. The + * returned string should be a name that is expected to be unambiguous among all + * available backup transports; the name of the class implementing the transport + * is a good choice. + * + * @return A unique name, suitable for use as a file or directory name, that the + * Backup Manager could use to disambiguate state files associated with + * different backup transports. + */ + String transportDirName(); + + /** * Verify that this is a suitable time for a backup pass. This should return zero * if a backup is reasonable right now, some positive value otherwise. This method * will be called outside of the {@link #startSession}/{@link #endSession} pair. @@ -54,63 +68,70 @@ interface IBackupTransport { long requestBackupTime(); /** - * Establish a connection to the back-end data repository, if necessary. If the transport - * needs to initialize state that is not tied to individual applications' backup operations, - * this is where it should be done. - * - * @return Zero on success; a nonzero error code on failure. - */ - int startSession(); - - /** - * Send one application's data to the backup destination. + * Send one application's data to the backup destination. The transport may send + * the data immediately, or may buffer it. After this is called, {@link #finishBackup} + * must be called to ensure the data is sent and recorded successfully. * * @param packageInfo The identity of the application whose data is being backed up. * This specifically includes the signature list for the package. * @param data The data stream that resulted from invoking the application's * BackupService.doBackup() method. This may be a pipe rather than a file on * persistent media, so it may not be seekable. - * @return Zero on success; a nonzero error code on failure. + * @return false if errors occurred (the backup should be aborted and rescheduled), + * true if everything is OK so far (but {@link #finishBackup} must be called). */ - int performBackup(in PackageInfo packageInfo, in ParcelFileDescriptor data); + boolean performBackup(in PackageInfo packageInfo, in ParcelFileDescriptor inFd); + + /** + * Finish sending application data to the backup destination. This must be + * called after {@link #performBackup} to ensure that all data is sent. Only + * when this method returns true can the backup be assumed to have succeeded. + * + * @return false if errors occurred (the backup should be aborted and rescheduled), + * true if everything is OK so far (but {@link #finishBackup} must be called). + */ + boolean finishBackup(); /** * Get the set of backups currently available over this transport. * - * @return Descriptions of the set of restore images available for this device. + * @return Descriptions of the set of restore images available for this device, + * or null if an error occurred (the attempt should be rescheduled). **/ RestoreSet[] getAvailableRestoreSets(); /** - * Get the set of applications from a given restore image. + * Start restoring application data from backup. After calling this function, + * alternate calls to {@link #nextRestorePackage} and {@link #nextRestoreData} + * to walk through the actual application data. * * @param token A backup token as returned by {@link #getAvailableRestoreSets}. - * @return An array of PackageInfo objects describing all of the applications - * available for restore from this restore image. This should include the list - * of signatures for each package so that the Backup Manager can filter using that - * information. + * @param packages List of applications to restore (if data is available). + * Application data will be restored in the order given. + * @return false if errors occurred (the restore should be aborted and rescheduled), + * true if everything is OK so far (go ahead and call {@link #nextRestorePackage}). */ - PackageInfo[] getAppSet(int token); + boolean startRestore(long token, in PackageInfo[] packages); /** - * Retrieve one application's data from the backing store. - * - * @param token The backup record from which a restore is being requested. - * @param packageInfo The identity of the application whose data is being restored. - * This must include the signature list for the package; it is up to the transport - * to verify that the requested app's signatures match the saved backup record - * because the transport cannot necessarily trust the client device. - * @param data An open, writable file into which the backup image should be stored. - * @return Zero on success; a nonzero error code on failure. + * Get the package name of the next application with data in the backup store. + * @return The name of one of the packages supplied to {@link #startRestore}, + * or "" (the empty string) if no more backup data is available, + * or null if an error occurred (the restore should be aborted and rescheduled). */ - int getRestoreData(int token, in PackageInfo packageInfo, in ParcelFileDescriptor data); + String nextRestorePackage(); /** - * Terminate the backup session, closing files, freeing memory, and cleaning up whatever - * other state the transport required. - * - * @return Zero on success; a nonzero error code on failure. Even on failure, the session - * is torn down and must be restarted if another backup is attempted. + * Get the data for the application returned by {@link #nextRestorePackage}. + * @param data An open, writable file into which the backup data should be stored. + * @return false if errors occurred (the restore should be aborted and rescheduled), + * true if everything is OK so far (go ahead and call {@link #nextRestorePackage}). + */ + boolean getRestoreData(in ParcelFileDescriptor outFd); + + /** + * End a restore session (aborting any in-process data transfer as necessary), + * freeing any resources and connections used during the restore process. */ - int endSession(); + void finishRestore(); } diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java index 3dd71f3..c5d9d40 100644 --- a/core/java/com/android/internal/backup/LocalTransport.java +++ b/core/java/com/android/internal/backup/LocalTransport.java @@ -30,14 +30,14 @@ public class LocalTransport extends IBackupTransport.Stub { private static final String TAG = "LocalTransport"; private static final boolean DEBUG = true; + private static final String TRANSPORT_DIR_NAME + = "com.android.internal.backup.LocalTransport"; + private Context mContext; private PackageManager mPackageManager; private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup"); - private FileFilter mDirFileFilter = new FileFilter() { - public boolean accept(File f) { - return f.isDirectory(); - } - }; + private PackageInfo[] mRestorePackages = null; + private int mRestorePackage = -1; // Index into mRestorePackages public LocalTransport(Context context) { @@ -46,26 +46,19 @@ public class LocalTransport extends IBackupTransport.Stub { mPackageManager = context.getPackageManager(); } - public long requestBackupTime() throws RemoteException { - // any time is a good time for local backup - return 0; - } - public int startSession() throws RemoteException { - if (DEBUG) Log.v(TAG, "session started"); - mDataDir.mkdirs(); - return 0; + public String transportDirName() throws RemoteException { + return TRANSPORT_DIR_NAME; } - public int endSession() throws RemoteException { - if (DEBUG) Log.v(TAG, "session ended"); + public long requestBackupTime() throws RemoteException { + // any time is a good time for local backup return 0; } - public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data) + public boolean performBackup(PackageInfo packageInfo, ParcelFileDescriptor data) throws RemoteException { if (DEBUG) Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName); - int err = 0; File packageDir = new File(mDataDir, packageInfo.packageName); packageDir.mkdirs(); @@ -101,9 +94,8 @@ public class LocalTransport extends IBackupTransport.Stub { try { entity.write(buf, 0, dataSize); } catch (IOException e) { - Log.e(TAG, "Unable to update key file " - + entityFile.getAbsolutePath()); - err = -1; + Log.e(TAG, "Unable to update key file " + entityFile.getAbsolutePath()); + return false; } finally { entity.close(); } @@ -111,14 +103,17 @@ public class LocalTransport extends IBackupTransport.Stub { entityFile.delete(); } } + return true; } catch (IOException e) { // oops, something went wrong. abort the operation and return error. - Log.v(TAG, "Exception reading backup input:"); - e.printStackTrace(); - err = -1; + Log.v(TAG, "Exception reading backup input:", e); + return false; } + } - return err; + public boolean finishBackup() throws RemoteException { + if (DEBUG) Log.v(TAG, "finishBackup()"); + return true; } // Restore handling @@ -129,72 +124,66 @@ public class LocalTransport extends IBackupTransport.Stub { return array; } - public PackageInfo[] getAppSet(int token) throws android.os.RemoteException { - if (DEBUG) Log.v(TAG, "getting app set " + token); - // the available packages are the extant subdirs of mDatadir - File[] packageDirs = mDataDir.listFiles(mDirFileFilter); - ArrayList<PackageInfo> packages = new ArrayList<PackageInfo>(); - for (File dir : packageDirs) { - try { - PackageInfo pkg = mPackageManager.getPackageInfo(dir.getName(), - PackageManager.GET_SIGNATURES); - if (pkg != null) { - packages.add(pkg); - } - } catch (NameNotFoundException e) { - // restore set contains data for a package not installed on the - // phone -- just ignore it. - } - } + public boolean startRestore(long token, PackageInfo[] packages) { + if (DEBUG) Log.v(TAG, "start restore " + token); + mRestorePackages = packages; + mRestorePackage = -1; + return true; + } - if (DEBUG) { - Log.v(TAG, "Built app set of " + packages.size() + " entries:"); - for (PackageInfo p : packages) { - Log.v(TAG, " + " + p.packageName); + public String nextRestorePackage() { + if (mRestorePackages == null) throw new IllegalStateException("startRestore not called"); + while (++mRestorePackage < mRestorePackages.length) { + String name = mRestorePackages[mRestorePackage].packageName; + if (new File(mDataDir, name).isDirectory()) { + if (DEBUG) Log.v(TAG, " nextRestorePackage() = " + name); + return name; } } - PackageInfo[] result = new PackageInfo[packages.size()]; - return packages.toArray(result); + if (DEBUG) Log.v(TAG, " no more packages to restore"); + return ""; } - public int getRestoreData(int token, PackageInfo packageInfo, ParcelFileDescriptor outFd) - throws android.os.RemoteException { - if (DEBUG) Log.v(TAG, "getting restore data " + token + " : " + packageInfo.packageName); - // we only support one hardcoded restore set - if (token != 0) return -1; - - // the data for a given package is at a known location - File packageDir = new File(mDataDir, packageInfo.packageName); + public boolean getRestoreData(ParcelFileDescriptor outFd) { + if (mRestorePackages == null) throw new IllegalStateException("startRestore not called"); + if (mRestorePackage < 0) throw new IllegalStateException("nextRestorePackage not called"); + File packageDir = new File(mDataDir, mRestorePackages[mRestorePackage].packageName); // The restore set is the concatenation of the individual record blobs, // each of which is a file in the package's directory File[] blobs = packageDir.listFiles(); - if (DEBUG) Log.v(TAG, " found " + blobs.length + " key files"); - int err = 0; - if (blobs != null && blobs.length > 0) { - BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor()); - try { - for (File f : blobs) { - copyToRestoreData(f, out); + if (blobs == null) { + Log.e(TAG, "Error listing directory: " + packageDir); + return false; // nextRestorePackage() ensures the dir exists, so this is an error + } + + // We expect at least some data if the directory exists in the first place + if (DEBUG) Log.v(TAG, " getRestoreData() found " + blobs.length + " key files"); + BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor()); + try { + for (File f : blobs) { + FileInputStream in = new FileInputStream(f); + try { + int size = (int) f.length(); + byte[] buf = new byte[size]; + in.read(buf); + String key = new String(Base64.decode(f.getName())); + if (DEBUG) Log.v(TAG, " ... key=" + key + " size=" + size); + out.writeEntityHeader(key, size); + out.writeEntityData(buf, size); + } finally { + in.close(); } - } catch (Exception e) { - Log.e(TAG, "Unable to read backup records"); - err = -1; } + return true; + } catch (IOException e) { + Log.e(TAG, "Unable to read backup records", e); + return false; } - return err; } - private void copyToRestoreData(File f, BackupDataOutput out) throws IOException { - FileInputStream in = new FileInputStream(f); - int size = (int) f.length(); - byte[] buf = new byte[size]; - in.read(buf); - String key = new String(Base64.decode(f.getName())); - if (DEBUG) Log.v(TAG, " ... copy to stream: key=" + key - + " size=" + size); - out.writeEntityHeader(key, size); - out.writeEntityData(buf, size); + public void finishRestore() { + if (DEBUG) Log.v(TAG, "finishRestore()"); } } diff --git a/core/java/com/android/internal/backup/SystemBackupAgent.java b/core/java/com/android/internal/backup/SystemBackupAgent.java new file mode 100644 index 0000000..6b396d7 --- /dev/null +++ b/core/java/com/android/internal/backup/SystemBackupAgent.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.backup; + +import android.backup.AbsoluteFileBackupHelper; +import android.backup.BackupHelperAgent; + +/** + * Backup agent for various system-managed data + */ +public class SystemBackupAgent extends BackupHelperAgent { + // the set of files that we back up whole, as absolute paths + String[] mFiles = { + /* WallpaperService.WALLPAPER_FILE */ + "/data/data/com.android.settings/files/wallpaper", + }; + + public void onCreate() { + addHelper("system_files", new AbsoluteFileBackupHelper(this, mFiles)); + } +} diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 16a3bad..a03802d 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -191,6 +191,8 @@ public final class BatteryStatsImpl extends BatteryStats { private final Map<String, KernelWakelockStats> mProcWakelockFileStats = new HashMap<String, KernelWakelockStats>(); + private HashMap<String, Integer> mUidCache = new HashMap<String, Integer>(); + // For debugging public BatteryStatsImpl() { mFile = mBackupFile = null; @@ -714,6 +716,10 @@ public final class BatteryStatsImpl extends BatteryStats { } } + boolean isRunningLocked() { + return mNesting > 0; + } + void stopRunningLocked(BatteryStatsImpl stats) { // Ignore attempt to stop a timer that isn't running if (mNesting == 0) { @@ -984,11 +990,11 @@ public final class BatteryStatsImpl extends BatteryStats { } public void noteStartGps(int uid) { - mUidStats.get(uid).noteStartGps(); + getUidStatsLocked(uid).noteStartGps(); } public void noteStopGps(int uid) { - mUidStats.get(uid).noteStopGps(); + getUidStatsLocked(uid).noteStopGps(); } public void noteScreenOnLocked() { @@ -1032,10 +1038,7 @@ public final class BatteryStatsImpl extends BatteryStats { } public void noteUserActivityLocked(int uid, int event) { - Uid u = mUidStats.get(uid); - if (u != null) { - u.noteUserActivityLocked(event); - } + getUidStatsLocked(uid).noteUserActivityLocked(event); } public void notePhoneOnLocked() { @@ -1051,7 +1054,24 @@ public final class BatteryStatsImpl extends BatteryStats { mPhoneOnTimer.stopRunningLocked(this); } } - + + public void noteAirplaneModeLocked(boolean isAirplaneMode) { + final int bin = mPhoneSignalStrengthBin; + if (bin >= 0) { + if (!isAirplaneMode) { + if (!mPhoneSignalStrengthsTimer[bin].isRunningLocked()) { + mPhoneSignalStrengthsTimer[bin].startRunningLocked(this); + } + } else { + for (int i = 0; i < NUM_SIGNAL_STRENGTH_BINS; i++) { + while (mPhoneSignalStrengthsTimer[i].isRunningLocked()) { + mPhoneSignalStrengthsTimer[i].stopRunningLocked(this); + } + } + } + } + } + public void notePhoneSignalStrengthLocked(SignalStrength signalStrength) { // Bin the strength. int bin; @@ -1115,16 +1135,10 @@ public final class BatteryStatsImpl extends BatteryStats { } if (mWifiOnUid != uid) { if (mWifiOnUid >= 0) { - Uid u = mUidStats.get(mWifiOnUid); - if (u != null) { - u.noteWifiTurnedOffLocked(); - } + getUidStatsLocked(mWifiOnUid).noteWifiTurnedOffLocked(); } mWifiOnUid = uid; - Uid u = mUidStats.get(uid); - if (u != null) { - u.noteWifiTurnedOnLocked(); - } + getUidStatsLocked(uid).noteWifiTurnedOnLocked(); } } @@ -1134,10 +1148,7 @@ public final class BatteryStatsImpl extends BatteryStats { mWifiOnTimer.stopRunningLocked(this); } if (mWifiOnUid >= 0) { - Uid u = mUidStats.get(mWifiOnUid); - if (u != null) { - u.noteWifiTurnedOffLocked(); - } + getUidStatsLocked(mWifiOnUid).noteWifiTurnedOffLocked(); mWifiOnUid = -1; } } @@ -1147,10 +1158,7 @@ public final class BatteryStatsImpl extends BatteryStats { mAudioOn = true; mAudioOnTimer.startRunningLocked(this); } - Uid u = mUidStats.get(uid); - if (u != null) { - u.noteAudioTurnedOnLocked(); - } + getUidStatsLocked(uid).noteAudioTurnedOnLocked(); } public void noteAudioOffLocked(int uid) { @@ -1158,10 +1166,7 @@ public final class BatteryStatsImpl extends BatteryStats { mAudioOn = false; mAudioOnTimer.stopRunningLocked(this); } - Uid u = mUidStats.get(uid); - if (u != null) { - u.noteAudioTurnedOffLocked(); - } + getUidStatsLocked(uid).noteAudioTurnedOffLocked(); } public void noteVideoOnLocked(int uid) { @@ -1169,10 +1174,7 @@ public final class BatteryStatsImpl extends BatteryStats { mVideoOn = true; mVideoOnTimer.startRunningLocked(this); } - Uid u = mUidStats.get(uid); - if (u != null) { - u.noteVideoTurnedOnLocked(); - } + getUidStatsLocked(uid).noteVideoTurnedOnLocked(); } public void noteVideoOffLocked(int uid) { @@ -1180,10 +1182,7 @@ public final class BatteryStatsImpl extends BatteryStats { mVideoOn = false; mVideoOnTimer.stopRunningLocked(this); } - Uid u = mUidStats.get(uid); - if (u != null) { - u.noteVideoTurnedOffLocked(); - } + getUidStatsLocked(uid).noteVideoTurnedOffLocked(); } public void noteWifiRunningLocked() { @@ -1215,45 +1214,27 @@ public final class BatteryStatsImpl extends BatteryStats { } public void noteFullWifiLockAcquiredLocked(int uid) { - Uid u = mUidStats.get(uid); - if (u != null) { - u.noteFullWifiLockAcquiredLocked(); - } + getUidStatsLocked(uid).noteFullWifiLockAcquiredLocked(); } public void noteFullWifiLockReleasedLocked(int uid) { - Uid u = mUidStats.get(uid); - if (u != null) { - u.noteFullWifiLockReleasedLocked(); - } + getUidStatsLocked(uid).noteFullWifiLockReleasedLocked(); } public void noteScanWifiLockAcquiredLocked(int uid) { - Uid u = mUidStats.get(uid); - if (u != null) { - u.noteScanWifiLockAcquiredLocked(); - } + getUidStatsLocked(uid).noteScanWifiLockAcquiredLocked(); } public void noteScanWifiLockReleasedLocked(int uid) { - Uid u = mUidStats.get(uid); - if (u != null) { - u.noteScanWifiLockReleasedLocked(); - } + getUidStatsLocked(uid).noteScanWifiLockReleasedLocked(); } public void noteWifiMulticastEnabledLocked(int uid) { - Uid u = mUidStats.get(uid); - if (u != null) { - u.noteWifiMulticastEnabledLocked(); - } + getUidStatsLocked(uid).noteWifiMulticastEnabledLocked(); } public void noteWifiMulticastDisabledLocked(int uid) { - Uid u = mUidStats.get(uid); - if (u != null) { - u.noteWifiMulticastDisabledLocked(); - } + getUidStatsLocked(uid).noteWifiMulticastDisabledLocked(); } @Override public long getScreenOnTime(long batteryRealtime, int which) { @@ -2839,7 +2820,7 @@ public final class BatteryStatsImpl extends BatteryStats { public void removeUidStatsLocked(int uid) { mUidStats.remove(uid); } - + /** * Retrieve the statistics object for a particular process, creating * if needed. @@ -2850,6 +2831,24 @@ public final class BatteryStatsImpl extends BatteryStats { } /** + * Retrieve the statistics object for a particular process, given + * the name of the process. + * @param name process name + * @return the statistics object for the process + */ + public Uid.Proc getProcessStatsLocked(String name, int pid) { + int uid; + if (mUidCache.containsKey(name)) { + uid = mUidCache.get(name); + } else { + uid = Process.getUidForPid(pid); + mUidCache.put(name, uid); + } + Uid u = getUidStatsLocked(uid); + return u.getProcessStatsLocked(name); + } + + /** * Retrieve the statistics object for a particular process, creating * if needed. */ diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java index a37bf6e..4a8d8b1 100644 --- a/core/java/com/android/internal/os/PowerProfile.java +++ b/core/java/com/android/internal/os/PowerProfile.java @@ -219,12 +219,12 @@ public class PowerProfile { public double getAveragePower(String type, int level) { if (sPowerMap.containsKey(type)) { Object data = sPowerMap.get(type); - if (data instanceof double[]) { - final double[] values = (double[]) data; - if (values.length > level) { + if (data instanceof Double[]) { + final Double[] values = (Double[]) data; + if (values.length > level && level >= 0) { return values[level]; - } else if (values.length < 0) { - return values[0]; + } else if (level < 0) { + return 0; } else { return values[values.length - 1]; } diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 80c2489..888cb11 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -19,6 +19,7 @@ LOCAL_SRC_FILES:= \ ActivityManager.cpp \ AndroidRuntime.cpp \ CursorWindow.cpp \ + Time.cpp \ com_google_android_gles_jni_EGLImpl.cpp \ com_google_android_gles_jni_GLImpl.cpp.arm \ android_opengl_GLES10.cpp \ @@ -119,7 +120,8 @@ LOCAL_SRC_FILES:= \ com_android_internal_graphics_NativeUtils.cpp \ android_backup_BackupDataInput.cpp \ android_backup_BackupDataOutput.cpp \ - android_backup_FileBackupHelperBase.cpp + android_backup_FileBackupHelperBase.cpp \ + android_backup_BackupHelperDispatcher.cpp LOCAL_C_INCLUDES += \ $(JNI_H_INCLUDE) \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 7350348..c815301 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -158,6 +158,7 @@ extern int register_android_location_GpsLocationProvider(JNIEnv* env); extern int register_android_backup_BackupDataInput(JNIEnv *env); extern int register_android_backup_BackupDataOutput(JNIEnv *env); extern int register_android_backup_FileBackupHelperBase(JNIEnv *env); +extern int register_android_backup_BackupHelperDispatcher(JNIEnv *env); static AndroidRuntime* gCurRuntime = NULL; @@ -1131,6 +1132,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_backup_BackupDataInput), REG_JNI(register_android_backup_BackupDataOutput), REG_JNI(register_android_backup_FileBackupHelperBase), + REG_JNI(register_android_backup_BackupHelperDispatcher), }; /* diff --git a/libs/ui/Time.cpp b/core/jni/Time.cpp index b553913..f3037f3 100644 --- a/libs/ui/Time.cpp +++ b/core/jni/Time.cpp @@ -1,4 +1,4 @@ -#include <utils/TimeUtils.h> +#include "TimeUtils.h" #include <stdio.h> #include <cutils/tztime.h> diff --git a/include/utils/TimeUtils.h b/core/jni/TimeUtils.h index b19e021..b19e021 100644 --- a/include/utils/TimeUtils.h +++ b/core/jni/TimeUtils.h diff --git a/core/jni/android_backup_BackupHelperDispatcher.cpp b/core/jni/android_backup_BackupHelperDispatcher.cpp new file mode 100644 index 0000000..2e3f0b9 --- /dev/null +++ b/core/jni/android_backup_BackupHelperDispatcher.cpp @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "BackupHelperDispatcher_native" +#include <utils/Log.h> + +#include "JNIHelp.h" +#include <android_runtime/AndroidRuntime.h> + +#include <sys/types.h> +#include <sys/uio.h> +#include <unistd.h> + + +#define VERSION_1_HEADER 0x01706c48 // 'Hlp'1 little endian + +namespace android +{ + +struct chunk_header_v1 { + int headerSize; + int version; + int dataSize; // corresponds to Header.chunkSize + int nameLength; // not including the NULL terminator, which is not written to the file +}; + +// java.io.FileDescriptor +static jfieldID s_descriptorField = 0; +static jfieldID s_chunkSizeField = 0; +static jfieldID s_keyPrefixField = 0; + +static int +readHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj) +{ + chunk_header_v1 flattenedHeader; + int fd; + ssize_t amt; + String8 keyPrefix; + char* buf; + + fd = env->GetIntField(fdObj, s_descriptorField); + + amt = read(fd, &flattenedHeader.headerSize, sizeof(flattenedHeader.headerSize)); + if (amt != sizeof(flattenedHeader.headerSize)) { + return -1; + } + + int remainingHeader = flattenedHeader.headerSize - sizeof(flattenedHeader.headerSize); + + if (flattenedHeader.headerSize < (int)sizeof(chunk_header_v1)) { + LOGW("Skipping unknown header: %d bytes", flattenedHeader.headerSize); + if (remainingHeader > 0) { + lseek(fd, remainingHeader, SEEK_CUR); + // >0 means skip this chunk + return 1; + } + } + + amt = read(fd, &flattenedHeader.version, + sizeof(chunk_header_v1)-sizeof(flattenedHeader.headerSize)); + if (amt <= 0) { + LOGW("Failed reading chunk header"); + return -1; + } + remainingHeader -= sizeof(chunk_header_v1)-sizeof(flattenedHeader.headerSize); + + if (flattenedHeader.version != VERSION_1_HEADER) { + LOGW("Skipping unknown header version: 0x%08x, %d bytes", flattenedHeader.version, + flattenedHeader.headerSize); + if (remainingHeader > 0) { + lseek(fd, remainingHeader, SEEK_CUR); + // >0 means skip this chunk + return 1; + } + } + +#if 0 + LOGD("chunk header:"); + LOGD(" headerSize=%d", flattenedHeader.headerSize); + LOGD(" version=0x%08x", flattenedHeader.version); + LOGD(" dataSize=%d", flattenedHeader.dataSize); + LOGD(" nameLength=%d", flattenedHeader.nameLength); +#endif + + if (flattenedHeader.dataSize < 0 || flattenedHeader.nameLength < 0 || + remainingHeader < flattenedHeader.nameLength) { + LOGW("Malformed V1 header remainingHeader=%d dataSize=%d nameLength=%d", remainingHeader, + flattenedHeader.dataSize, flattenedHeader.nameLength); + return -1; + } + + buf = keyPrefix.lockBuffer(flattenedHeader.nameLength); + if (buf == NULL) { + LOGW("unable to allocate %d bytes", flattenedHeader.nameLength); + return -1; + } + + amt = read(fd, buf, flattenedHeader.nameLength); + buf[flattenedHeader.nameLength] = 0; + + keyPrefix.unlockBuffer(flattenedHeader.nameLength); + + remainingHeader -= flattenedHeader.nameLength; + + if (remainingHeader > 0) { + lseek(fd, remainingHeader, SEEK_CUR); + } + + env->SetIntField(headerObj, s_chunkSizeField, flattenedHeader.dataSize); + env->SetObjectField(headerObj, s_keyPrefixField, env->NewStringUTF(keyPrefix.string())); + + return 0; +} + +static int +skipChunk_native(JNIEnv* env, jobject clazz, jobject fdObj, jint bytesToSkip) +{ + int fd; + + fd = env->GetIntField(fdObj, s_descriptorField); + + lseek(fd, bytesToSkip, SEEK_CUR); + + return 0; +} + +static int +padding_len(int len) +{ + len = len % 4; + return len == 0 ? len : 4 - len; +} + +static int +allocateHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj) +{ + int pos; + jstring nameObj; + int nameLength; + int namePadding; + int headerSize; + int fd; + + fd = env->GetIntField(fdObj, s_descriptorField); + + nameObj = (jstring)env->GetObjectField(headerObj, s_keyPrefixField); + + nameLength = env->GetStringUTFLength(nameObj); + namePadding = padding_len(nameLength); + + headerSize = sizeof(chunk_header_v1) + nameLength + namePadding; + + pos = lseek(fd, 0, SEEK_CUR); + + lseek(fd, headerSize, SEEK_CUR); + + return pos; +} + +static int +writeHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj, jint pos) +{ + int err; + chunk_header_v1 header; + int fd; + int namePadding; + int prevPos; + jstring nameObj; + const char* buf; + + fd = env->GetIntField(fdObj, s_descriptorField); + prevPos = lseek(fd, 0, SEEK_CUR); + + nameObj = (jstring)env->GetObjectField(headerObj, s_keyPrefixField); + header.nameLength = env->GetStringUTFLength(nameObj); + namePadding = padding_len(header.nameLength); + + header.headerSize = sizeof(chunk_header_v1) + header.nameLength + namePadding; + header.version = VERSION_1_HEADER; + header.dataSize = prevPos - (pos + header.headerSize); + + lseek(fd, pos, SEEK_SET); + err = write(fd, &header, sizeof(chunk_header_v1)); + if (err != sizeof(chunk_header_v1)) { + return errno; + } + + buf = env->GetStringUTFChars(nameObj, NULL); + err = write(fd, buf, header.nameLength); + env->ReleaseStringUTFChars(nameObj, buf); + if (err != header.nameLength) { + return errno; + } + + if (namePadding != 0) { + int zero = 0; + err = write(fd, &zero, namePadding); + if (err != namePadding) { + return errno; + } + } + + lseek(fd, prevPos, SEEK_SET); + return 0; +} + +static const JNINativeMethod g_methods[] = { + { "readHeader_native", + "(Landroid/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;)I", + (void*)readHeader_native }, + { "skipChunk_native", + "(Ljava/io/FileDescriptor;I)I", + (void*)skipChunk_native }, + { "allocateHeader_native", + "(Landroid/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;)I", + (void*)allocateHeader_native }, + { "writeHeader_native", + "(Landroid/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;I)I", + (void*)writeHeader_native }, +}; + +int register_android_backup_BackupHelperDispatcher(JNIEnv* env) +{ + jclass clazz; + + clazz = env->FindClass("java/io/FileDescriptor"); + LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor"); + s_descriptorField = env->GetFieldID(clazz, "descriptor", "I"); + LOG_FATAL_IF(s_descriptorField == NULL, + "Unable to find descriptor field in java.io.FileDescriptor"); + + clazz = env->FindClass("android/backup/BackupHelperDispatcher$Header"); + LOG_FATAL_IF(clazz == NULL, + "Unable to find class android.backup.BackupHelperDispatcher.Header"); + s_chunkSizeField = env->GetFieldID(clazz, "chunkSize", "I"); + LOG_FATAL_IF(s_chunkSizeField == NULL, + "Unable to find chunkSize field in android.backup.BackupHelperDispatcher.Header"); + s_keyPrefixField = env->GetFieldID(clazz, "keyPrefix", "Ljava/lang/String;"); + LOG_FATAL_IF(s_keyPrefixField == NULL, + "Unable to find keyPrefix field in android.backup.BackupHelperDispatcher.Header"); + + return AndroidRuntime::registerNativeMethods(env, "android/backup/BackupHelperDispatcher", + g_methods, NELEM(g_methods)); +} + +} diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index 9b92bc5..8e48b38 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -53,19 +53,33 @@ struct fields_t { static fields_t fields; static Mutex sLock; -struct camera_context_t { +// provides persistent context for calls from native code to Java +class JNICameraContext: public CameraListener +{ +public: + JNICameraContext(JNIEnv* env, jobject weak_this, jclass clazz, const sp<Camera>& camera); + ~JNICameraContext() { release(); } + virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2); + virtual void postData(int32_t msgType, const sp<IMemory>& dataPtr); + sp<Camera> getCamera() { Mutex::Autolock _l(mLock); return mCamera; } + void release(); + +private: + void copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType); + jobject mCameraJObjectWeak; // weak reference to java object jclass mCameraJClass; // strong reference to java class sp<Camera> mCamera; // strong reference to native object + Mutex mLock; }; -sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, camera_context_t** pContext) +sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, JNICameraContext** pContext) { sp<Camera> camera; Mutex::Autolock _l(sLock); - camera_context_t* context = reinterpret_cast<camera_context_t*>(env->GetIntField(thiz, fields.context)); + JNICameraContext* context = reinterpret_cast<JNICameraContext*>(env->GetIntField(thiz, fields.context)); if (context != NULL) { - camera = context->mCamera; + camera = context->getCamera(); } LOGV("get_native_camera: context=%p, camera=%p", context, camera.get()); if (camera == 0) { @@ -76,30 +90,104 @@ sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, camera_context_t** pCont return camera; } -static void err_callback(status_t err, void *cookie) +JNICameraContext::JNICameraContext(JNIEnv* env, jobject weak_this, jclass clazz, const sp<Camera>& camera) { - camera_context_t* context = reinterpret_cast<camera_context_t*>(cookie); - if ((context == NULL) || (context->mCamera == 0)) return; + mCameraJObjectWeak = env->NewGlobalRef(weak_this); + mCameraJClass = (jclass)env->NewGlobalRef(clazz); + mCamera = camera; +} - LOGV("err_callback: context=%p, camera=%p", context, context->mCamera.get()); +void JNICameraContext::release() +{ + LOGV("release"); + Mutex::Autolock _l(mLock); + JNIEnv *env = AndroidRuntime::getJNIEnv(); - int error; - switch (err) { - case DEAD_OBJECT: - error = kCameraErrorMediaServer; - break; - default: - error = kCameraErrorUnknown; - break; + if (mCameraJObjectWeak != NULL) { + env->DeleteGlobalRef(mCameraJObjectWeak); + mCameraJObjectWeak = NULL; } + if (mCameraJClass != NULL) { + env->DeleteGlobalRef(mCameraJClass); + mCameraJClass = NULL; + } + mCamera.clear(); +} - JNIEnv *env = AndroidRuntime::getJNIEnv(); - if (env == NULL) { - LOGE("err_callback on dead VM"); +void JNICameraContext::notify(int32_t msgType, int32_t ext1, int32_t ext2) +{ + LOGV("notify"); + + // VM pointer will be NULL if object is released + Mutex::Autolock _l(mLock); + if (mCameraJObjectWeak == NULL) { + LOGW("callback on dead camera object"); return; } - env->CallStaticVoidMethod(context->mCameraJClass, fields.post_event, - context->mCameraJObjectWeak, kErrorCallback, error, 0, NULL); + JNIEnv *env = AndroidRuntime::getJNIEnv(); + env->CallStaticVoidMethod(mCameraJClass, fields.post_event, + mCameraJObjectWeak, msgType, ext1, ext2); +} + +void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType) +{ + jbyteArray obj = NULL; + + // allocate Java byte array and copy data + if (dataPtr != NULL) { + ssize_t offset; + size_t size; + sp<IMemoryHeap> heap = dataPtr->getMemory(&offset, &size); + LOGV("postData: off=%d, size=%d", offset, size); + uint8_t *heapBase = (uint8_t*)heap->base(); + + if (heapBase != NULL) { + uint8_t *data = heapBase + offset; + obj = env->NewByteArray(size); + if (obj == NULL) { + LOGE("Couldn't allocate byte array for JPEG data"); + env->ExceptionClear(); + } else { + jbyte *bytes = env->GetByteArrayElements(obj, NULL); + memcpy(bytes, data, size); + env->ReleaseByteArrayElements(obj, bytes, 0); + + } + } else { + LOGE("image heap is NULL"); + } + } + + // post image data to Java + env->CallStaticVoidMethod(mCameraJClass, fields.post_event, + mCameraJObjectWeak, msgType, 0, 0, obj); + if (obj) { + env->DeleteLocalRef(obj); + } +} + +void JNICameraContext::postData(int32_t msgType, const sp<IMemory>& dataPtr) +{ + // VM pointer will be NULL if object is released + Mutex::Autolock _l(mLock); + JNIEnv *env = AndroidRuntime::getJNIEnv(); + + // return data based on callback type + switch(msgType) { + case CAMERA_MSG_VIDEO_FRAME: + // should never happen + break; + // don't return raw data to Java + case CAMERA_MSG_RAW_IMAGE: + LOGV("rawCallback"); + env->CallStaticVoidMethod(mCameraJClass, fields.post_event, + mCameraJObjectWeak, msgType, 0, 0, NULL); + break; + default: + LOGV("dataCallback(%d, %p)", msgType, dataPtr.get()); + copyAndPost(env, dataPtr, msgType); + break; + } } // connect to camera service @@ -127,19 +215,12 @@ static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobj // We use a weak reference so the Camera object can be garbage collected. // The reference is only used as a proxy for callbacks. - camera_context_t* context = new camera_context_t; - context->mCameraJObjectWeak = env->NewGlobalRef(weak_this); - context->mCameraJClass = (jclass)env->NewGlobalRef(clazz); - context->mCamera = camera; + sp<JNICameraContext> context = new JNICameraContext(env, weak_this, clazz, camera); + context->incStrong(thiz); + camera->setListener(context); // save context in opaque field - env->SetIntField(thiz, fields.context, (int)context); - - LOGV("native_setup: mCameraJObjectWeak=%x, camera_obj=%x, context=%p", - (int)context->mCameraJObjectWeak, (int)thiz, context); - - // set error callback - camera->setErrorCallback(err_callback, context); + env->SetIntField(thiz, fields.context, (int)context.get()); } // disconnect from camera service @@ -148,11 +229,11 @@ static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobj // finalizer is invoked later. static void android_hardware_Camera_release(JNIEnv *env, jobject thiz) { - camera_context_t* context = NULL; + JNICameraContext* context = NULL; sp<Camera> camera; { Mutex::Autolock _l(sLock); - context = reinterpret_cast<camera_context_t*>(env->GetIntField(thiz, fields.context)); + context = reinterpret_cast<JNICameraContext*>(env->GetIntField(thiz, fields.context)); // Make sure we do not attempt to callback on a deleted Java object. env->SetIntField(thiz, fields.context, 0); @@ -160,21 +241,18 @@ static void android_hardware_Camera_release(JNIEnv *env, jobject thiz) // clean up if release has not been called before if (context != NULL) { - camera = context->mCamera; - context->mCamera.clear(); + camera = context->getCamera(); + context->release(); LOGV("native_release: context=%p camera=%p", context, camera.get()); // clear callbacks if (camera != NULL) { - camera->setPreviewCallback(NULL, NULL, FRAME_CALLBACK_FLAG_NOOP); - camera->setErrorCallback(NULL, NULL); + camera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP); camera->disconnect(); - env->DeleteGlobalRef(context->mCameraJObjectWeak); - env->DeleteGlobalRef(context->mCameraJClass); } // remove context to prevent further Java access - delete context; + context->decStrong(thiz); } } @@ -190,48 +268,6 @@ static void android_hardware_Camera_setPreviewDisplay(JNIEnv *env, jobject thiz, } } -static void preview_callback(const sp<IMemory>& mem, void *cookie) -{ - LOGV("preview_callback"); - JNIEnv *env = AndroidRuntime::getJNIEnv(); - if (env == NULL) { - LOGE("preview_callback on dead VM"); - return; - } - camera_context_t* context = reinterpret_cast<camera_context_t*>(cookie); - if ((context == NULL) || (context->mCamera == 0)) { - LOGW("context or camera is NULL in preview_callback"); - return; - } - LOGV("native_release: context=%p camera=%p", context, context->mCamera.get()); - - int arg1 = 0, arg2 = 0; - jobject obj = NULL; - - ssize_t offset; - size_t size; - sp<IMemoryHeap> heap = mem->getMemory(&offset, &size); - - uint8_t *data = ((uint8_t *)heap->base()) + offset; - - jbyteArray array = env->NewByteArray(size); - if (array == NULL) { - LOGE("Couldn't allocate byte array for YUV data"); - env->ExceptionClear(); - return; - } - - jbyte *bytes = env->GetByteArrayElements(array, NULL); - memcpy(bytes, data, size); - env->ReleaseByteArrayElements(array, bytes, 0); - - obj = array; - - env->CallStaticVoidMethod(context->mCameraJClass, fields.post_event, - context->mCameraJObjectWeak, kPreviewCallback, arg1, arg2, obj); - env->DeleteLocalRef(array); -} - static void android_hardware_Camera_startPreview(JNIEnv *env, jobject thiz) { LOGV("startPreview"); @@ -267,7 +303,7 @@ static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject t // Important: Only install preview_callback if the Java code has called // setPreviewCallback() with a non-null value, otherwise we'd pay to memcpy // each preview frame for nothing. - camera_context_t* context; + JNICameraContext* context; sp<Camera> camera = get_native_camera(env, thiz, &context); if (camera == 0) return; @@ -277,130 +313,32 @@ static void android_hardware_Camera_setHasPreviewCallback(JNIEnv *env, jobject t } else { callback_flag = FRAME_CALLBACK_FLAG_NOOP; } - camera->setPreviewCallback(installed ? preview_callback : NULL, context, callback_flag); -} - -static void autofocus_callback_impl(bool success, void *cookie) -{ - LOGV("autoFocusCallback"); - camera_context_t* context = reinterpret_cast<camera_context_t*>(cookie); - - JNIEnv *env = AndroidRuntime::getJNIEnv(); - if (env == NULL) { - LOGE("autofocus_callback on dead VM"); - return; - } - env->CallStaticVoidMethod(context->mCameraJClass, fields.post_event, - context->mCameraJObjectWeak, kAutoFocusCallback, success, 0, NULL); + camera->setPreviewCallbackFlags(callback_flag); } static void android_hardware_Camera_autoFocus(JNIEnv *env, jobject thiz) { LOGV("autoFocus"); - camera_context_t* context; + JNICameraContext* context; sp<Camera> c = get_native_camera(env, thiz, &context); if (c == 0) return; - c->setAutoFocusCallback(autofocus_callback_impl, context); if (c->autoFocus() != NO_ERROR) { jniThrowException(env, "java/lang/RuntimeException", "autoFocus failed"); } } -static void jpeg_callback(const sp<IMemory>& mem, void *cookie) -{ - LOGV("jpegCallback"); - camera_context_t* context = reinterpret_cast<camera_context_t*>(cookie); - - JNIEnv *env = AndroidRuntime::getJNIEnv(); - if (env == NULL) { - LOGE("jpeg`_callback on dead VM"); - return; - } - int arg1 = 0, arg2 = 0; - jobject obj = NULL; - - if (mem == NULL) { - env->CallStaticVoidMethod(context->mCameraJClass, fields.post_event, - context->mCameraJObjectWeak, kJpegCallback, arg1, arg2, NULL); - return; - } - ssize_t offset; - size_t size; - sp<IMemoryHeap> heap = mem->getMemory(&offset, &size); - LOGV("jpeg_callback: mem off=%d, size=%d", offset, size); - - uint8_t *heap_base = (uint8_t *)heap->base(); - if (heap_base == NULL) { - LOGE("YUV heap is NULL"); - return; - } - - uint8_t *data = heap_base + offset; - - jbyteArray array = env->NewByteArray(size); - if (array == NULL) { - LOGE("Couldn't allocate byte array for JPEG data"); - env->ExceptionClear(); - return; - } - - jbyte *bytes = env->GetByteArrayElements(array, NULL); - memcpy(bytes, data, size); - env->ReleaseByteArrayElements(array, bytes, 0); - - obj = array; - - env->CallStaticVoidMethod(context->mCameraJClass, fields.post_event, - context->mCameraJObjectWeak, kJpegCallback, arg1, arg2, obj); - env->DeleteLocalRef(array); -} - -static void shutter_callback_impl(void *cookie) -{ - LOGV("shutterCallback"); - camera_context_t* context = reinterpret_cast<camera_context_t*>(cookie); - - JNIEnv *env = AndroidRuntime::getJNIEnv(); - if (env == NULL) { - LOGE("shutter_callback on dead VM"); - return; - } - env->CallStaticVoidMethod(context->mCameraJClass, fields.post_event, - context->mCameraJObjectWeak, kShutterCallback, 0, 0, NULL); -} - -static void raw_callback(const sp<IMemory>& mem __attribute__((unused)), - void *cookie) -{ - LOGV("rawCallback"); - camera_context_t* context = reinterpret_cast<camera_context_t*>(cookie); - - JNIEnv *env = AndroidRuntime::getJNIEnv(); - if (env == NULL) { - LOGE("raw_callback on dead VM"); - return; - } - env->CallStaticVoidMethod(context->mCameraJClass, fields.post_event, - context->mCameraJObjectWeak, kRawCallback, 0, 0, NULL); -} - static void android_hardware_Camera_takePicture(JNIEnv *env, jobject thiz) { LOGV("takePicture"); - camera_context_t* context; + JNICameraContext* context; sp<Camera> camera = get_native_camera(env, thiz, &context); if (camera == 0) return; - camera->setShutterCallback(shutter_callback_impl, context); - camera->setRawCallback(raw_callback, context); - camera->setJpegCallback(jpeg_callback, context); if (camera->takePicture() != NO_ERROR) { jniThrowException(env, "java/lang/RuntimeException", "takePicture failed"); return; } - - return; } static void android_hardware_Camera_setParameters(JNIEnv *env, jobject thiz, jstring params) diff --git a/core/jni/android_text_format_Time.cpp b/core/jni/android_text_format_Time.cpp index 7c208e9..98f4e03 100644 --- a/core/jni/android_text_format_Time.cpp +++ b/core/jni/android_text_format_Time.cpp @@ -23,7 +23,7 @@ #include "jni.h" #include "utils/misc.h" #include "android_runtime/AndroidRuntime.h" -#include <utils/TimeUtils.h> +#include "TimeUtils.h" #include <nativehelper/JNIHelp.h> #include <cutils/tztime.h> diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index d147bcc..2d90ba4 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -535,7 +535,7 @@ static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject c jint keyboard, jint keyboardHidden, jint navigation, jint screenWidth, jint screenHeight, - jint sdkVersion) + jint screenLayout, jint sdkVersion) { AssetManager* am = assetManagerForJavaObject(env, clazz); if (am == NULL) { @@ -557,6 +557,7 @@ static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject c config.navigation = (uint8_t)navigation; config.screenWidth = (uint16_t)screenWidth; config.screenHeight = (uint16_t)screenHeight; + config.screenLayout = (uint8_t)screenLayout; config.sdkVersion = (uint16_t)sdkVersion; config.minorVersion = 0; am->setConfiguration(config, locale8); @@ -1567,7 +1568,7 @@ static JNINativeMethod gAssetManagerMethods[] = { (void*) android_content_AssetManager_setLocale }, { "getLocales", "()[Ljava/lang/String;", (void*) android_content_AssetManager_getLocales }, - { "setConfiguration", "(IILjava/lang/String;IIIIIIIII)V", + { "setConfiguration", "(IILjava/lang/String;IIIIIIIIII)V", (void*) android_content_AssetManager_setConfiguration }, { "getResourceIdentifier","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", (void*) android_content_AssetManager_getResourceIdentifier }, diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp index 945a325..aee0ed7 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -269,9 +269,9 @@ void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jin void android_os_Process_setThreadPriority(JNIEnv* env, jobject clazz, jint pid, jint pri) { - if (pri == ANDROID_PRIORITY_BACKGROUND) { + if (pri >= ANDROID_PRIORITY_BACKGROUND) { add_pid_to_cgroup(pid, ANDROID_TGROUP_BG_NONINTERACT); - } else if (getpriority(PRIO_PROCESS, pid) == ANDROID_PRIORITY_BACKGROUND) { + } else if (getpriority(PRIO_PROCESS, pid) >= ANDROID_PRIORITY_BACKGROUND) { add_pid_to_cgroup(pid, ANDROID_TGROUP_DEFAULT); } @@ -496,7 +496,7 @@ void android_os_Process_readProcLines(JNIEnv* env, jobject clazz, jstring fileSt const String8& field = fields[i]; if (strncmp(p, field.string(), field.length()) == 0) { p += field.length(); - while (*p == ' ') p++; + while (*p == ' ' || *p == '\t') p++; char* num = p; while (*p >= '0' && *p <= '9') p++; skipToEol = *p != '\n'; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index df6151d..7f24dcc 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1000,6 +1000,7 @@ android:hasCode="false" android:label="@string/android_system_label" android:allowClearUserData="false" + android:backupAgent="com.android.internal.backup.SystemBackupAgent" android:icon="@drawable/ic_launcher_android"> <activity android:name="com.android.internal.app.ChooserActivity" android:theme="@style/Theme.Dialog.Alert" diff --git a/core/res/res/drawable/search_dropdown_background.9.png b/core/res/res/drawable/search_dropdown_background.9.png Binary files differindex a6923b7..804260a 100755..100644 --- a/core/res/res/drawable/search_dropdown_background.9.png +++ b/core/res/res/drawable/search_dropdown_background.9.png diff --git a/core/res/res/drawable/search_dropdown_background_apps.9.png b/core/res/res/drawable/search_dropdown_background_apps.9.png Binary files differdeleted file mode 100644 index 804260a..0000000 --- a/core/res/res/drawable/search_dropdown_background_apps.9.png +++ /dev/null diff --git a/core/res/res/layout/search_bar.xml b/core/res/res/layout/search_bar.xml index 54ab6de..13e66aa 100644 --- a/core/res/res/layout/search_bar.xml +++ b/core/res/res/layout/search_bar.xml @@ -76,6 +76,7 @@ android:ellipsize="end" android:inputType="text|textAutoComplete" android:dropDownWidth="fill_parent" + android:dropDownHeight="fill_parent" android:dropDownAnchor="@id/search_plate" android:dropDownVerticalOffset="-9dip" android:popupBackground="@android:drawable/search_dropdown_background" diff --git a/core/res/res/values-de-rAT/donottranslate-cldr.xml b/core/res/res/values-de-rAT/donottranslate-cldr.xml index cd7cba8..27624a3 100644 --- a/core/res/res/values-de-rAT/donottranslate-cldr.xml +++ b/core/res/res/values-de-rAT/donottranslate-cldr.xml @@ -2,10 +2,43 @@ <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">Jänner</string> + <string name="month_long_standalone_february">Februar</string> + <string name="month_long_standalone_march">März</string> + <string name="month_long_standalone_april">April</string> + <string name="month_long_standalone_may">Mai</string> + <string name="month_long_standalone_june">Juni</string> + <string name="month_long_standalone_july">Juli</string> + <string name="month_long_standalone_august">August</string> + <string name="month_long_standalone_september">September</string> + <string name="month_long_standalone_october">Oktober</string> + <string name="month_long_standalone_november">November</string> + <string name="month_long_standalone_december">Dezember</string> <string name="month_long_january">Jänner</string> + <string name="month_long_february">Februar</string> + <string name="month_long_march">März</string> + <string name="month_long_april">April</string> + <string name="month_long_may">Mai</string> + <string name="month_long_june">Juni</string> + <string name="month_long_july">Juli</string> + <string name="month_long_august">August</string> + <string name="month_long_september">September</string> + <string name="month_long_october">Oktober</string> + <string name="month_long_november">November</string> + <string name="month_long_december">Dezember</string> <string name="month_medium_january">Jän</string> + <string name="month_medium_february">Feb</string> + <string name="month_medium_march">Mär</string> + <string name="month_medium_april">Apr</string> + <string name="month_medium_may">Mai</string> + <string name="month_medium_june">Jun</string> + <string name="month_medium_july">Jul</string> + <string name="month_medium_august">Aug</string> + <string name="month_medium_september">Sep</string> + <string name="month_medium_october">Okt</string> + <string name="month_medium_november">Nov</string> + <string name="month_medium_december">Dez</string> <string name="month_shortest_january">J</string> <string name="month_shortest_february">F</string> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 23ab573..e978ef5 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -1998,6 +1998,16 @@ <!-- The dropdown should fit the width of its anchor. --> <enum name="wrap_content" value="-2" /> </attr> + <!-- Specifies the basic width of the dropdown. Its value may + be a dimension (such as "12dip") for a constant width, fill_parent + to fill the width of the screen, or wrap_content to match the height of + the content of the drop down. --> + <attr name="dropDownHeight" format="dimension"> + <!-- The dropdown should fill the width of the screen. --> + <enum name="fill_parent" value="-1" /> + <!-- The dropdown should fit the width of its anchor. --> + <enum name="wrap_content" value="-2" /> + </attr> <attr name="inputType" /> </declare-styleable> <declare-styleable name="PopupWindow"> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 91cd9fd..7571e24 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -512,6 +512,9 @@ <!-- The screen orientation has changed, that is the user has rotated the device. --> <flag name="orientation" value="0x0080" /> + <!-- The screen orientation has changed, that is the user has + rotated the device. --> + <flag name="screenLayout" value="0x0100" /> <!-- The font scaling factor has changed, that is the user has selected a new global font size. --> <flag name="fontScale" value="0x40000000" /> @@ -829,8 +832,59 @@ <p>This appears as a child tag of the {@link #AndroidManifest manifest} tag. --> <declare-styleable name="AndroidManifestSupportsDensity" parent="AndroidManifest"> - <!-- Required value of the density in dip (device independent pixel). --> - <attr name="density" format="integer" /> + <!-- Required value of the density in dip (device independent pixel). + You should use one of the pre-defined constants for the standard + screen densities defined here. + --> + <attr name="density" format="integer"> + <!-- A low density screen, such as a QVGA or WQVGA screen in a + typical hand-held phone. The constant for this is 120. --> + <enum name="low" value="120" /> + <!-- A medium density screen, such as an HVGA screen in a + typical hand-held phone. The constant for this is 160. --> + <enum name="medium" value="160" /> + <!-- A high density screen, such as a VGA or WVGA screen in a + typical hand-held phone. The constant for this is 240. --> + <enum name="high" value="240" /> + </attr> + </declare-styleable> + + <!-- The <code>supports-screens</code> specifies the screen dimensions an + application supports. By default a modern application supports all + screen sizes and must explicitly disable certain screen sizes here; + older applications are assumed to only support the traditional normal + (HVGA) screen size. Note that screen size is a separate axis from + density, and is determined as the available pixels to an application + after density scaling has been applied. + + <p>This appears as a child tag of the + {@link #AndroidManifest manifest} tag. --> + <declare-styleable name="AndroidManifestSupportsScreens" parent="AndroidManifest"> + <!-- Indicates whether the application supports smaller screen form-factors. + A small screen is defined as one with a smaller aspect ratio than + the traditional HVGA screen; that is, for a portrait screen, less + tall than an HVGA screen. In practice, this means a QVGA low + density or VGA high density screen. An application that does + not support small screens <em>will not be available</em> for + small screen devices, since there is little the platform can do + to make such an application work on a smaller screen. --> + <attr name="smallScreens" format="boolean" /> + <!-- Indicates whether an application supports the normal screen + form-factors. Traditionally this is an HVGA normal density + screen, but WQVGA low density and WVGA high density are also + considered to be normal. This attribute is true by default, + and applications currently should leave it that way. --> + <attr name="normalScreens" format="boolean" /> + <!-- Indicates whether the application supports larger screen form-factors. + A large screen is defined as a screen that is significantly larger + than a normal phone screen, and thus may require some special care + on the application's part to make good use of it. An example would + be a VGA <em>normal density</em> screen, though even larger screens + are certainly possible. An application that does not support + large screens will be placed as a postage stamp on such a + screen, so that it retains the dimensions it was originally + designed for. --> + <attr name="largeScreens" format="boolean" /> </declare-styleable> <!-- The <code>expandable</code> specifies if this package supports screen metrics diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index e5a58b1..14d8dbe 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1116,7 +1116,11 @@ <public type="attr" name="allowBackup" /> <public type="attr" name="glEsVersion" /> <public type="attr" name="queryAfterZeroResults" /> - + <public type="attr" name="dropDownHeight" /> + <public type="attr" name="smallScreens" /> + <public type="attr" name="normalScreens" /> + <public type="attr" name="largeScreens" /> + <public-padding type="attr" name="donut_resource_pad" end="0x0101029f" /> <public-padding type="id" name="donut_resource_pad" end="0x01020040" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index f742ab7..9f1ed40 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1804,15 +1804,15 @@ <!-- External media notification strings --> <!-- Shown when external media is being checked --> <string name="ext_media_checking_notification_title">Preparing SD card</string> - <string name="ext_media_checking_notification_message">Checking for errors</string> + <string name="ext_media_checking_notification_message">Checking for errors.</string> <!-- Shown when external media is blank (or unsupported filesystem) --> <string name="ext_media_nofs_notification_title">Blank SD card</string> - <string name="ext_media_nofs_notification_message">The SD card is blank or using an unsupported filesystem.</string> + <string name="ext_media_nofs_notification_message">SD card blank or has unsupported filesystem.</string> <!-- Shown when external media is unmountable (corrupt)) --> <string name="ext_media_unmountable_notification_title">Damaged SD card</string> - <string name="ext_media_unmountable_notification_message">The SD card is damaged. You may have to reformat your card.</string> + <string name="ext_media_unmountable_notification_message">SD card damaged. You may have to reformat it.</string> <!-- Shown when external media is unsafely removed --> <string name="ext_media_badremoval_notification_title">SD card unexpectedly removed</string> @@ -1820,11 +1820,11 @@ <!-- Shown when external media has been safely removed --> <string name="ext_media_safe_unmount_notification_title">SD card safe to remove</string> - <string name="ext_media_safe_unmount_notification_message">The SD card can now be safely removed.</string> + <string name="ext_media_safe_unmount_notification_message">You can safely remove SD card.</string> <!-- Shown when external media is missing --> <string name="ext_media_nomedia_notification_title">Removed SD card</string> - <string name="ext_media_nomedia_notification_message">The SD has been removed. Insert a new SD card to increase your device storage.</string> + <string name="ext_media_nomedia_notification_message">SD card removed. Insert a new one.</string> <!-- Shown in LauncherActivity when the requested target Intent didn't return any matching Activities, leaving the list empty. --> <string name="activity_list_empty">No matching activities found</string> diff --git a/docs/html/guide/topics/resources/res-selection-flowchart.png b/docs/html/guide/topics/resources/res-selection-flowchart.png Binary files differnew file mode 100755 index 0000000..d738b3f --- /dev/null +++ b/docs/html/guide/topics/resources/res-selection-flowchart.png diff --git a/docs/html/guide/topics/resources/res-selection-flowchart.vsd b/docs/html/guide/topics/resources/res-selection-flowchart.vsd Binary files differnew file mode 100755 index 0000000..65bfcdb --- /dev/null +++ b/docs/html/guide/topics/resources/res-selection-flowchart.vsd diff --git a/docs/html/guide/topics/resources/resources-i18n.jd b/docs/html/guide/topics/resources/resources-i18n.jd index 4bbb44a..6900704 100644..100755 --- a/docs/html/guide/topics/resources/resources-i18n.jd +++ b/docs/html/guide/topics/resources/resources-i18n.jd @@ -1,713 +1,716 @@ -page.title=Resources and Internationalization -parent.title=Resources and Assets -parent.link=index.html -@jd:body - -<div id="qv-wrapper"> -<div id="qv"> - - <h2>Key classes</h2> - <ol> - <li>{@link android.content.res.Resources}</li> - </ol> - - <h2>In this document</h2> - <ol> - <li><a href="#intro">Introduction</a></li> - <li><a href="#CreatingResources">Creating Resources</a></li> - <li><a href="#UsingResources">Using Resources</a> - <ol> - <li><a href="#ResourcesInCode">Using Resources in Code</a></li> - <li><a href="#ReferencesToResources">References to Resources</a></li> - <li><a href="#ReferencesToThemeAttributes">References to Theme Attributes</a></li> - <li><a href="#UsingSystemResources">Using System Resources</a></li> - </ol> - </li> - <li><a href="#AlternateResources">Alternate Resources</a></li> - <li><a href="#ResourcesTerminology">Terminology</a></li> - <li><a href="#i18n">Internationalization (I18N)</a></li> - </ol> -</div> -</div> - -<p>Resources are external files (that is, non-code files) that are used by -your code and compiled into your application at build time. Android -supports a number of different kinds of resource files, including XML, -PNG, and JPEG files. The XML files have very different formats depending -on what they describe. This document describes what kinds of files are -supported, and the syntax or format of each.</p> -<p>Resources are externalized from source code, and XML files are compiled into -a binary, fast loading format for efficiency reasons. Strings, likewise are compressed -into a more efficient storage form. It is for these reasons that we have these -different resource types in the Android platform.</p> - -<p>This is a fairly technically dense document, and together with the -<a href="available-resources.html">Available Resources</a> -document, they cover a lot of information about resources. It is not necessary -to know this document by heart to use Android, but rather to know that the -information is here when you need it.</p> - -<a name="intro"></a> -<h2>Introduction</h2> - -<p>This topic includes a terminology list associated with resources, and a series - of examples of using resources in code. For a complete guide to the supported - Android resource types, see - <a href="available-resources.html">Available Resources</a>. - </p> -<p>The Android resource system keeps track of all non-code - assets associated with an application. You use the - {@link android.content.res.Resources Resources} class to access your - application's resources; the Resources instance associated with your - application can generally be found through - {@link android.content.Context#getResources Context.getResources()}.</p> -<p>An application's resources are compiled into the application -binary at build time for you by the build system. To use a resource, -you must install it correctly in the source tree and build your -application. As part of the build process, symbols for each -of the resources are generated that you can use in your source -code -- this allows the compiler to verify that your application code matches -up with the resources you defined.</p> - -<p>The rest of this section is organized as a tutorial on how to -use resources in an application.</p> - -<a name="CreatingResources" id="CreatingResources"></a> -<h2>Creating Resources</h2> - -<p>Android supports string, bitmap, and many other types of resource. The syntax and format -of each, and where they're stored, depends upon the type of object. In -general, though, you create resources from three types of files: XML files -(everything but bitmaps and raw), bitmap files(for images) and Raw files (anything -else, for example sound files, etc.). In fact, there are two different types of -XML file as well, those that get compiled as-is into the package, and those that -are used to generate resources by aapt. Here is a list of each -resource type, the format of the file, a description of the file, and details -of any XML files. </p> - -<p>You will create and store your resource files under the appropriate -subdirectory under the <code>res/</code> directory in your project. Android -has a resource compiler (aapt) that compiles resources according to which -subfolder they are in, and the format of the file. Here is a list of the file -types for each resource. See the -<a href="available-resources.html">Available Resources</a> for -descriptions of each type of object, the syntax, and the format or syntax of -the containing file.</p> - -<table width="100%" border="1"> - <tr> - <th scope="col">Directory</th> - <th scope="col">Resource Types </th> - </tr> - <tr> - <td><code>res/anim/</code></td> - <td>XML files that are compiled into - <a href="available-resources.html#animationdrawable">frame by - frame animation</a> or - <a href="available-resources.html#tweenedanimation">tweened - animation</a> objects </td> - </tr> - <tr> - <td><code>res/drawable/</code></td> - <td><p>.png, .9.png, .jpg files that are compiled into the following - Drawable resource subtypes:</p> - <ul class="nolist"> - <li><a href="available-resources.html#imagefileresources">bitmap files</a></li> - <li><a href="available-resources.html#ninepatch">9-patches (resizable bitmaps)</a></li> - </ul> - <p>To get a resource of this type, use <code>mContext.getResources().getDrawable(R.drawable.<em>imageId</em>)</code></p> - <p class="note"><strong>Note:</strong> Image resources placed in here may - be automatically optimized with lossless image compression by the - <a href="{@docRoot}guide/developing/tools/aapt.html">aapt</a> tool. For example, a true-color PNG - that does not require more than 256 colors may be converted to an 8-bit PNG with a color palette. - This will result in an image of equal quality but which requires less memory. So be aware that the - image binaries placed in this directory can change during the build. If you plan on reading - an image as a bit stream in order to convert it to a bitmap, put your images in the - <code>res/raw/</code> folder instead, where they will not be optimized.</p> - </td> - </tr> - <tr> - <td><code>res/layout/</code></td> - <td>XML files that are compiled into screen layouts (or part of a screen). - See <a href="{@docRoot}guide/topics/ui/declaring-layout.html">Declaring Layout</a>.</td> - </tr> - <tr> - <td><code>res/values/</code></td> - <td><p>XML files that can be compiled into many kinds of resource.</p> - <p class="note"><strong>Note:</strong> Unlike the other res/ folders, this one - can hold any number of files that hold descriptions of resources to create - rather than the resources themselves. The XML element types control - where these resources are placed under the R class.</p> - <p>While the files can be named anything, these are - the typical files in this folder (the convention is to name - the file after the type of elements defined within):</p> - <ul> - <li><strong>arrays.xml</strong> to define arrays </li> - <!-- TODO: add section on arrays --> - <li><strong>colors.xml</strong> to define <a href="available-resources.html#colordrawableresources">color - drawables</a> and <a href="#colorvals">color string values</a>. - Use <code>Resources.getDrawable()</code> and - <code>Resources.getColor(), respectively,</code> - to get these resources.</li> - <li><strong>dimens.xml</strong> to define <a href="available-resources.html#dimension">dimension value</a>. Use <code>Resources.getDimension()</code> to get - these resources.</li> - <li><strong>strings.xml</strong> to define <a href="available-resources.html#stringresources">string</a> values (use either - <code>Resources.getString</code> or preferably <code>Resources.getText()</code> - to get - these resources. <code>getText()</code> will retain any rich text styling - which is usually desirable for UI strings.</li> - <li><strong>styles.xml</strong> to define <a href="available-resources.html#stylesandthemes">style</a> objects.</li> - </ul></td> - </tr> - <tr> - <td><code>res/xml/</code></td> - <td>Arbitrary XML files that are compiled and can be read at run time by - calling {@link android.content.res.Resources#getXml(int) Resources.getXML()}.</td> - </tr> - <tr> - <td><code>res/raw/</code></td> - <td>Arbitrary files to copy directly to the device. They are added uncompiled - to the compressed file that your application build produces. To use these - resources in your application, call {@link android.content.res.Resources#openRawResource(int) - Resources.openRawResource()} with the resource ID, which is R.raw.<em>somefilename</em>.</td> - </tr> -</table> -<p>Resources are compiled into the final APK file. Android creates a wrapper class, - called R, that you can use to refer to these resources in your code. R contains subclasses - named according to the path and file name of the source file</p> -<a name="colorvals" id="colorvals"></a> -<h3>Global Resource Notes</h3> -<ul> - <li>Several resources allow you to define colors. Android accepts color values - written in various web-style formats -- a hexadecimal constant in any of the - following forms: #RGB, #ARGB, #RRGGBB, #AARRGGBB. </li> - <li>All color values support setting an alpha channel value, where the first - two hexadecimal numbers specify the transparency. Zero in the alpha channel - means transparent. The default value is opaque. </li> -</ul> -<a name="UsingResources" id="UsingResources"></a> -<h2>Using Resources </h2> -<p>This section describes how to use the resources you've created. It includes the - following topics:</p> -<ul> - <li><a href="#ResourcesInCode">Using resources in code</a> - How to call - resources in your code to instantiate them. </li> - <li><a href="#ReferencesToResources">Referring to resources from other resources</a> - - You can reference resources from other resources. This lets you reuse common - resource values inside resources. </li> - <li><a href="#AlternateResources">Supporting Alternate Resources for Alternate - Configurations</a> - You can specify different resources - to load, depending on the language or display configuration of the host - hardware. </li> -</ul> -<p>At compile time, Android generates a class named R that contains resource identifiers - to all the resources in your program. This class contains several subclasses, - one for each type of resource supported by Android, and for which you provided - a resource file. Each class contains one or more identifiers for the compiled resources, - that you use in your code to load the resource. Here is a small resource file - that contains string, layout (screens or parts of screens), and image resources.</p> -<p class="note"><strong>Note:</strong> the R class is an auto-generated file and is not -designed to be edited by hand. It will be automatically re-created as needed when -the resources are updated.</p> -<pre class="prettyprint">package com.android.samples; -public final class R { - public static final class string { - public static final int greeting=0x0204000e; - public static final int start_button_text=0x02040001; - public static final int submit_button_text=0x02040008; - public static final int main_screen_title=0x0204000a; - }; - public static final class layout { - public static final int start_screen=0x02070000; - public static final int new_user_pane=0x02070001; - public static final int select_user_list=0x02070002; - - }; - public static final class drawable { - public static final int company_logo=0x02020005; - public static final int smiling_cat=0x02020006; - public static final int yellow_fade_background=0x02020007; - public static final int stretch_button_1=0x02020008; - - }; -}; -</pre> -<a name="ResourcesInCode" id="ResourcesInCode"></a> -<h3>Using Resources in Code </h3> - -<p>Using resources in code is just a matter of knowing the full resource ID -and what type of object your resource has been compiled into. Here is the -syntax for referring to a resource:</p> -<p><code>R.<em>resource_type</em>.<em>resource_name</em></code></p> -<p>or</p> -<p><code>android.R.<em>resource_type</em>.<em>resource_name</em></code></p> - -<p>Where <code>resource_type</code> is the R subclass that holds a specific type -of resource. <code>resource_name</code> is the <em>name</em> attribute for resources -defined in XML files, or the file name (without the extension) for resources -defined by other file types. Each type of resource will be added to a specific -R subclass, depending on the type of resource it is; to learn which R subclass -hosts your compiled resource type, consult the -<a href="available-resources.html">Available Resources</a> document. Resources compiled by your own application can -be referred to without a package name (simply as -<code>R.<em>resource_type</em>.<em>resource_name</em></code>). Android contains -a number of standard resources, such as screen styles and button backgrounds. To -refer to these in code, you must qualify them with <code>android</code>, as in -<code>android.R.drawable.button_background</code>.</p> - -<p>Here are some good and bad examples of using compiled resources in code:</p> - -<pre class="prettyprint">// Load a background for the current screen from a drawable resource. -this.getWindow().setBackgroundDrawableResource(R.drawable.my_background_image); - -// WRONG Sending a string resource reference into a -// method that expects a string. -this.getWindow().setTitle(R.string.main_title); - -// RIGHT Need to get the title from the Resources wrapper. -this.getWindow().setTitle(Resources.getText(R.string.main_title)); - -// Load a custom layout for the current screen. -setContentView(R.layout.main_screen); - -// Set a slide in animation for a ViewFlipper object. -mFlipper.setInAnimation(AnimationUtils.loadAnimation(this, - R.anim.hyperspace_in)); - -// Set the text on a TextView object. -TextView msgTextView = (TextView)findViewByID(R.id.msg); -msgTextView.setText(R.string.hello_message); </pre> - -<a name="ReferencesToResources" id="ReferencesToResources"></a> -<h3>References to Resources</h3> - -<p>A value supplied in an attribute (or resource) can also be a reference to -a resource. This is often used in layout files to supply strings (so they -can be localized) and images (which exist in another file), though a reference -can be any resource type including colors and integers.</p> - -<p>For example, if we have -<a href="available-resources.html#colordrawableresources">color -resources</a>, we can write a layout file that sets the text color size to be -the value contained in one of those resources:</p> - -<pre> -<?xml version="1.0" encoding="utf-8"?> -<EditText id="text" - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" android:layout_height="fill_parent" - <strong>android:textColor="@color/opaque_red"</strong> - android:text="Hello, World!" /> -</pre> - -<p>Note here the use of the '@' prefix to introduce a resource reference -- the -text following that is the name of a resource in the form -of <code>@[package:]type/name</code>. In this case we didn't need to specify -the package because we are referencing a resource in our own package. To -reference a system resource, you would need to write:</p> - -<pre> -<?xml version="1.0" encoding="utf-8"?> -<EditText id="text" - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" android:layout_height="fill_parent" - android:textColor="@<strong>android:</strong>color/opaque_red" - android:text="Hello, World!" /> -</pre> - -<p>As another example, you should always use resource references when supplying -strings in a layout file so that they can be localized:</p> - -<pre> -<?xml version="1.0" encoding="utf-8"?> -<EditText id="text" - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" android:layout_height="fill_parent" - android:textColor="@android:color/opaque_red" - android:text="@string/hello_world" /> -</pre> - -<p>This facility can also be used to create references between resources. -For example, we can create new drawable resources that are aliases for -existing images:</p> - -<pre> -<?xml version="1.0" encoding="utf-8"?> -<resources> - <drawable id="my_background">@android:drawable/theme2_background</drawable> -</resources> -</pre> - -<a name="ReferencesToThemeAttributes"></a> -<h3>References to Theme Attributes</h3> - -<p>Another kind of resource value allows you to reference the value of an -attribute in the current theme. This attribute reference can <em>only</em> -be used in style resources and XML attributes; it allows you to customize the -look of UI elements by changing them to standard variations supplied by the -current theme, instead of supplying more concrete values.</p> - -<p>As an example, we can use this in our layout to set the text color to -one of the standard colors defined in the base system theme:</p> - -<pre> -<?xml version="1.0" encoding="utf-8"?> -<EditText id="text" - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" android:layout_height="fill_parent" - <strong>android:textColor="?android:textDisabledColor"</strong> - android:text="@string/hello_world" /> -</pre> - -<p>Note that this is very similar to a resource reference, except we are using -an '?' prefix instead of '@'. When you use this markup, you are supplying -the name of an attribute resource that will be looked up in the theme -- -because the resource tool knows that an attribute resource is expected, -you do not need to explicitly state the type (which would be -<code>?android:attr/android:textDisabledColor</code>).</p> - -<p>Other than using this resource identifier to find the value in the -theme instead of raw resources, the name syntax is identical to the '@' format: -<code>?[namespace:]type/name</code> with the type here being optional.</p> - -<a name="UsingSystemResources"></a> -<h3>Using System Resources</h3> - -<p>Many resources included with the system are available to applications. -All such resources are defined under the class "android.R". For example, -you can display the standard application icon in a screen with the following -code:</p> - -<pre class="prettyprint"> -public class MyActivity extends Activity -{ - public void onStart() - { - requestScreenFeatures(FEATURE_BADGE_IMAGE); - - super.onStart(); - - setBadgeResource(android.R.drawable.sym_def_app_icon); - } -} -</pre> - -<p>In a similar way, this code will apply to your screen the standard -"green background" visual treatment defined by the system:</p> - -<pre class="prettyprint"> -public class MyActivity extends Activity -{ - public void onStart() - { - super.onStart(); - - setTheme(android.R.style.Theme_Black); - } -} -</pre> - -<a name="AlternateResources" id="AlternateResources"></a> -<h2>Alternate Resources (for alternate languages and configurations)</h2> - -<p>You can supply different resources for your product according to the UI -language or hardware configuration on the device. Note that although you can -include different string, layout, and other resources, the SDK does not expose -methods to let you specify which alternate resource set to load. Android -detects the proper set for the hardware and location, and loads them as -appropriate. Users can select alternate language settings using the settings -panel on the device. </p> -<p>To include alternate resources, create parallel resource folders with -qualifiers appended to the folder names, indicating the configuration it -applies to (language, screen orientation, and so on). For example, here is a -project that holds one string resource file for English, and another for -French:</p> - -<pre> -MyApp/ - res/ - values-en/ - strings.xml - values-fr/ - strings.xml -</pre> - -<p>Android supports several types of qualifiers, with various values for each. -Append these to the end of the resource folder name, separated by dashes. You -can add multiple qualifiers to each folder name, but they must appear in the -order they are listed here. For example, a folder containing drawable -resources for a fully specified configuration would look like:</p> - -<pre> -MyApp/ - res/ - drawable-en-rUS-port-160dpi-finger-keysexposed-qwerty-dpad-480x320/ -</pre> - -<p>More typically, you will only specify a few specific configuration options -that a resource is defined for. You may drop any of the values from the -complete list, as long as the remaining values are still in the same -order:</p> - -<pre> -MyApp/ - res/ - drawable-en-rUS-finger/ - drawable-port/ - drawable-port-160dpi/ - drawable-qwerty/ -</pre> - -<table border="1"> - <tr> - <th> Qualifier </th> - <th> Values </th> - </tr> - <tr> - <td>Language</td> - <td>The two letter <a href="http://www.loc.gov/standards/iso639-2/php/code_list.php">ISO - 639-1</a> language code in lowercase. For example: - <code>en</code>, <code>fr</code>, <code>es</code> </td> - </tr> - <tr> - <td>Region</td> - <td>The two letter - <a href="http://www.iso.org/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html">ISO - 3166-1-alpha-2</a> language code in uppercase preceded by a lowercase - "r". For example: <code>rUS</code>, <code>rFR</code>, <code>rES</code></td> - </tr> - <tr> - <td>Screen orientation</td> - <td><code>port</code>, <code>land</code>, <code>square</code> </td> - </tr> - <tr> - <td>Screen pixel density</td> - <td><code>92dpi</code>, <code>108dpi</code>, etc. </td> - </tr> - <tr> - <td>Touchscreen type</td> - <td><code>notouch</code>, <code>stylus</code>, <code>finger</code></td> - </tr> - <tr> - <td>Whether the keyboard is available to the user</td> - <td><code>keysexposed</code>, <code>keyshidden</code> </td> - </tr> - <tr> - <td>Primary text input method</td> - <td><code>nokeys</code>, <code>qwerty</code>, <code>12key</code> </td> - </tr> - <tr> - <td>Primary non-touchscreen<br /> - navigation method</td> - <td><code>nonav</code>, <code>dpad</code>, <code>trackball</code>, <code>wheel</code> </td> - </tr> - <tr> - <td>Screen dimensions</td> - <td><code>320x240</code>, <code>640x480</code>, etc. The larger dimension - must be specified first. </td> - </tr> -</table> - -<p>This list does not include device-specific parameters such as carrier, -branding, device/hardware, or manufacturer. Everything that an application -needs to know about the device that it is running on is encoded via the -resource qualifiers in the table above.</p> - -<p>Here are some general guidelines on qualified resource directory names:</p> - -<ul> - <li>Values are separated by a dash (as well as a dash after the base directory - name) </li> - <li>Values are case-sensitive (even though they must be unique across all folder - names in a case-insensitive way)<br />For example,</li> - <ul> - <li>A portrait-specific <code>drawable</code> directory must be named - <code>drawable-port</code>, not <code>drawable-PORT</code>.</li> - <li>You may not have two directories named <code>drawable-port</code> - and <code>drawable-PORT</code>, even if you had intended "port" and - "PORT" to refer to different parameter values.</li> - </ul> - <li>Only one value for each qualifier type is supported (that is, you cannot - specify <code>drawable-rEN-rFR/</code>)</li> - <li>You can specify multiple parameters to define specific configurations, - but they must always be in the order listed above. - For example, <code>drawable-en-rUS-land</code> will apply to landscape view, - US-English devices. </li> - <li>Android will try to find the most specific matching directory for the current - configuration, as described below</li> - <li>The order of parameters listed in this table is used to break a tie in case - of multiple qualified directories (see the example given below) </li> - <li>All directories, both qualified and unqualified, live under the <code>res/</code> folder. - Qualified directories cannot be nested (you cannot have <code>res/drawable/drawable-en</code>) </li> - <li>All resources will be referenced in code or resource reference syntax by - their simple, undecorated name. So if a resource is named this:<br /> - <code>MyApp/res/drawable-port-92dp/myimage.png</code><br /> - It would be referenced as this:<br /> - <code>R.drawable.myimage</code> (code)<br /> - <code>@drawable/myimage</code> (XML)</li> -</ul> - -<h3>How Android finds the best matching directory </h3> - -<p>Android will pick which of the various underlying resource files should be -used at runtime, depending on the current configuration. The selection process -is as follows:</p> - -<ol> - <li> - Eliminate any resources whose configuration does not match the current - device configuration. For example, if the screen pixel density is 108dpi, - this would eliminate only <code>MyApp/res/drawable-port-92dpi/</code>. - <blockquote> - <pre> -MyApp/res/drawable/myimage.png -MyApp/res/drawable-en/myimage.png -MyApp/res/drawable-port/myimage.png -<strike>MyApp/res/drawable-port-92dpi/myimage.png</strike> -</pre> - </blockquote> - </li> - <li> - Pick the resources with the highest number of matching configurations. - For example, if our locale is en-GB and orientation is port, then we - have two candidates with one matching configuration each: - <code>MyApp/res/drawable-en/</code> and <code>MyApp/res/drawable-port/</code>. - The directory <code>MyApp/res/drawable/</code> is eliminated because - it has zero matching configurations, while the others have one matching - configuration. - <blockquote> - <pre> -<strike>MyApp/res/drawable/myimage.png</strike> -MyApp/res/drawable-en/myimage.png -MyApp/res/drawable-port/myimage.png -</pre> - </blockquote> - </li> - <li> - Pick the final matching file based on configuration precedence, which - is the order of parameters listed in the table above. That is, it is - more important to match the language than the orientation, so we break - the tie by picking the language-specific file, <code>MyApp/res/drawable-en/</code>. - <blockquote> - <pre>MyApp/res/drawable-en/myimage.png -<strike>MyApp/res/drawable-port/myimage.png</strike> -</pre> - </blockquote> - </li> -</ol> - -<a name="ResourcesTerminology"></a> -<h2>Terminology</h2> - -<p>The resource system brings a number of different pieces together to -form the final complete resource functionality. To help understand the -overall system, here are some brief definitions of the core concepts and -components you will encounter in using it:</p> - -<p><strong>Asset</strong>: A single blob of data associated with an application. This -includes object files compiled from the Java source code, graphics (such as PNG -images), XML files, etc. These files are organized in a directory hierarchy -that, during final packaging of the application, is bundled together into a -single ZIP file.</p> - -<p><strong>aapt</strong>: Android Asset Packaging Tool. The tool that generates the -final ZIP file of application assets. In addition to collecting raw assets -together, it also parses resource definitions into binary asset data.</p> - -<p><strong>Resource Table</strong>: A special asset that aapt generates for you, -describing all of the resources contained in an application/package. -This file is accessed for you by the Resources class; it is not touched -directly by applications.</p> - -<p><strong>Resource</strong>: An entry in the Resource Table describing a single -named value. Broadly, there are two types of resources: primitives and -bags.</p> - -<p><strong>Resource Identifier</strong>: In the Resource Table all resources are -identified by a unique integer number. In source code (resource descriptions, -XML files, Java source code) you can use symbolic names that stand as constants for -the actual resource identifier integer.</p> - -<p><strong>Primitive Resource</strong>: All primitive resources can be written as a -simple string, using formatting to describe a variety of primitive types -included in the resource system: integers, colors, strings, references to -other resources, etc. Complex resources, such as bitmaps and XML -describes, are stored as a primitive string resource whose value is the path -of the underlying Asset holding its actual data.</p> - -<p><strong>Bag Resource</strong>: A special kind of resource entry that, instead of a -simple string, holds an arbitrary list of name/value pairs. Each name is -itself a resource identifier, and each value can hold -the same kinds of string formatted data as a normal resource. Bags also -support inheritance: a bag can inherit the values from another bag, selectively -replacing or extending them to generate its own contents.</p> - -<p><strong>Kind</strong>: The resource kind is a way to organize resource identifiers -for various purposes. For example, drawable resources are used to -instantiate Drawable objects, so their data is a primitive resource containing -either a color constant or string path to a bitmap or XML asset. Other -common resource kinds are string (localized string primitives), color -(color primitives), layout (a string path to an XML asset describing a view -layout), and style (a bag resource describing user interface attributes). -There is also a standard "attr" resource kind, which defines the resource -identifiers to be used for naming bag items and XML attributes</p> - -<p><strong>Style</strong>: The name of the resource kind containing bags that are used -to supply a set of user interface attributes. For example, a TextView class may -be given a style resource that defines its text size, color, and alignment. -In a layout XML file, you associate a style with a bag using the "style" -attribute, whose value is the name of the style resource.</p> - -<p><strong>Style Class</strong>: Specifies a related set of attribute resources. -This data is not placed in the resource table itself, but used to generate -constants in the source code that make it easier for you to retrieve values out of -a style resource and/or XML tag's attributes. For example, the -Android platform defines a "View" style class that -contains all of the standard view attributes: padding, visibility, -background, etc.; when View is inflated it uses this style class to -retrieve those values from the XML file (at which point style and theme -information is applied as approriate) and load them into its instance.</p> - -<p><strong>Configuration</strong>: For any particular resource identifier, there may be -multiple different available values depending on the current configuration. -The configuration includes the locale (language and country), screen -orientation, screen density, etc. The current configuration is used to -select which resource values are in effect when the resource table is -loaded.</p> - -<p><strong>Theme</strong>: A standard style resource that supplies global -attribute values for a particular context. For example, when writing an -Activity the application developer can select a standard theme to use, such -as the Theme.White or Theme.Black styles; this style supplies information -such as the screen background image/color, default text color, button style, -text editor style, text size, etc. When inflating a layout resource, most -values for widgets (the text color, selector, background) if not explicitly -set will come from the current theme; style and attribute -values supplied in the layout can also assign their value from explicitly -named values in the theme attributes if desired.</p> - -<p><strong>Overlay</strong>: A resource table that does not define a new set of resources, -but instead replaces the values of resources that are in another resource table. -Like a configuration, this is applied at load time -to the resource data; it can add new configuration values (for example -strings in a new locale), replace existing values (for example change -the standard white background image to a "Hello Kitty" background image), -and modify resource bags (for example change the font size of the Theme.White -style to have an 18 pt font size). This is the facility that allows the -user to select between different global appearances of their device, or -download files with new appearances.</p> - -<h2>Resource Reference</h2> -<p>The <a href="available-resources.html">Available Resources</a> -document provides a detailed list of the various types of resource and how to use them -from within the Java source code, or from other references.</p> - -<a name="i18n" id="i18n"></a> -<h2>Internationalization and Localization</h2> -<p class="note"><strong>Coming Soon:</strong> Internationalization and Localization are -critical, but are also not quite ready yet in the current SDK. As the -SDK matures, this section will contain information on the Internationalization -and Localization features of the Android platform. In the meantime, it is a good -idea to start by externalizing all strings, and practicing good structure in -creating and using resources.</p> - +page.title=Resources and Internationalization
+parent.title=Resources and Assets
+parent.link=index.html
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+
+ <h2>Key classes</h2>
+ <ol>
+ <li>{@link android.content.res.Resources}</li>
+ </ol>
+
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#intro">Introduction</a></li>
+ <li><a href="#CreatingResources">Creating Resources</a></li>
+ <li><a href="#UsingResources">Using Resources</a>
+ <ol>
+ <li><a href="#ResourcesInCode">Using Resources in Code</a></li>
+ <li><a href="#ReferencesToResources">References to Resources</a></li>
+ <li><a href="#ReferencesToThemeAttributes">References to Theme Attributes</a></li>
+ <li><a href="#UsingSystemResources">Using System Resources</a></li>
+ </ol>
+ </li>
+ <li><a href="#AlternateResources">Alternate Resources</a></li>
+ <li><a href="#ResourcesTerminology">Terminology</a></li>
+ <li><a href="#i18n">Internationalization (I18N)</a></li>
+ </ol>
+</div>
+</div>
+
+<p>Resources are external files (that is, non-code files) that are used by
+your code and compiled into your application at build time. Android
+supports a number of different kinds of resource files, including XML,
+PNG, and JPEG files. The XML files have very different formats depending
+on what they describe. This document describes what kinds of files are
+supported, and the syntax or format of each.</p>
+<p>Resources are externalized from source code, and XML files are compiled into
+a binary, fast loading format for efficiency reasons. Strings, likewise, are compressed
+into a more efficient storage form. It is for these reasons that we have these
+different resource types in the Android platform.</p>
+
+<p>This is a fairly technically dense document, and together with the
+<a href="available-resources.html">Available Resources</a>
+document, they cover a lot of information about resources. It is not necessary
+to know this document by heart to use Android, but rather to know that the
+information is here when you need it.</p>
+
+<a name="intro"></a>
+<h2>Introduction</h2>
+
+<p>This topic includes a terminology list associated with resources, and a series
+ of examples of using resources in code. For a complete guide to the supported
+ Android resource types, see
+ <a href="available-resources.html">Available Resources</a>.
+ </p>
+<p>The Android resource system keeps track of all non-code
+ assets associated with an application. You use the
+ {@link android.content.res.Resources Resources} class to access your
+ application's resources; the Resources instance associated with your
+ application can generally be found through
+ {@link android.content.Context#getResources Context.getResources()}.</p>
+<p>An application's resources are compiled into the application
+binary at build time for you by the build system. To use a resource,
+you must install it correctly in the source tree and build your
+application. As part of the build process, symbols for each
+of the resources are generated that you can use in your source
+code -- this allows the compiler to verify that your application code matches
+up with the resources you defined.</p>
+
+<p>The rest of this section is organized as a tutorial on how to
+use resources in an application.</p>
+
+<a name="CreatingResources" id="CreatingResources"></a>
+<h2>Creating Resources</h2>
+
+<p>Android supports string, bitmap, and many other types of resource. The syntax and format
+of each, and where they're stored, depends upon the type of object. In
+general, though, you create resources from three types of files: XML files
+(everything but bitmaps and raw), bitmap files(for images) and Raw files (anything
+else, for example sound files, etc.). In fact, there are two different types of
+XML file as well, those that get compiled as-is into the package, and those that
+are used to generate resources by aapt. Here is a list of each
+resource type, the format of the file, a description of the file, and details
+of any XML files. </p>
+
+<p>You will create and store your resource files under the appropriate
+subdirectory under the <code>res/</code> directory in your project. Android
+has a resource compiler (aapt) that compiles resources according to which
+subfolder they are in, and the format of the file. Table 1 shows a llist of the file
+types for each resource. See the
+<a href="available-resources.html">Available Resources</a> for
+descriptions of each type of object, the syntax, and the format or syntax of
+the containing file.</p>
+<p class="caption">Table 1</p>
+<table width="100%" border="1">
+ <tr>
+ <th scope="col">Directory</th>
+ <th scope="col">Resource Types </th>
+ </tr>
+ <tr>
+ <td><code>res/anim/</code></td>
+ <td>XML files that are compiled into
+ <a href="available-resources.html#animationdrawable">frame by
+ frame animation</a> or
+ <a href="available-resources.html#tweenedanimation">tweened
+ animation</a> objects </td>
+ </tr>
+ <tr>
+ <td><code>res/drawable/</code></td>
+ <td><p>.png, .9.png, .jpg files that are compiled into the following
+ Drawable resource subtypes:</p>
+ <ul class="nolist">
+ <li><a href="available-resources.html#imagefileresources">bitmap files</a></li>
+ <li><a href="available-resources.html#ninepatch">9-patches (resizable bitmaps)</a></li>
+ </ul>
+ <p>To get a resource of this type, use <code>mContext.getResources().getDrawable(R.drawable.<em>imageId</em>)</code></p>
+ <p class="note"><strong>Note:</strong> Image resources placed in here may
+ be automatically optimized with lossless image compression by the
+ <a href="{@docRoot}guide/developing/tools/aapt.html">aapt</a> tool. For example, a true-color PNG
+ that does not require more than 256 colors may be converted to an 8-bit PNG with a color palette.
+ This will result in an image of equal quality but which requires less memory. So be aware that the
+ image binaries placed in this directory can change during the build. If you plan on reading
+ an image as a bit stream in order to convert it to a bitmap, put your images in the
+ <code>res/raw/</code> folder instead, where they will not be optimized.</p>
+ </td>
+ </tr>
+ <tr>
+ <td><code>res/layout/</code></td>
+ <td>XML files that are compiled into screen layouts (or part of a screen).
+ See <a href="{@docRoot}guide/topics/ui/declaring-layout.html">Declaring Layout</a>.</td>
+ </tr>
+ <tr>
+ <td><code>res/values/</code></td>
+ <td><p>XML files that can be compiled into many kinds of resource.</p>
+ <p class="note"><strong>Note:</strong> Unlike the other res/ folders, this one
+ can hold any number of files that hold descriptions of resources to create
+ rather than the resources themselves. The XML element types control
+ where these resources are placed under the R class.</p>
+ <p>While the files can be named anything, these are
+ the typical files in this folder (the convention is to name
+ the file after the type of elements defined within):</p>
+ <ul>
+ <li><strong>arrays.xml</strong> to define arrays </li>
+ <!-- TODO: add section on arrays -->
+ <li><strong>colors.xml</strong> to define <a href="available-resources.html#colordrawableresources">color
+ drawables</a> and <a href="#colorvals">color string values</a>.
+ Use <code>Resources.getDrawable()</code> and
+ <code>Resources.getColor(), respectively,</code>
+ to get these resources.</li>
+ <li><strong>dimens.xml</strong> to define <a href="available-resources.html#dimension">dimension value</a>. Use <code>Resources.getDimension()</code> to get
+ these resources.</li>
+ <li><strong>strings.xml</strong> to define <a href="available-resources.html#stringresources">string</a> values (use either
+ <code>Resources.getString</code> or preferably <code>Resources.getText()</code>
+ to get
+ these resources. <code>getText()</code> will retain any rich text styling
+ which is usually desirable for UI strings.</li>
+ <li><strong>styles.xml</strong> to define <a href="available-resources.html#stylesandthemes">style</a> objects.</li>
+ </ul></td>
+ </tr>
+ <tr>
+ <td><code>res/xml/</code></td>
+ <td>Arbitrary XML files that are compiled and can be read at run time by
+ calling {@link android.content.res.Resources#getXml(int) Resources.getXML()}.</td>
+ </tr>
+ <tr>
+ <td><code>res/raw/</code></td>
+ <td>Arbitrary files to copy directly to the device. They are added uncompiled
+ to the compressed file that your application build produces. To use these
+ resources in your application, call {@link android.content.res.Resources#openRawResource(int)
+ Resources.openRawResource()} with the resource ID, which is R.raw.<em>somefilename</em>.</td>
+ </tr>
+</table>
+<p>Resources are compiled into the final APK file. Android creates a wrapper class,
+ called R, that you can use to refer to these resources in your code. R contains subclasses
+ named according to the path and file name of the source file</p>
+<a name="colorvals" id="colorvals"></a>
+<h3>Global Resource Notes</h3>
+<ul>
+ <li>Several resources allow you to define colors. Android accepts color values
+ written in various web-style formats -- a hexadecimal constant in any of the
+ following forms: #RGB, #ARGB, #RRGGBB, #AARRGGBB. </li>
+ <li>All color values support setting an alpha channel value, where the first
+ two hexadecimal numbers specify the transparency. Zero in the alpha channel
+ means transparent. The default value is opaque. </li>
+</ul>
+<a name="UsingResources" id="UsingResources"></a>
+<h2>Using Resources </h2>
+<p>This section describes how to use the resources you've created. It includes the
+ following topics:</p>
+<ul>
+ <li><a href="#ResourcesInCode">Using resources in code</a> - How to call
+ resources in your code to instantiate them. </li>
+ <li><a href="#ReferencesToResources">Referring to resources from other resources</a> -
+ You can reference resources from other resources. This lets you reuse common
+ resource values inside resources. </li>
+ <li><a href="#AlternateResources">Supporting Alternate Resources for Alternate
+ Configurations</a> - You can specify different resources
+ to load, depending on the language or display configuration of the host
+ hardware. </li>
+</ul>
+<p>At compile time, Android generates a class named R that contains resource identifiers
+ to all the resources in your program. This class contains several subclasses,
+ one for each type of resource supported by Android, and for which you provided
+ a resource file. Each class contains one or more identifiers for the compiled resources,
+ that you use in your code to load the resource. Here is a small resource file
+ that contains string, layout (screens or parts of screens), and image resources.</p>
+<p class="note"><strong>Note:</strong> the R class is an auto-generated file and is not
+designed to be edited by hand. It will be automatically re-created as needed when
+the resources are updated.</p>
+<pre class="prettyprint">package com.android.samples;
+public final class R {
+ public static final class string {
+ public static final int greeting=0x0204000e;
+ public static final int start_button_text=0x02040001;
+ public static final int submit_button_text=0x02040008;
+ public static final int main_screen_title=0x0204000a;
+ };
+ public static final class layout {
+ public static final int start_screen=0x02070000;
+ public static final int new_user_pane=0x02070001;
+ public static final int select_user_list=0x02070002;
+
+ };
+ public static final class drawable {
+ public static final int company_logo=0x02020005;
+ public static final int smiling_cat=0x02020006;
+ public static final int yellow_fade_background=0x02020007;
+ public static final int stretch_button_1=0x02020008;
+
+ };
+};
+</pre>
+<a name="ResourcesInCode" id="ResourcesInCode"></a>
+<h3>Using Resources in Code </h3>
+
+<p>Using resources in code is just a matter of knowing the full resource ID
+and what type of object your resource has been compiled into. Here is the
+syntax for referring to a resource:</p>
+<p><code>R.<em>resource_type</em>.<em>resource_name</em></code></p>
+<p>or</p>
+<p><code>android.R.<em>resource_type</em>.<em>resource_name</em></code></p>
+
+<p>Where <code>resource_type</code> is the R subclass that holds a specific type
+of resource. <code>resource_name</code> is the <em>name</em> attribute for resources
+defined in XML files, or the file name (without the extension) for resources
+defined by other file types. Each type of resource will be added to a specific
+R subclass, depending on the type of resource it is; to learn which R subclass
+hosts your compiled resource type, consult the
+<a href="available-resources.html">Available Resources</a> document. Resources compiled by your own application can
+be referred to without a package name (simply as
+<code>R.<em>resource_type</em>.<em>resource_name</em></code>). Android contains
+a number of standard resources, such as screen styles and button backgrounds. To
+refer to these in code, you must qualify them with <code>android</code>, as in
+<code>android.R.drawable.button_background</code>.</p>
+
+<p>Here are some good and bad examples of using compiled resources in code:</p>
+
+<pre class="prettyprint">// Load a background for the current screen from a drawable resource.
+this.getWindow().setBackgroundDrawableResource(R.drawable.my_background_image);
+
+// WRONG Sending a string resource reference into a
+// method that expects a string.
+this.getWindow().setTitle(R.string.main_title);
+
+// RIGHT Need to get the title from the Resources wrapper.
+this.getWindow().setTitle(Resources.getText(R.string.main_title));
+
+// Load a custom layout for the current screen.
+setContentView(R.layout.main_screen);
+
+// Set a slide in animation for a ViewFlipper object.
+mFlipper.setInAnimation(AnimationUtils.loadAnimation(this,
+ R.anim.hyperspace_in));
+
+// Set the text on a TextView object.
+TextView msgTextView = (TextView)findViewByID(R.id.msg);
+msgTextView.setText(R.string.hello_message); </pre>
+
+<a name="ReferencesToResources" id="ReferencesToResources"></a>
+<h3>References to Resources</h3>
+
+<p>A value supplied in an attribute (or resource) can also be a reference to
+a resource. This is often used in layout files to supply strings (so they
+can be localized) and images (which exist in another file), though a reference
+can be any resource type including colors and integers.</p>
+
+<p>For example, if we have
+<a href="available-resources.html#colordrawableresources">color
+resources</a>, we can write a layout file that sets the text color size to be
+the value contained in one of those resources:</p>
+
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<EditText id="text"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent" android:layout_height="fill_parent"
+ <strong>android:textColor="@color/opaque_red"</strong>
+ android:text="Hello, World!" />
+</pre>
+
+<p>Note here the use of the '@' prefix to introduce a resource reference -- the
+text following that is the name of a resource in the form
+of <code>@[package:]type/name</code>. In this case we didn't need to specify
+the package because we are referencing a resource in our own package. To
+reference a system resource, you would need to write:</p>
+
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<EditText id="text"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent" android:layout_height="fill_parent"
+ android:textColor="@<strong>android:</strong>color/opaque_red"
+ android:text="Hello, World!" />
+</pre>
+
+<p>As another example, you should always use resource references when supplying
+strings in a layout file so that they can be localized:</p>
+
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<EditText id="text"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent" android:layout_height="fill_parent"
+ android:textColor="@android:color/opaque_red"
+ android:text="@string/hello_world" />
+</pre>
+
+<p>This facility can also be used to create references between resources.
+For example, we can create new drawable resources that are aliases for
+existing images:</p>
+
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <drawable id="my_background">@android:drawable/theme2_background</drawable>
+</resources>
+</pre>
+
+<a name="ReferencesToThemeAttributes"></a>
+<h3>References to Theme Attributes</h3>
+
+<p>Another kind of resource value allows you to reference the value of an
+attribute in the current theme. This attribute reference can <em>only</em>
+be used in style resources and XML attributes; it allows you to customize the
+look of UI elements by changing them to standard variations supplied by the
+current theme, instead of supplying more concrete values.</p>
+
+<p>As an example, we can use this in our layout to set the text color to
+one of the standard colors defined in the base system theme:</p>
+
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<EditText id="text"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent" android:layout_height="fill_parent"
+ <strong>android:textColor="?android:textDisabledColor"</strong>
+ android:text="@string/hello_world" />
+</pre>
+
+<p>Note that this is very similar to a resource reference, except we are using
+an '?' prefix instead of '@'. When you use this markup, you are supplying
+the name of an attribute resource that will be looked up in the theme --
+because the resource tool knows that an attribute resource is expected,
+you do not need to explicitly state the type (which would be
+<code>?android:attr/android:textDisabledColor</code>).</p>
+
+<p>Other than using this resource identifier to find the value in the
+theme instead of raw resources, the name syntax is identical to the '@' format:
+<code>?[namespace:]type/name</code> with the type here being optional.</p>
+
+<a name="UsingSystemResources"></a>
+<h3>Using System Resources</h3>
+
+<p>Many resources included with the system are available to applications.
+All such resources are defined under the class "android.R". For example,
+you can display the standard application icon in a screen with the following
+code:</p>
+
+<pre class="prettyprint">
+public class MyActivity extends Activity
+{
+ public void onStart()
+ {
+ requestScreenFeatures(FEATURE_BADGE_IMAGE);
+
+ super.onStart();
+
+ setBadgeResource(android.R.drawable.sym_def_app_icon);
+ }
+}
+</pre>
+
+<p>In a similar way, this code will apply to your screen the standard
+"green background" visual treatment defined by the system:</p>
+
+<pre class="prettyprint">
+public class MyActivity extends Activity
+{
+ public void onStart()
+ {
+ super.onStart();
+
+ setTheme(android.R.style.Theme_Black);
+ }
+}
+</pre>
+
+<a name="AlternateResources" id="AlternateResources"></a>
+<h2>Alternate Resources (for alternate languages and configurations)</h2>
+
+<p>You can supply different resources for your application to use depending on the UI
+language or hardware configuration on the device. Note that although you can
+include different string, layout, and other resources, the SDK does not expose
+methods to let you specify which alternate resource set to load. Android
+detects the proper set for the hardware and location, and loads them as
+appropriate. Users can select alternate language settings using the settings
+panel on the device. </p>
+<p>To include alternate resources, create parallel resource folders with
+qualifiers appended to the folder names, indicating the configuration it
+applies to (language, screen orientation, and so on). For example, here is a
+project that holds one string resource file for English, and another for
+French:</p>
+
+<pre>
+MyApp/
+ res/
+ values-en/
+ strings.xml
+ values-fr/
+ strings.xml
+</pre>
+
+<p>Android supports several types of qualifiers, with various values for each.
+Append these to the end of the resource folder name, separated by dashes. You
+can add multiple qualifiers to each folder name, but they must appear in the
+order they are listed here. For example, a folder containing drawable
+resources for a fully specified configuration would look like this:</p>
+
+<pre>
+MyApp/
+ res/
+ drawable-en-rUS-port-160dpi-finger-keysexposed-qwerty-dpad-480x320/
+</pre>
+
+<p>More typically, you will only specify a few specific configuration options. You may drop any of the values from the
+complete list, as long as the remaining values are still in the same
+order:</p>
+
+<pre>
+MyApp/
+ res/
+ drawable-en-rUS-finger/
+ drawable-port/
+ drawable-port-160dpi/
+ drawable-qwerty/
+</pre>
+<p>Table 2 lists the valid folder-name qualifiers, in order of precedence. Qualifiers that are listed higher in the table take precedence over those listed lower, as described in <a href="#best-match">How Android finds the best matching directory</a>. </p>
+<p class="caption" id="table2">Table 2</p>
+<table border="1">
+ <tr>
+ <th> Qualifier </th>
+ <th> Values </th>
+ </tr>
+ <tr>
+ <td>MCC and MNC</td>
+ <td>The <a href="http://en.wikipedia.org/wiki/Mobile_country_code">mobile country code</a> and <a href="http://en.wikipedia.org/wiki/Mobile_Network_Code">mobile network code</a> from the SIM in the device. For example <code>mcc310-mnc004</code> (U.S., Verizon brand); <code>mcc208-mnc00</code> (France, Orange brand); <code>mcc234-mnc00</code> (U.K., BT brand). <br>
+ <br>
+ If the device uses a radio connection (GSM phone), the MCC will come from the SIM, and the MNC will come from the network to which the device is attached. You might sometimes use the MCC alone, for example to include country-specific legal resources in your application. If your application specifies resources for a MCC/MNC combination, those resources can only be used if both the MCC and the MNC match. </td>
+ </tr>
+ <tr>
+ <td>Language and region</td>
+ <td>The two letter <a href="http://www.loc.gov/standards/iso639-2/php/code_list.php">ISO
+ 639-1</a> language code and two letter
+ <a href="http://www.iso.org/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html">ISO
+ 3166-1-alpha-2</a> region code (preceded by lowercase "r"). For example
+ <code>en-rUS</code>, <code>fr-rFR</code>, <code>es-rES</code>. <br>
+ <br>
+ The codes are case-sensitive: The language code is lowercase, and the country code is uppercase. You cannot specify a region alone, but you can specify a language alone, for example <code>en</code>, <code>fr</code>, <code>es</code>. </td>
+ </tr>
+ <tr>
+ <td>Screen orientation</td>
+ <td><code>port</code>, <code>land</code>, <code>square</code> </td>
+ </tr>
+ <tr>
+ <td>Screen pixel density</td>
+ <td><code>92dpi</code>, <code>108dpi</code>, etc. When Android selects which resource files to use, it handles screen density differently than the other qualifiers. In step 1 of <a href="#best-match">How Android finds the best matching directory</a> (below), screen density is always considered to be a match. In step 4, if the qualifier being considered is screen density, Android will select the best final match at that point, without any need to move on to step 5. </td>
+ </tr>
+ <tr>
+ <td>Touchscreen type</td>
+ <td><code>notouch</code>, <code>stylus</code>, <code>finger</code></td>
+ </tr>
+ <tr>
+ <td>Whether the keyboard is available to the user</td>
+ <td><code>keysexposed</code>, <code>keyshidden</code>, <code>keyssoft</code> <br>
+ If your application has specific resources that should only be used with a soft keyboard, use the <code>keyssoft</code> value. If no <code>keyssoft</code> resources are available (only <code>keysexposed</code> and <code>keyshidden</code>) and the device shows a soft keyboard, the system will use <code>keysexposed</code> resources. </td>
+ </tr>
+ <tr>
+ <td>Primary text input method</td>
+ <td><code>nokeys</code>, <code>qwerty</code>, <code>12key</code> </td>
+ </tr>
+ <tr>
+ <td>Primary non-touchscreen<br />
+ navigation method</td>
+ <td><code>nonav</code>, <code>dpad</code>, <code>trackball</code>, <code>wheel</code> </td>
+ </tr>
+ <tr>
+ <td>Screen dimensions</td>
+ <td><code>320x240</code>, <code>640x480</code>, etc. The larger dimension
+ must be specified first. </td>
+ </tr>
+ <tr>
+ <td>SDK version</td>
+ <td>The SDK version supported by the device, for example <code>v3</code>. The Android 1.0 SDK is <code>v1, </code> the 1.1 SDK is <code>v2</code>, and the 1.5 SDK is <code>v3</code>.</td>
+ </tr>
+ <tr>
+ <td>(Minor version)</td>
+ <td>(You cannot currently specify minor version. It is always set to 0.)</td>
+ </tr>
+</table>
+
+<p>This list does not include device-specific parameters such as carrier,
+branding, device/hardware, or manufacturer. Everything that an application
+needs to know about the device that it is running on is encoded via the
+resource qualifiers in the table above.</p>
+
+<p>All resource directories, qualified and unqualified, live under the <code>res/</code> folder. Here are some guidelines on qualified resource directory names:</p>
+
+<ul>
+ <li>You can specify multiple qualifiers, separated by dashes. For example, <code>drawable-en-rUS-land</code> will apply to US-English
+ devices in landscape orientation. </li>
+ <li>The qualifiers must be in the order listed in <a href="#table2">Table 2</a> above. For example:
+ <ul>
+ <li>Correct: <code>values-mcc460-nokeys/</code></li>
+ <li>Incorrect: <code>values-nokeys-mcc460/</code></li>
+ </ul>
+ </li>
+ <li>Values are case-sensitive. For example, a portrait-specific <code>drawable</code> directory must be named
+ <code>drawable-port</code>, not <code>drawable-PORT</code> or <code>drawable-Port</code>.</li>
+ <li>Only one value for each qualifier type is supported. For example, if you want to use exactly the same drawable files for Spain and France, you will need two resource directories, such as <code>drawable-rES/</code> and <code>drawable-rFR/</code>, containing identical files. You cannot
+ have a directory named <code>drawable-rES-rFR/</code>. </li>
+ <li>Qualified directories cannot be nested. For example, you cannot have <code>res/drawable/drawable-en</code>. </li>
+</ul>
+
+<h3>How resources are referenced in code</h3>
+<p>All resources will be referenced in code or resource reference syntax by
+ their simple, undecorated names. So if a resource were named this:<br />
+ <code>MyApp/res/drawable-port-92dpi/myimage.png</code><br />
+ It would be referenced as this:<br />
+ <code>R.drawable.myimage</code> (code)<br />
+ <code>@drawable/myimage</code> (XML)</p>
+<p>If several drawable directories are available, Android will select one of them (as described below) and load <code>myimage.png</code> from it.</p>
+<h3 id="best-match">How Android finds the best matching directory </h3>
+
+<p>Android will pick which of the various underlying resource files should be
+used at runtime, depending on the current configuration of the device. The example used here assumes the following device configuration:</p>
+<blockquote>
+ <p>Locale = <code>en-GB</code><br>
+ Screen orientation = <code>port</code><br>
+ Screen pixel density = <code>108dpi</code><br>
+ Touchscreen type = <code>notouch</code><br>
+ Primary text input method = <code>12key</code><br>
+ </p>
+</blockquote>
+<p>Here is how Android makes the selection: </p>
+<ol>
+ <li>
+ Eliminate resource files that contradict the
+ device configuration. For example, assume that the following resource directories are available for drawables. The <code>drawable-fr-rCA/</code> directory will be eliminated, because it contradicts the locale of the device.<br>
+<pre>MyApp/res/drawable/
+MyApp/res/drawable-en/
+<strike>MyApp/res/drawable-fr-rCA/</strike>
+MyApp/res/drawable-en-port/
+MyApp/res/drawable-en-notouch-12key/
+MyApp/res/drawable-port-92dpi/
+MyApp/res/drawable-port-notouch-12key</pre>
+ <strong>Exception: </strong>Screen pixel density is the one qualifier that is not used to eliminate files. Even though the screen density of the device is 108 dpi, <code>drawable-port-92dpi/</code> is not eliminated from the list, because every screen density is considered to be a
+ match at this point.</li>
+ <li>From <a href="#table2">Table 2</a>, pick the highest-precedence qualifier that remains in the list. (Start with MCC, then move down through the list.) </li>
+ <li>Do any of the available resource directories include this qualifier? </li>
+ <ul>
+ <li>If No, return to step 2 and look at the next qualifier listed in Table 2. In our example, the answer is "no" until we reach Language.</li>
+ <li>If Yes, move on to step 4.</li>
+ </ul>
+ <li>Eliminate resource directories that do not include this qualifier. In our example, we eliminate all the directories that do not include a language qualifier. </li>
+ <pre><strike>MyApp/res/drawable/</strike>
+MyApp/res/drawable-en/
+MyApp/res/drawable-en-port/
+MyApp/res/drawable-en-notouch-12key/
+<strike>MyApp/res/drawable-port-92dpi/</strike>
+<strike>MyApp/res/drawable-port-notouch-12key</strike></pre>
+ <strong>Exception:</strong> If the qualifier in question is screen pixel density, Android will select the option that most closely matches the device, and the selection process will be complete. In general, Android will prefer scaling down a larger original image to scaling up a smaller original image.<br><br></li>
+
+<li>Go back and repeat steps 2, 3, and 4 until only one choice remains. In the example, screen orientation is the next qualifier in the table for which we have any matches.
+ Eliminate resources that do not specify a screen orientation. </p>
+ <pre><strike>MyApp/res/drawable-en/</strike>
+MyApp/res/drawable-en-port/
+<strike>MyApp/res/drawable-en-notouch-12key/</strike></pre>
+ Only one choice remains, so that's it. When drawables are called for in this example application, the Android system will load resources from the <code>MyApp/res/drawable-en-port/</code> directory.
+</ol>
+<p class="note"><strong>Tip:</strong> The <em>precedence</em> of the qualifiers is more important than the number of qualifiers that exactly match the device. For example, in step 4 above, the last choice on the list includes three qualifiers that exactly match the device (orientation, touchscreen type, and input method), while <code>drawable-en</code> has only one parameter that matches (language). However, language has a higher precedence, so <code>drawable-port-notouch-12key</code> is out.</p>
+<p>This flowchart summarizes how Android selects resource directories to load.</p>
+<p><img src="res-selection-flowchart.png" alt="resource-selection" width="461" height="471" style="margin:15px"></p>
+<h3>Terminology</h3>
+<p>The resource system brings a number of different pieces together to
+form the final complete resource functionality. To help understand the
+overall system, here are some brief definitions of the core concepts and
+components you will encounter in using it:</p>
+
+<p><strong>Asset</strong>: A single blob of data associated with an application. This
+includes object files compiled from the Java source code, graphics (such as PNG
+images), XML files, etc. These files are organized in a directory hierarchy
+that, during final packaging of the application, is bundled together into a
+single ZIP file.</p>
+
+<p><strong>aapt</strong>: Android Asset Packaging Tool. The tool that generates the
+final ZIP file of application assets. In addition to collecting raw assets
+together, it also parses resource definitions into binary asset data.</p>
+
+<p><strong>Resource Table</strong>: A special asset that aapt generates for you,
+describing all of the resources contained in an application/package.
+This file is accessed for you by the Resources class; it is not touched
+directly by applications.</p>
+
+<p><strong>Resource</strong>: An entry in the Resource Table describing a single
+named value. Broadly, there are two types of resources: primitives and
+bags.</p>
+
+<p><strong>Resource Identifier</strong>: In the Resource Table all resources are
+identified by a unique integer number. In source code (resource descriptions,
+XML files, Java source code) you can use symbolic names that stand as constants for
+the actual resource identifier integer.</p>
+
+<p><strong>Primitive Resource</strong>: All primitive resources can be written as a
+simple string, using formatting to describe a variety of primitive types
+included in the resource system: integers, colors, strings, references to
+other resources, etc. Complex resources, such as bitmaps and XML
+describes, are stored as a primitive string resource whose value is the path
+of the underlying Asset holding its actual data.</p>
+
+<p><strong>Bag Resource</strong>: A special kind of resource entry that, instead of a
+simple string, holds an arbitrary list of name/value pairs. Each name is
+itself a resource identifier, and each value can hold
+the same kinds of string formatted data as a normal resource. Bags also
+support inheritance: a bag can inherit the values from another bag, selectively
+replacing or extending them to generate its own contents.</p>
+
+<p><strong>Kind</strong>: The resource kind is a way to organize resource identifiers
+for various purposes. For example, drawable resources are used to
+instantiate Drawable objects, so their data is a primitive resource containing
+either a color constant or string path to a bitmap or XML asset. Other
+common resource kinds are string (localized string primitives), color
+(color primitives), layout (a string path to an XML asset describing a view
+layout), and style (a bag resource describing user interface attributes).
+There is also a standard "attr" resource kind, which defines the resource
+identifiers to be used for naming bag items and XML attributes</p>
+
+<p><strong>Style</strong>: The name of the resource kind containing bags that are used
+to supply a set of user interface attributes. For example, a TextView class may
+be given a style resource that defines its text size, color, and alignment.
+In a layout XML file, you associate a style with a bag using the "style"
+attribute, whose value is the name of the style resource.</p>
+
+<p><strong>Style Class</strong>: Specifies a related set of attribute resources.
+This data is not placed in the resource table itself, but used to generate
+constants in the source code that make it easier for you to retrieve values out of
+a style resource and/or XML tag's attributes. For example, the
+Android platform defines a "View" style class that
+contains all of the standard view attributes: padding, visibility,
+background, etc.; when View is inflated it uses this style class to
+retrieve those values from the XML file (at which point style and theme
+information is applied as approriate) and load them into its instance.</p>
+
+<p><strong>Configuration</strong>: For any particular resource identifier, there may be
+multiple different available values depending on the current configuration.
+The configuration includes the locale (language and country), screen
+orientation, etc. The current configuration is used to
+select which resource values are in effect when the resource table is
+loaded.</p>
+
+<p><strong>Theme</strong>: A standard style resource that supplies global
+attribute values for a particular context. For example, when writing an
+Activity the application developer can select a standard theme to use, such
+as the Theme.White or Theme.Black styles; this style supplies information
+such as the screen background image/color, default text color, button style,
+text editor style, text size, etc. When inflating a layout resource, most
+values for widgets (the text color, selector, background) if not explicitly
+set will come from the current theme; style and attribute
+values supplied in the layout can also assign their value from explicitly
+named values in the theme attributes if desired.</p>
+
+<p><strong>Overlay</strong>: A resource table that does not define a new set of resources,
+but instead replaces the values of resources that are in another resource table.
+Like a configuration, this is applied at load time
+to the resource data; it can add new configuration values (for example
+strings in a new locale), replace existing values (for example change
+the standard white background image to a "Hello Kitty" background image),
+and modify resource bags (for example change the font size of the Theme.White
+style to have an 18 pt font size). This is the facility that allows the
+user to select between different global appearances of their device, or
+download files with new appearances.</p>
+
+<h2>Resource Reference</h2>
+<p>The <a href="available-resources.html">Available Resources</a>
+document provides a detailed list of the various types of resource and how to use them
+from within the Java source code, or from other references.</p>
+
+<a name="i18n" id="i18n"></a>
+<h2>Internationalization and Localization</h2>
+<p class="note"><strong>Coming Soon:</strong> Internationalization and Localization are
+critical, but are also not quite ready yet in the current SDK. As the
+SDK matures, this section will contain information on the Internationalization
+and Localization features of the Android platform. In the meantime, it is a good
+idea to start by externalizing all strings, and practicing good structure in
+creating and using resources.</p>
\ No newline at end of file diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java index 3db45f0..a7a8708 100644 --- a/graphics/java/android/graphics/drawable/GradientDrawable.java +++ b/graphics/java/android/graphics/drawable/GradientDrawable.java @@ -880,7 +880,9 @@ public class GradientDrawable extends Drawable { mShape = state.mShape; mGradient = state.mGradient; mOrientation = state.mOrientation; - mColors = state.mColors.clone(); + if (state.mColors != null) { + mColors = state.mColors.clone(); + } if (state.mPositions != null) { mPositions = state.mPositions.clone(); } diff --git a/include/media/ToneGenerator.h b/include/media/ToneGenerator.h index 6b0cc8a..eafa661 100644 --- a/include/media/ToneGenerator.h +++ b/include/media/ToneGenerator.h @@ -71,6 +71,82 @@ public: TONE_SUP_CONGESTION_ABBREV, // Abbreviated congestion: congestion tone limited to 4 seconds TONE_SUP_CONFIRM, // Confirm tone: a 350 Hz tone added to a 440 Hz tone repeated 3 times in a 100 ms on, 100 ms off cycle. TONE_SUP_PIP, // Pip tone: four bursts of 480 Hz tone (0.1 s on, 0.1 s off). + + // CDMA Tones + TONE_CDMA_DIAL_TONE_LITE, + TONE_CDMA_NETWORK_USA_RINGBACK, + TONE_CDMA_INTERCEPT, + TONE_CDMA_ABBR_INTERCEPT, + TONE_CDMA_REORDER, + TONE_CDMA_ABBR_REORDER, + TONE_CDMA_NETWORK_BUSY, + TONE_CDMA_CONFIRM, + TONE_CDMA_ANSWER, + TONE_CDMA_NETWORK_CALLWAITING, + TONE_CDMA_PIP, + + // ISDN + TONE_CDMA_CALL_SIGNAL_ISDN_NORMAL, // ISDN Alert Normal + TONE_CDMA_CALL_SIGNAL_ISDN_INTERGROUP, // ISDN Intergroup + TONE_CDMA_CALL_SIGNAL_ISDN_SP_PRI, // ISDN SP PRI + TONE_CDMA_CALL_SIGNAL_ISDN_PAT3, // ISDN Alert PAT3 + TONE_CDMA_CALL_SIGNAL_ISDN_PING_RING, // ISDN Alert PING RING + TONE_CDMA_CALL_SIGNAL_ISDN_PAT5, // ISDN Alert PAT5 + TONE_CDMA_CALL_SIGNAL_ISDN_PAT6, // ISDN Alert PAT6 + TONE_CDMA_CALL_SIGNAL_ISDN_PAT7, // ISDN Alert PAT7 + // ISDN end + + // IS54 + TONE_CDMA_HIGH_L, // IS54 High Pitch Long + TONE_CDMA_MED_L, // IS54 Med Pitch Long + TONE_CDMA_LOW_L, // IS54 Low Pitch Long + TONE_CDMA_HIGH_SS, // IS54 High Pitch Short Short + TONE_CDMA_MED_SS, // IS54 Medium Pitch Short Short + TONE_CDMA_LOW_SS, // IS54 Low Pitch Short Short + TONE_CDMA_HIGH_SSL, // IS54 High Pitch Short Short Long + TONE_CDMA_MED_SSL, // IS54 Medium Pitch Short Short Long + TONE_CDMA_LOW_SSL, // IS54 Low Pitch Short Short Long + TONE_CDMA_HIGH_SS_2, // IS54 High Pitch Short Short 2 + TONE_CDMA_MED_SS_2, // IS54 Med Pitch Short Short 2 + TONE_CDMA_LOW_SS_2, // IS54 Low Pitch Short Short 2 + TONE_CDMA_HIGH_SLS, // IS54 High Pitch Short Long Short + TONE_CDMA_MED_SLS, // IS54 Med Pitch Short Long Short + TONE_CDMA_LOW_SLS, // IS54 Low Pitch Short Long Short + TONE_CDMA_HIGH_S_X4, // IS54 High Pitch Short Short Short Short + TONE_CDMA_MED_S_X4, // IS54 Med Pitch Short Short Short Short + TONE_CDMA_LOW_S_X4, // IS54 Low Pitch Short Short Short Short + TONE_CDMA_HIGH_PBX_L, // PBX High Pitch Long + TONE_CDMA_MED_PBX_L, // PBX Med Pitch Long + TONE_CDMA_LOW_PBX_L, // PBX Low Pitch Long + TONE_CDMA_HIGH_PBX_SS, // PBX High Short Short + TONE_CDMA_MED_PBX_SS, // PBX Med Short Short + TONE_CDMA_LOW_PBX_SS, // PBX Low Short Short + TONE_CDMA_HIGH_PBX_SSL, // PBX High Short Short Long + TONE_CDMA_MED_PBX_SSL, // PBX Med Short Short Long + TONE_CDMA_LOW_PBX_SSL, // PBX Low Short Short Long + TONE_CDMA_HIGH_PBX_SLS, // PBX High SLS + TONE_CDMA_MED_PBX_SLS, // PBX Med SLS + TONE_CDMA_LOW_PBX_SLS, // PBX Low SLS + TONE_CDMA_HIGH_PBX_S_X4, // PBX High SSSS + TONE_CDMA_MED_PBX_S_X4, // PBX Med SSSS + TONE_CDMA_LOW_PBX_S_X4, // PBX LOW SSSS + //IS54 end + // proprietary + TONE_CDMA_ALERT_NETWORK_LITE, + TONE_CDMA_ALERT_AUTOREDIAL_LITE, + TONE_CDMA_ONE_MIN_BEEP, + TONE_CDMA_KEYPAD_VOLUME_KEY_LITE, + TONE_CDMA_PRESSHOLDKEY_LITE, + TONE_CDMA_ALERT_INCALL_LITE, + TONE_CDMA_EMERGENCY_RINGBACK, + TONE_CDMA_ALERT_CALL_GUARD, + TONE_CDMA_SOFT_ERROR_LITE, + TONE_CDMA_CALLDROP_LITE, + // proprietary end + TONE_CDMA_NETWORK_BUSY_ONE_SHOT, + TONE_CDMA_ABBR_ALERT, + TONE_CDMA_SIGNAL_OFF, + //CDMA end NUM_TONES, NUM_SUP_TONES = LAST_SUP_TONE-FIRST_SUP_TONE+1 }; @@ -125,7 +201,7 @@ private: static const unsigned char sToneMappingTable[NUM_REGIONS-1][NUM_SUP_TONES]; static const unsigned int TONEGEN_MAX_WAVES = 3; // Maximun number of sine waves in a tone segment - static const unsigned int TONEGEN_MAX_SEGMENTS = 5; // Maximun number of segments in a tone descriptor + static const unsigned int TONEGEN_MAX_SEGMENTS = 12; // Maximun number of segments in a tone descriptor static const unsigned int TONEGEN_INF = 0xFFFFFFFF; // Represents infinite time duration static const float TONEGEN_GAIN = 0.9; // Default gain passed to WaveGenerator(). @@ -140,6 +216,8 @@ private: // correspond to tone ON state and segments with odd index to OFF state. // The data stored in segments[] is the duration of the corresponding period in ms. // The first segment encountered with a 0 duration indicates that no more segment follows. + // - loopCnt - Number of times to repeat a sequence of seqments after playing this + // - loopIndx - The segment index to go back and play is loopcnt > 0 // - repeatCnt indicates the number of times the sequence described by segments[] array must be repeated. // When the tone generator encounters the first 0 duration segment, it will compare repeatCnt to mCurCount. // If mCurCount > repeatCnt, the tone is stopped automatically. Otherwise, tone sequence will be @@ -150,6 +228,8 @@ private: public: unsigned int duration; unsigned short waveFreq[TONEGEN_MAX_WAVES+1]; + unsigned short loopCnt; + unsigned short loopIndx; }; class ToneDescriptor { @@ -174,6 +254,8 @@ private: const ToneDescriptor *mpToneDesc; // pointer to active tone descriptor const ToneDescriptor *mpNewToneDesc; // pointer to next active tone descriptor + unsigned short mLoopCounter; // Current tone loopback count + int mSamplingRate; // AudioFlinger Sampling rate AudioTrack *mpAudioTrack; // Pointer to audio track used for playback Mutex mLock; // Mutex to control concurent access to ToneGenerator object from audio callback and application API diff --git a/include/tts/TtsEngine.h b/include/tts/TtsEngine.h index 8486532..ca50a5e 100644 --- a/include/tts/TtsEngine.h +++ b/include/tts/TtsEngine.h @@ -69,6 +69,14 @@ enum tts_result { TTS_MISSING_RESOURCES = -6 }; +enum tts_support_result { + TTS_LANG_COUNTRY_VAR_AVAILABLE = 2, + TTS_LANG_COUNTRY_AVAILABLE = 1, + TTS_LANG_AVAILABLE = 0, + TTS_LANG_MISSING_DATA = -1, + TTS_LANG_NOT_SUPPORTED = -2 +}; + class TtsEngine { public: @@ -86,19 +94,32 @@ public: // @return TTS_SUCCESS, or TTS_FAILURE virtual tts_result stop(); + // Returns the level of support for the language, country and variant. + // @return TTS_LANG_COUNTRY_VAR_AVAILABLE if the language, country and variant are supported, + // and the corresponding resources are correctly installed + // TTS_LANG_COUNTRY_AVAILABLE if the language and country are supported and the + // corresponding resources are correctly installed, but there is no match for + // the specified variant + // TTS_LANG_AVAILABLE if the language is supported and the + // corresponding resources are correctly installed, but there is no match for + // the specified country and variant + // TTS_LANG_MISSING_DATA if the required resources to provide any level of support + // for the language are not correctly installed + // TTS_LANG_NOT_SUPPORTED if the language is not supported by the TTS engine. + virtual tts_support_result isLanguageAvailable(const char *lang, const char *country, + const char *variant); + // Load the resources associated with the specified language. The loaded // language will only be used once a call to setLanguage() with the same - // language value is issued. Language values are based on the Android - // conventions for localization as described in the Android platform - // documentation on internationalization. This implies that language - // data is specified in the format xx-rYY, where xx is a two letter - // ISO 639-1 language code in lowercase and rYY is a two letter - // ISO 3166-1-alpha-2 language code in uppercase preceded by a - // lowercase "r". - // @param value pointer to the language value - // @param size length of the language value + // language value is issued. Language and country values are coded according to the ISO three + // letter codes for languages and countries, as can be retrieved from a java.util.Locale + // instance. The variant value is encoded as the variant string retrieved from a + // java.util.Locale instance built with that variant data. + // @param lang pointer to the ISO three letter code for the language + // @param country pointer to the ISO three letter code for the country + // @param variant pointer to the variant code // @return TTS_SUCCESS, or TTS_FAILURE - virtual tts_result loadLanguage(const char *value, const size_t size); + virtual tts_result loadLanguage(const char *lang, const char *country, const char *variant); // Load the resources associated with the specified language, country and Locale variant. // The loaded language will only be used once a call to setLanguageFromLocale() with the same diff --git a/include/ui/Camera.h b/include/ui/Camera.h index 048bdd5..97e0e90 100644 --- a/include/ui/Camera.h +++ b/include/ui/Camera.h @@ -63,16 +63,12 @@ namespace android { #define FRAME_CALLBACK_FLAG_CAMERA 0x05 #define FRAME_CALLBACK_FLAG_BARCODE_SCANNER 0x07 -// msgType in notifyCallback function +// msgType in notifyCallback and dataCallback functions enum { - CAMERA_MSG_ERROR, + CAMERA_MSG_ERROR = 0, CAMERA_MSG_SHUTTER, CAMERA_MSG_FOCUS, - CAMERA_MSG_ZOOM -}; - -// msgType in dataCallback function -enum { + CAMERA_MSG_ZOOM, CAMERA_MSG_PREVIEW_FRAME, CAMERA_MSG_VIDEO_FRAME, CAMERA_MSG_POSTVIEW_FRAME, @@ -86,10 +82,13 @@ class Surface; class Mutex; class String8; -typedef void (*shutter_callback)(void *cookie); -typedef void (*frame_callback)(const sp<IMemory>& mem, void *cookie); -typedef void (*autofocus_callback)(bool focused, void *cookie); -typedef void (*error_callback)(status_t err, void *cookie); +// ref-counted object for callbacks +class CameraListener: virtual public RefBase +{ +public: + virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2) = 0; + virtual void postData(int32_t msgType, const sp<IMemory>& dataPtr) = 0; +}; class Camera : public BnCameraClient, public IBinder::DeathRecipient { @@ -144,13 +143,8 @@ public: // get preview/capture parameters - key/value pairs String8 getParameters() const; - void setShutterCallback(shutter_callback cb, void *cookie); - void setRawCallback(frame_callback cb, void *cookie); - void setJpegCallback(frame_callback cb, void *cookie); - void setRecordingCallback(frame_callback cb, void *cookie); - void setPreviewCallback(frame_callback cb, void *cookie, int preview_callback_flag = FRAME_CALLBACK_FLAG_NOOP); - void setErrorCallback(error_callback cb, void *cookie); - void setAutoFocusCallback(autofocus_callback cb, void *cookie); + void setListener(const sp<CameraListener>& listener); + void setPreviewCallbackFlags(int preview_callback_flag); // ICameraClient interface virtual void notifyCallback(int32_t msgType, int32_t ext, int32_t ext2); @@ -160,6 +154,8 @@ public: private: Camera(); + Camera(const Camera&); + Camera& operator=(const Camera); virtual void binderDied(const wp<IBinder>& who); class DeathNotifier: public IBinder::DeathRecipient @@ -179,20 +175,7 @@ private: sp<ICamera> mCamera; status_t mStatus; - shutter_callback mShutterCallback; - void *mShutterCallbackCookie; - frame_callback mRawCallback; - void *mRawCallbackCookie; - frame_callback mJpegCallback; - void *mJpegCallbackCookie; - frame_callback mPreviewCallback; - void *mPreviewCallbackCookie; - frame_callback mRecordingCallback; - void *mRecordingCallbackCookie; - error_callback mErrorCallback; - void *mErrorCallbackCookie; - autofocus_callback mAutoFocusCallback; - void *mAutoFocusCallbackCookie; + sp<CameraListener> mListener; friend class DeathNotifier; diff --git a/include/utils/BackupHelpers.h b/include/utils/BackupHelpers.h index a21359f..b1f5045 100644 --- a/include/utils/BackupHelpers.h +++ b/include/utils/BackupHelpers.h @@ -43,6 +43,7 @@ struct SnapshotHeader { struct FileState { int modTime_sec; int modTime_nsec; + int mode; int size; int crc32; int nameLen; @@ -136,6 +137,7 @@ public: private: void* m_buf; + bool m_loggedUnknownMetadata; KeyedVector<String8,FileRec> m_files; }; diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index f1029b7..5c41ead 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -866,7 +866,7 @@ struct ResTable_config uint8_t keyboard; uint8_t navigation; uint8_t inputFlags; - uint8_t pad0; + uint8_t inputPad0; }; uint32_t input; }; @@ -905,6 +905,23 @@ struct ResTable_config uint32_t version; }; + enum { + SCREENLAYOUT_ANY = 0x0000, + SCREENLAYOUT_SMALL = 0x0001, + SCREENLAYOUT_NORMAL = 0x0002, + SCREENLAYOUT_LARGE = 0x0003, + }; + + union { + struct { + uint8_t screenLayout; + uint8_t screenConfigPad0; + uint8_t screenConfigPad1; + uint8_t screenConfigPad2; + }; + uint32_t screenConfig; + }; + inline void copyFromDeviceNoSwap(const ResTable_config& o) { const size_t size = dtohl(o.size); if (size >= sizeof(ResTable_config)) { @@ -950,6 +967,8 @@ struct ResTable_config diff = (int32_t)(screenSize - o.screenSize); if (diff != 0) return diff; diff = (int32_t)(version - o.version); + if (diff != 0) return diff; + diff = (int32_t)(screenLayout - o.screenLayout); return (int)diff; } @@ -967,7 +986,8 @@ struct ResTable_config CONFIG_ORIENTATION = 0x0080, CONFIG_DENSITY = 0x0100, CONFIG_SCREEN_SIZE = 0x0200, - CONFIG_VERSION = 0x0400 + CONFIG_VERSION = 0x0400, + CONFIG_SCREEN_LAYOUT = 0x0800 }; // Compare two configuration, returning CONFIG_* flags set for each value @@ -985,6 +1005,7 @@ struct ResTable_config if (navigation != o.navigation) diffs |= CONFIG_NAVIGATION; if (screenSize != o.screenSize) diffs |= CONFIG_SCREEN_SIZE; if (version != o.version) diffs |= CONFIG_VERSION; + if (screenLayout != o.screenLayout) diffs |= CONFIG_SCREEN_LAYOUT; return diffs; } @@ -1062,6 +1083,13 @@ struct ResTable_config } } + if (screenConfig || o.screenConfig) { + if (screenLayout != o.screenLayout) { + if (!screenLayout) return false; + if (!o.screenLayout) return true; + } + } + if (version || o.version) { if (sdkVersion != o.sdkVersion) { if (!sdkVersion) return false; @@ -1191,6 +1219,12 @@ struct ResTable_config } } + if (screenConfig || o.screenConfig) { + if ((screenLayout != o.screenLayout) && requested->screenLayout) { + return (screenLayout); + } + } + if (version || o.version) { if ((sdkVersion != o.sdkVersion) && requested->sdkVersion) { return (sdkVersion); @@ -1282,6 +1316,12 @@ struct ResTable_config return false; } } + if (screenConfig != 0) { + if (settings.screenLayout != 0 && screenLayout != 0 + && screenLayout != settings.screenLayout) { + return false; + } + } if (version != 0) { if (settings.sdkVersion != 0 && sdkVersion != 0 && sdkVersion != settings.sdkVersion) { @@ -1310,13 +1350,13 @@ struct ResTable_config String8 toString() const { char buf[200]; - sprintf(buf, "imsi=%d/%d lang=%c%c reg=%c%c orient=0x%02x touch=0x%02x dens=0x%02x " - "kbd=0x%02x nav=0x%02x input=0x%02x screenW=0x%04x screenH=0x%04x vers=%d.%d", + sprintf(buf, "imsi=%d/%d lang=%c%c reg=%c%c orient=%d touch=%d dens=%d " + "kbd=%d nav=%d input=%d scrnW=%d scrnH=%d layout=%d vers=%d.%d", mcc, mnc, language[0] ? language[0] : '-', language[1] ? language[1] : '-', country[0] ? country[0] : '-', country[1] ? country[1] : '-', orientation, touchscreen, density, keyboard, navigation, inputFlags, - screenWidth, screenHeight, sdkVersion, minorVersion); + screenWidth, screenHeight, screenLayout, sdkVersion, minorVersion); return String8(buf); } }; diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk index b3b2104..7bbe38b 100644 --- a/libs/ui/Android.mk +++ b/libs/ui/Android.mk @@ -24,8 +24,7 @@ LOCAL_SRC_FILES:= \ Region.cpp \ Surface.cpp \ SurfaceComposerClient.cpp \ - SurfaceFlingerSynchro.cpp \ - Time.cpp + SurfaceFlingerSynchro.cpp LOCAL_SHARED_LIBRARIES := \ libcorecg \ diff --git a/libs/ui/Camera.cpp b/libs/ui/Camera.cpp index 6613700..bb22dab 100644 --- a/libs/ui/Camera.cpp +++ b/libs/ui/Camera.cpp @@ -85,20 +85,6 @@ sp<Camera> Camera::create(const sp<ICamera>& camera) void Camera::init() { mStatus = UNKNOWN_ERROR; - mShutterCallback = 0; - mShutterCallbackCookie = 0; - mRawCallback = 0; - mRawCallbackCookie = 0; - mJpegCallback = 0; - mJpegCallbackCookie = 0; - mPreviewCallback = 0; - mPreviewCallbackCookie = 0; - mRecordingCallback = 0; - mRecordingCallbackCookie = 0; - mErrorCallback = 0; - mErrorCallbackCookie = 0; - mAutoFocusCallback = 0; - mAutoFocusCallbackCookie = 0; } Camera::~Camera() @@ -127,7 +113,6 @@ void Camera::disconnect() { LOGV("disconnect"); if (mCamera != 0) { - mErrorCallback = 0; mCamera->disconnect(); mCamera = 0; } @@ -285,125 +270,49 @@ String8 Camera::getParameters() const return params; } -void Camera::setAutoFocusCallback(autofocus_callback cb, void *cookie) +void Camera::setListener(const sp<CameraListener>& listener) { - LOGV("setAutoFocusCallback"); - mAutoFocusCallback = cb; - mAutoFocusCallbackCookie = cookie; -} - -void Camera::setShutterCallback(shutter_callback cb, void *cookie) -{ - LOGV("setShutterCallback"); - mShutterCallback = cb; - mShutterCallbackCookie = cookie; -} - -void Camera::setRawCallback(frame_callback cb, void *cookie) -{ - LOGV("setRawCallback"); - mRawCallback = cb; - mRawCallbackCookie = cookie; -} - -void Camera::setJpegCallback(frame_callback cb, void *cookie) -{ - LOGV("setJpegCallback"); - mJpegCallback = cb; - mJpegCallbackCookie = cookie; + Mutex::Autolock _l(mLock); + mListener = listener; } -void Camera::setPreviewCallback(frame_callback cb, void *cookie, int flag) +void Camera::setPreviewCallbackFlags(int flag) { - LOGV("setPreviewCallback"); - mPreviewCallback = cb; - mPreviewCallbackCookie = cookie; + LOGV("setPreviewCallbackFlags"); sp <ICamera> c = mCamera; if (c == 0) return; mCamera->setPreviewCallbackFlag(flag); } -void Camera::setRecordingCallback(frame_callback cb, void *cookie) -{ - LOGV("setRecordingCallback"); - mRecordingCallback = cb; - mRecordingCallbackCookie = cookie; -} - -void Camera::setErrorCallback(error_callback cb, void *cookie) -{ - LOGV("setErrorCallback"); - mErrorCallback = cb; - mErrorCallbackCookie = cookie; -} - // callback from camera service void Camera::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) { - switch(msgType) { - case CAMERA_MSG_ERROR: - LOGV("errorCallback"); - if (mErrorCallback) { - mErrorCallback((status_t)ext1, mErrorCallbackCookie); - } - break; - case CAMERA_MSG_FOCUS: - LOGV("autoFocusCallback"); - if (mAutoFocusCallback) { - mAutoFocusCallback((bool)ext1, mAutoFocusCallbackCookie); - } - break; - case CAMERA_MSG_SHUTTER: - LOGV("shutterCallback"); - if (mShutterCallback) { - mShutterCallback(mShutterCallbackCookie); - } - break; - default: - LOGV("notifyCallback(%d, %d, %d)", msgType, ext1, ext2); - break; + sp<CameraListener> listener; + { + Mutex::Autolock _l(mLock); + listener = mListener; + } + if (listener != NULL) { + listener->notify(msgType, ext1, ext2); } } // callback from camera service when frame or image is ready void Camera::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr) { - switch(msgType) { - case CAMERA_MSG_PREVIEW_FRAME: - LOGV("previewCallback"); - if (mPreviewCallback) { - mPreviewCallback(dataPtr, mPreviewCallbackCookie); - } - break; - case CAMERA_MSG_VIDEO_FRAME: - LOGV("recordingCallback"); - if (mRecordingCallback) { - mRecordingCallback(dataPtr, mRecordingCallbackCookie); - } - break; - case CAMERA_MSG_RAW_IMAGE: - LOGV("rawCallback"); - if (mRawCallback) { - mRawCallback(dataPtr, mRawCallbackCookie); - } - break; - case CAMERA_MSG_COMPRESSED_IMAGE: - LOGV("jpegCallback"); - if (mJpegCallback) { - mJpegCallback(dataPtr, mJpegCallbackCookie); - } - break; - default: - LOGV("dataCallback(%d, %p)", msgType, dataPtr.get()); - break; + sp<CameraListener> listener; + { + Mutex::Autolock _l(mLock); + listener = mListener; + } + if (listener != NULL) { + listener->postData(msgType, dataPtr); } } void Camera::binderDied(const wp<IBinder>& who) { LOGW("ICamera died"); - if (mErrorCallback) { - mErrorCallback(DEAD_OBJECT, mErrorCallbackCookie); - } + notifyCallback(CAMERA_MSG_ERROR, DEAD_OBJECT, 0); } void Camera::DeathNotifier::binderDied(const wp<IBinder>& who) { diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp index 0868cff..cce754a 100644 --- a/libs/utils/BackupData.cpp +++ b/libs/utils/BackupData.cpp @@ -107,7 +107,10 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) } else { k = key; } - LOGD("m_keyPrefix=%s key=%s k=%s", m_keyPrefix.string(), key.string(), k.string()); + if (true) { + LOGD("Writing entity: prefix='%s' key='%s' dataSize=%d", m_keyPrefix.string(), key.string(), + dataSize); + } entity_header_v1 header; ssize_t keyLen; diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp index d65a457..4ad9b51 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -41,11 +41,42 @@ namespace android { #define MAGIC0 0x70616e53 // Snap #define MAGIC1 0x656c6946 // File -#if 1 // TEST_BACKUP_HELPERS +/* + * File entity data format (v1): + * + * - 4-byte version number of the metadata, little endian (0x00000001 for v1) + * - 12 bytes of metadata + * - the file data itself + * + * i.e. a 16-byte metadata header followed by the raw file data. If the + * restore code does not recognize the metadata version, it can still + * interpret the file data itself correctly. + * + * file_metadata_v1: + * + * - 4 byte version number === 0x00000001 (little endian) + * - 4-byte access mode (little-endian) + * - undefined (8 bytes) + */ + +struct file_metadata_v1 { + int version; + int mode; + int undefined_1; + int undefined_2; +}; + +const static int CURRENT_METADATA_VERSION = 1; + +#if 1 +#define LOGP(f, x...) +#else +#if TEST_BACKUP_HELPERS #define LOGP(f, x...) printf(f "\n", x) #else #define LOGP(x...) LOGD(x) #endif +#endif const static int ROUND_UP[4] = { 0, 3, 2, 1 }; @@ -181,29 +212,48 @@ write_delete_file(BackupDataWriter* dataStream, const String8& key) } static int -write_update_file(BackupDataWriter* dataStream, int fd, const String8& key, +write_update_file(BackupDataWriter* dataStream, int fd, int mode, const String8& key, char const* realFilename) { - LOGP("write_update_file %s (%s)\n", realFilename, key.string()); + LOGP("write_update_file %s (%s) : mode 0%o\n", realFilename, key.string(), mode); const int bufsize = 4*1024; int err; int amt; int fileSize; int bytesLeft; + file_metadata_v1 metadata; char* buf = (char*)malloc(bufsize); int crc = crc32(0L, Z_NULL, 0); - bytesLeft = fileSize = lseek(fd, 0, SEEK_END); + fileSize = lseek(fd, 0, SEEK_END); lseek(fd, 0, SEEK_SET); + if (sizeof(metadata) != 16) { + LOGE("ERROR: metadata block is the wrong size!"); + } + + bytesLeft = fileSize + sizeof(metadata); err = dataStream->WriteEntityHeader(key, bytesLeft); if (err != 0) { + free(buf); return err; } + // store the file metadata first + metadata.version = tolel(CURRENT_METADATA_VERSION); + metadata.mode = tolel(mode); + metadata.undefined_1 = metadata.undefined_2 = 0; + err = dataStream->WriteEntityData(&metadata, sizeof(metadata)); + if (err != 0) { + free(buf); + return err; + } + bytesLeft -= sizeof(metadata); // bytesLeft should == fileSize now + + // now store the file content while ((amt = read(fd, buf, bufsize)) != 0 && bytesLeft > 0) { bytesLeft -= amt; if (bytesLeft < 0) { @@ -211,6 +261,7 @@ write_update_file(BackupDataWriter* dataStream, int fd, const String8& key, } err = dataStream->WriteEntityData(buf, amt); if (err != 0) { + free(buf); return err; } } @@ -224,6 +275,7 @@ write_update_file(BackupDataWriter* dataStream, int fd, const String8& key, bytesLeft -= amt; err = dataStream->WriteEntityData(buf, amt); if (err != 0) { + free(buf); return err; } } @@ -233,7 +285,6 @@ write_update_file(BackupDataWriter* dataStream, int fd, const String8& key, } free(buf); - return NO_ERROR; } @@ -241,11 +292,19 @@ static int write_update_file(BackupDataWriter* dataStream, const String8& key, char const* realFilename) { int err; + struct stat st; + + err = stat(realFilename, &st); + if (err < 0) { + return errno; + } + int fd = open(realFilename, O_RDONLY); if (fd == -1) { return errno; } - err = write_update_file(dataStream, fd, key, realFilename); + + err = write_update_file(dataStream, fd, st.st_mode, key, realFilename); close(fd); return err; } @@ -266,7 +325,6 @@ compute_crc32(int fd) } free(buf); - return crc; } @@ -295,13 +353,13 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD err = stat(file, &st); if (err != 0) { - LOGW("Error stating file %s", file); r.deleted = true; } else { r.deleted = false; r.s.modTime_sec = st.st_mtime; r.s.modTime_nsec = 0; // workaround sim breakage //r.s.modTime_nsec = st.st_mtime_nsec; + r.s.mode = st.st_mode; r.s.size = st.st_size; // we compute the crc32 later down below, when we already have the file open. @@ -349,13 +407,13 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD g.s.crc32 = compute_crc32(fd); LOGP("%s", q.string()); - LOGP(" new: modTime=%d,%d size=%-3d crc32=0x%08x", - f.modTime_sec, f.modTime_nsec, f.size, f.crc32); - LOGP(" old: modTime=%d,%d size=%-3d crc32=0x%08x", - g.s.modTime_sec, g.s.modTime_nsec, g.s.size, g.s.crc32); + LOGP(" new: modTime=%d,%d mode=%04o size=%-3d crc32=0x%08x", + f.modTime_sec, f.modTime_nsec, f.mode, f.size, f.crc32); + LOGP(" old: modTime=%d,%d mode=%04o size=%-3d crc32=0x%08x", + g.s.modTime_sec, g.s.modTime_nsec, g.s.mode, g.s.size, g.s.crc32); if (f.modTime_sec != g.s.modTime_sec || f.modTime_nsec != g.s.modTime_nsec - || f.size != g.s.size || f.crc32 != g.s.crc32) { - write_update_file(dataStream, fd, p, g.file.string()); + || f.mode != g.s.mode || f.size != g.s.size || f.crc32 != g.s.crc32) { + write_update_file(dataStream, fd, g.s.mode, p, g.file.string()); } close(fd); @@ -389,6 +447,7 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD RestoreHelperBase::RestoreHelperBase() { m_buf = malloc(RESTORE_BUF_SIZE); + m_loggedUnknownMetadata = false; } RestoreHelperBase::~RestoreHelperBase() @@ -415,8 +474,25 @@ RestoreHelperBase::WriteFile(const String8& filename, BackupDataReader* in) return err; } - // TODO: World readable/writable for now. - mode = 0666; + // Get the metadata block off the head of the file entity and use that to + // set up the output file + file_metadata_v1 metadata; + amt = in->ReadEntityData(&metadata, sizeof(metadata)); + if (amt != sizeof(metadata)) { + LOGW("Could not read metadata for %s -- %ld / %s", filename.string(), + (long)amt, strerror(errno)); + return EIO; + } + metadata.version = fromlel(metadata.version); + metadata.mode = fromlel(metadata.mode); + if (metadata.version > CURRENT_METADATA_VERSION) { + if (!m_loggedUnknownMetadata) { + m_loggedUnknownMetadata = true; + LOGW("Restoring file with unsupported metadata version %d (currently %d)", + metadata.version, CURRENT_METADATA_VERSION); + } + } + mode = metadata.mode; // Write the file and compute the crc crc = crc32(0L, Z_NULL, 0); @@ -450,6 +526,7 @@ RestoreHelperBase::WriteFile(const String8& filename, BackupDataReader* in) r.s.modTime_sec = st.st_mtime; r.s.modTime_nsec = 0; // workaround sim breakage //r.s.modTime_nsec = st.st_mtime_nsec; + r.s.mode = st.st_mode; r.s.size = st.st_size; r.s.crc32 = crc; @@ -536,6 +613,7 @@ compare_file(const char* path, const unsigned char* data, int len) } } + free(contents); return contentsMatch && sizesMatch ? 0 : 1; } @@ -623,6 +701,7 @@ backup_helper_test_four() states[0].modTime_sec = 0xfedcba98; states[0].modTime_nsec = 0xdeadbeef; + states[0].mode = 0777; // decimal 511, hex 0x000001ff states[0].size = 0xababbcbc; states[0].crc32 = 0x12345678; states[0].nameLen = -12; @@ -632,6 +711,7 @@ backup_helper_test_four() states[1].modTime_sec = 0x93400031; states[1].modTime_nsec = 0xdeadbeef; + states[1].mode = 0666; // decimal 438, hex 0x000001b6 states[1].size = 0x88557766; states[1].crc32 = 0x22334422; states[1].nameLen = -1; @@ -641,6 +721,7 @@ backup_helper_test_four() states[2].modTime_sec = 0x33221144; states[2].modTime_nsec = 0xdeadbeef; + states[2].mode = 0744; // decimal 484, hex 0x000001e4 states[2].size = 0x11223344; states[2].crc32 = 0x01122334; states[2].nameLen = 0; @@ -650,6 +731,7 @@ backup_helper_test_four() states[3].modTime_sec = 0x33221144; states[3].modTime_nsec = 0xdeadbeef; + states[3].mode = 0755; // decimal 493, hex 0x000001ed states[3].size = 0x11223344; states[3].crc32 = 0x01122334; states[3].nameLen = 0; @@ -669,35 +751,38 @@ backup_helper_test_four() static const unsigned char correct_data[] = { // header 0x53, 0x6e, 0x61, 0x70, 0x04, 0x00, 0x00, 0x00, - 0x46, 0x69, 0x6c, 0x65, 0xac, 0x00, 0x00, 0x00, + 0x46, 0x69, 0x6c, 0x65, 0xbc, 0x00, 0x00, 0x00, // bytes_of_padding 0x98, 0xba, 0xdc, 0xfe, 0xef, 0xbe, 0xad, 0xde, - 0xbc, 0xbc, 0xab, 0xab, 0x78, 0x56, 0x34, 0x12, - 0x10, 0x00, 0x00, 0x00, 0x62, 0x79, 0x74, 0x65, - 0x73, 0x5f, 0x6f, 0x66, 0x5f, 0x70, 0x61, 0x64, - 0x64, 0x69, 0x6e, 0x67, + 0xff, 0x01, 0x00, 0x00, 0xbc, 0xbc, 0xab, 0xab, + 0x78, 0x56, 0x34, 0x12, 0x10, 0x00, 0x00, 0x00, + 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x6f, 0x66, + 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, // bytes_of_padding3 0x31, 0x00, 0x40, 0x93, 0xef, 0xbe, 0xad, 0xde, - 0x66, 0x77, 0x55, 0x88, 0x22, 0x44, 0x33, 0x22, - 0x11, 0x00, 0x00, 0x00, 0x62, 0x79, 0x74, 0x65, - 0x73, 0x5f, 0x6f, 0x66, 0x5f, 0x70, 0x61, 0x64, - 0x64, 0x69, 0x6e, 0x67, 0x33, 0xab, 0xab, 0xab, + 0xb6, 0x01, 0x00, 0x00, 0x66, 0x77, 0x55, 0x88, + 0x22, 0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x00, + 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x6f, 0x66, + 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, + 0x33, 0xab, 0xab, 0xab, // bytes of padding2 0x44, 0x11, 0x22, 0x33, 0xef, 0xbe, 0xad, 0xde, - 0x44, 0x33, 0x22, 0x11, 0x34, 0x23, 0x12, 0x01, - 0x12, 0x00, 0x00, 0x00, 0x62, 0x79, 0x74, 0x65, - 0x73, 0x5f, 0x6f, 0x66, 0x5f, 0x70, 0x61, 0x64, - 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x32, 0xab, 0xab, + 0xe4, 0x01, 0x00, 0x00, 0x44, 0x33, 0x22, 0x11, + 0x34, 0x23, 0x12, 0x01, 0x12, 0x00, 0x00, 0x00, + 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x6f, 0x66, + 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, + 0x5f, 0x32, 0xab, 0xab, // bytes of padding3 0x44, 0x11, 0x22, 0x33, 0xef, 0xbe, 0xad, 0xde, - 0x44, 0x33, 0x22, 0x11, 0x34, 0x23, 0x12, 0x01, - 0x13, 0x00, 0x00, 0x00, 0x62, 0x79, 0x74, 0x65, - 0x73, 0x5f, 0x6f, 0x66, 0x5f, 0x70, 0x61, 0x64, - 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x5f, 0x31, 0xab + 0xed, 0x01, 0x00, 0x00, 0x44, 0x33, 0x22, 0x11, + 0x34, 0x23, 0x12, 0x01, 0x13, 0x00, 0x00, 0x00, + 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x6f, 0x66, + 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, + 0x5f, 0x5f, 0x31, 0xab }; err = compare_file(filename, correct_data, sizeof(correct_data)); @@ -731,14 +816,14 @@ backup_helper_test_four() const FileState state = readSnapshot.valueAt(i); if (name != filenames[i] || states[i].modTime_sec != state.modTime_sec - || states[i].modTime_nsec != state.modTime_nsec + || states[i].modTime_nsec != state.modTime_nsec || states[i].mode != state.mode || states[i].size != state.size || states[i].crc32 != states[i].crc32) { - fprintf(stderr, "state %d expected={%d/%d, 0x%08x, 0x%08x, %3d} '%s'\n" - " actual={%d/%d, 0x%08x, 0x%08x, %3d} '%s'\n", i, - states[i].modTime_sec, states[i].modTime_nsec, states[i].size, states[i].crc32, - name.length(), filenames[i].string(), - state.modTime_sec, state.modTime_nsec, state.size, state.crc32, state.nameLen, - name.string()); + fprintf(stderr, "state %d expected={%d/%d, 0x%08x, %04o, 0x%08x, %3d} '%s'\n" + " actual={%d/%d, 0x%08x, %04o, 0x%08x, %3d} '%s'\n", i, + states[i].modTime_sec, states[i].modTime_nsec, states[i].mode, states[i].size, + states[i].crc32, name.length(), filenames[i].string(), + state.modTime_sec, state.modTime_nsec, state.mode, state.size, state.crc32, + state.nameLen, name.string()); matched = false; } } @@ -839,6 +924,7 @@ test_read_header_and_entity(BackupDataReader& reader, const char* str) size_t actualSize; bool done; int type; + ssize_t nRead; // printf("\n\n---------- test_read_header_and_entity -- %s\n\n", str); @@ -873,8 +959,9 @@ test_read_header_and_entity(BackupDataReader& reader, const char* str) goto finished; } - err = reader.ReadEntityData(buf, bufSize); - if (err != NO_ERROR) { + nRead = reader.ReadEntityData(buf, bufSize); + if (nRead < 0) { + err = reader.Status(); fprintf(stderr, "ReadEntityData failed with %s\n", strerror(err)); goto finished; } diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index e4f9f0f..7a33220 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -3919,7 +3919,7 @@ void ResTable::print(bool inclValues) const printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type); continue; } - printf(" config %d lang=%c%c cnt=%c%c orien=%d touch=%d density=%d key=%d infl=%d nav=%d w=%d h=%d\n", + printf(" config %d lang=%c%c cnt=%c%c orien=%d touch=%d density=%d key=%d infl=%d nav=%d w=%d h=%d lyt=%d\n", (int)configIndex, type->config.language[0] ? type->config.language[0] : '-', type->config.language[1] ? type->config.language[1] : '-', @@ -3932,7 +3932,8 @@ void ResTable::print(bool inclValues) const type->config.inputFlags, type->config.navigation, dtohs(type->config.screenWidth), - dtohs(type->config.screenHeight)); + dtohs(type->config.screenHeight), + type->config.screenLayout); size_t entryCount = dtohl(type->entryCount); uint32_t entriesStart = dtohl(type->entriesStart); if ((entriesStart&0x3) != 0) { diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java index 5c8fcf2..edd1ea0 100755 --- a/location/java/com/android/internal/location/GpsLocationProvider.java +++ b/location/java/com/android/internal/location/GpsLocationProvider.java @@ -621,23 +621,37 @@ public class GpsLocationProvider extends ILocationProvider.Stub { } public void addListener(int uid) { - mClientUids.put(uid, 0); - if (mNavigating) { - try { - mBatteryStats.noteStartGps(uid); - } catch (RemoteException e) { - Log.w(TAG, "RemoteException in addListener"); + synchronized(mListeners) { + if (mClientUids.indexOfKey(uid) >= 0) { + // Shouldn't be here -- already have this uid. + Log.w(TAG, "Duplicate add listener for uid " + uid); + return; + } + mClientUids.put(uid, 0); + if (mNavigating) { + try { + mBatteryStats.noteStartGps(uid); + } catch (RemoteException e) { + Log.w(TAG, "RemoteException in addListener"); + } } } } public void removeListener(int uid) { - mClientUids.delete(uid); - if (mNavigating) { - try { - mBatteryStats.noteStopGps(uid); - } catch (RemoteException e) { - Log.w(TAG, "RemoteException in removeListener"); + synchronized(mListeners) { + if (mClientUids.indexOfKey(uid) < 0) { + // Shouldn't be here -- don't have this uid. + Log.w(TAG, "Unneeded remove listener for uid " + uid); + return; + } + mClientUids.delete(uid); + if (mNavigating) { + try { + mBatteryStats.noteStopGps(uid); + } catch (RemoteException e) { + Log.w(TAG, "RemoteException in removeListener"); + } } } } @@ -836,30 +850,33 @@ public class GpsLocationProvider extends ILocationProvider.Stub { private void reportStatus(int status) { if (VERBOSE) Log.v(TAG, "reportStatus status: " + status); - boolean wasNavigating = mNavigating; - mNavigating = (status == GPS_STATUS_SESSION_BEGIN); - - if (wasNavigating != mNavigating) { + synchronized(mListeners) { + boolean wasNavigating = mNavigating; + mNavigating = (status == GPS_STATUS_SESSION_BEGIN); + + if (wasNavigating == mNavigating) { + return; + } + if (mNavigating) { if (DEBUG) Log.d(TAG, "Acquiring wakelock"); mWakeLock.acquire(); } - synchronized(mListeners) { - int size = mListeners.size(); - for (int i = 0; i < size; i++) { - Listener listener = mListeners.get(i); - try { - if (mNavigating) { - listener.mListener.onGpsStarted(); - } else { - listener.mListener.onGpsStopped(); - } - } catch (RemoteException e) { - Log.w(TAG, "RemoteException in reportStatus"); - mListeners.remove(listener); - // adjust for size of list changing - size--; + + int size = mListeners.size(); + for (int i = 0; i < size; i++) { + Listener listener = mListeners.get(i); + try { + if (mNavigating) { + listener.mListener.onGpsStarted(); + } else { + listener.mListener.onGpsStopped(); } + } catch (RemoteException e) { + Log.w(TAG, "RemoteException in reportStatus"); + mListeners.remove(listener); + // adjust for size of list changing + size--; } } diff --git a/media/java/android/media/ToneGenerator.java b/media/java/android/media/ToneGenerator.java index 54ca6c4..e5ee9a3 100644 --- a/media/java/android/media/ToneGenerator.java +++ b/media/java/android/media/ToneGenerator.java @@ -50,12 +50,12 @@ public class ToneGenerator * * @see #ToneGenerator(int, int) */ - public static final int TONE_DTMF_2 = 2; - /** - * DTMF tone for key 3: 1477Hz, 697Hz, continuous - * - * @see #ToneGenerator(int, int) - */ + public static final int TONE_DTMF_2 = 2; + /** + * DTMF tone for key 3: 1477Hz, 697Hz, continuous + * + * @see #ToneGenerator(int, int) + */ public static final int TONE_DTMF_3 = 3; /** * DTMF tone for key 4: 1209Hz, 770Hz, continuous @@ -254,242 +254,474 @@ public class ToneGenerator * @see #ToneGenerator(int, int) */ public static final int TONE_SUP_PIP = 33; - - /** - * CDMA SPECIFIC TONES START + * CDMA Dial tone : 425Hz continuous + * + * @see #ToneGenerator(int, int) */ - - /** TODO(Moto): Change "Proprietary" below with an appropriate specification reference */ - + public static final int TONE_CDMA_DIAL_TONE_LITE = 34; /** - * Proprietary tone, general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON + * CDMA USA Ringback: 440Hz+480Hz 2s ON, 4000 OFF ... * * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_NETWORK_USA_RINGBACK = 35; + /** + * CDMA Intercept tone: 440Hz 250ms ON, 620Hz 250ms ON ... * - * @hide + * @see #ToneGenerator(int, int) */ - public static final int TONE_CDMA_DIAL_TONE_LITE = 34; - - /** - * Proprietary tone, general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON + public static final int TONE_CDMA_INTERCEPT = 36; + /** + * CDMA Abbr Intercept tone: 440Hz 250ms ON, 620Hz 250ms ON * * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_ABBR_INTERCEPT = 37; + /** + * CDMA Reorder tone: 480Hz+620Hz 250ms ON, 250ms OFF... * - * @hide + * @see #ToneGenerator(int, int) */ - public static final int TONE_CDMA_NETWORK_USA_RINGBACK = 35; - + public static final int TONE_CDMA_REORDER = 38; /** - * Proprietary tone, general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON * + * CDMA Abbr Reorder tone: 480Hz+620Hz 250ms ON, 250ms OFF repeated for 8 times * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_ABBR_REORDER = 39; + /** + * CDMA Network Busy tone: 480Hz+620Hz 500ms ON, 500ms OFF continuous * - * @hide + * @see #ToneGenerator(int, int) */ - public static final int TONE_CDMA_REORDER = 36; - - /** - * Proprietary tone, general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON + public static final int TONE_CDMA_NETWORK_BUSY = 40; + /** + * CDMA Confirm tone: 350Hz+440Hz 100ms ON, 100ms OFF repeated for 3 times * * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_CONFIRM = 41; + /** * - * @hide + * CDMA answer tone: silent tone - defintion Frequency 0, 0ms ON, 0ms OFF + * @see #ToneGenerator(int, int) */ - public static final int TONE_CDMA_ABBR_REORDER = 37; - - /** - * Proprietary tone, general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON + public static final int TONE_CDMA_ANSWER = 42; + /** * + * CDMA Network Callwaiting tone: 440Hz 300ms ON * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_NETWORK_CALLWAITING = 43; + /** + * CDMA PIP tone: 480Hz 100ms ON, 100ms OFF repeated for 4 times * - * @hide + * @see #ToneGenerator(int, int) */ - public static final int TONE_CDMA_NETWORK_BUSY = 38; - - - /** - * Proprietary tone, general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON + public static final int TONE_CDMA_PIP = 44; + /** + * ISDN Call Signal Normal tone: {2091Hz 32ms ON, 2556 64ms ON} 20 times, + * 2091 32ms ON, 2556 48ms ON, 4s OFF * * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_CALL_SIGNAL_ISDN_NORMAL = 45; + /** + * ISDN Call Signal Intergroup tone: {2091Hz 32ms ON, 2556 64ms ON} 8 times, + * 2091Hz 32ms ON, 400ms OFF, {2091Hz 32ms ON, 2556Hz 64ms ON} times, + * 2091Hz 32ms ON, 4s OFF. * - * @hide + * @see #ToneGenerator(int, int) */ - public static final int TONE_CDMA_ANSWER = 39; - - /** - * Proprietary tone, general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON + public static final int TONE_CDMA_CALL_SIGNAL_ISDN_INTERGROUP = 46; + /** + * ISDN Call Signal SP PRI tone:{2091Hz 32ms ON, 2556 64ms ON} 4 times + * 2091Hz 16ms ON, 200ms OFF, {2091Hz 32ms ON, 2556Hz 64ms ON} 4 times, + * 2091Hz 16ms ON, 200ms OFF * * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_CALL_SIGNAL_ISDN_SP_PRI = 47; + /** + * ISDN Call sign PAT3 tone: silent tone * - * @hide + * @see #ToneGenerator(int, int) */ - public static final int TONE_CDMA_NETWORK_CALLWAITING = 40; - - /** - * Proprietary tone, general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON + public static final int TONE_CDMA_CALL_SIGNAL_ISDN_PAT3 = 48; + /** + * ISDN Ping Ring tone: {2091Hz 32ms ON, 2556Hz 64ms ON} 5 times + * 2091Hz 20ms ON * * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_CALL_SIGNAL_ISDN_PING_RING = 49; + /** * - * @hide + * ISDN Pat5 tone: silent tone + * @see #ToneGenerator(int, int) */ - public static final int TONE_CDMA_PIP = 41; - - + public static final int TONE_CDMA_CALL_SIGNAL_ISDN_PAT5 = 50; /** - * Proprietary tone, general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON * + * ISDN Pat6 tone: silent tone * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_CALL_SIGNAL_ISDN_PAT6 = 51; + /** + * ISDN Pat7 tone: silent tone * - * @hide + * @see #ToneGenerator(int, int) */ - public static final int TONE_CDMA_CALL_SIGNAL_ISDN_NORMAL = 42; - + public static final int TONE_CDMA_CALL_SIGNAL_ISDN_PAT7 = 52; /** - * Proprietary tone, general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON + * TONE_CDMA_HIGH_L tone: {3700Hz 25ms, 4000Hz 25ms} 40 times + * 4000ms OFF, Repeat .... * * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_HIGH_L = 53; + /** + * TONE_CDMA_MED_L tone: {2600Hz 25ms, 2900Hz 25ms} 40 times + * 4000ms OFF, Repeat .... * - * @hide + * @see #ToneGenerator(int, int) */ - public static final int TONE_CDMA_CALL_SIGNAL_ISDN_INTERGROUP = 43; - - /** - * Proprietary tone, general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON + public static final int TONE_CDMA_MED_L = 54; + /** + * TONE_CDMA_LOW_L tone: {1300Hz 25ms, 1450Hz 25ms} 40 times, + * 4000ms OFF, Repeat .... * * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_LOW_L = 55; + /** + * CDMA HIGH SS tone: {3700Hz 25ms, 4000Hz 25ms} repeat 16 times, + * 400ms OFF, repeat .... * - * @hide + * @see #ToneGenerator(int, int) */ - public static final int TONE_CDMA_CALL_SIGNAL_SP_PRI = 44; - + public static final int TONE_CDMA_HIGH_SS = 56; /** - * Proprietary tone, general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON + * CDMA MED SS tone: {2600Hz 25ms, 2900Hz 25ms} repeat 16 times, + * 400ms OFF, repeat .... * * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_MED_SS = 57; + /** + * CDMA LOW SS tone: {1300z 25ms, 1450Hz 25ms} repeat 16 times, + * 400ms OFF, repeat .... * - * @hide + * @see #ToneGenerator(int, int) */ - public static final int TONE_CDMA_CALL_SIGNAL_ISDN_PAT3 = 45; - + public static final int TONE_CDMA_LOW_SS = 58; /** - * Proprietary tone, general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON + * CDMA HIGH SSL tone: {3700Hz 25ms, 4000Hz 25ms} 8 times, + * 200ms OFF, {3700Hz 25ms, 4000Hz 25ms} repeat 8 times, + * 200ms OFF, {3700Hz 25ms, 4000Hz 25ms} repeat 16 times, + * 4000ms OFF, repeat ... * * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_HIGH_SSL = 59; + /** + * CDMA MED SSL tone: {2600Hz 25ms, 2900Hz 25ms} 8 times, + * 200ms OFF, {2600Hz 25ms, 2900Hz 25ms} repeat 8 times, + * 200ms OFF, {2600Hz 25ms, 2900Hz 25ms} repeat 16 times, + * 4000ms OFF, repeat ... * - * @hide + * @see #ToneGenerator(int, int) */ - public static final int TONE_CDMA_CALL_SIGNAL_ISDN_RING_RING = 46; - + public static final int TONE_CDMA_MED_SSL = 60; /** - * Proprietary tone, general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON + * CDMA LOW SSL tone: {1300Hz 25ms, 1450Hz 25ms} 8 times, + * 200ms OFF, {1300Hz 25ms, 1450Hz 25ms} repeat 8 times, + * 200ms OFF, {1300Hz 25ms, 1450Hz 25ms} repeat 16 times, + * 4000ms OFF, repeat ... * * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_LOW_SSL = 61; + /** + * CDMA HIGH SS2 tone: {3700Hz 25ms, 4000Hz 25ms} 20 times, + * 1000ms OFF, {3700Hz 25ms, 4000Hz 25ms} 20 times, + * 3000ms OFF, repeat .... * - * @hide + * @see #ToneGenerator(int, int) */ - public static final int TONE_CDMA_CALL_SIGNAL_ISDN_PAT5 = 47; - + public static final int TONE_CDMA_HIGH_SS_2 = 62; /** - * general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON + * CDMA MED SS2 tone: {2600Hz 25ms, 2900Hz 25ms} 20 times, + * 1000ms OFF, {2600Hz 25ms, 2900Hz 25ms} 20 times, + * 3000ms OFF, repeat .... * * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_MED_SS_2 = 63; + /** + * CDMA LOW SS2 tone: {1300Hz 25ms, 1450Hz 25ms} 20 times, + * 1000ms OFF, {1300Hz 25ms, 1450Hz 25ms} 20 times, + * 3000ms OFF, repeat .... * - * @hide + * @see #ToneGenerator(int, int) */ - public static final int TONE_CDMA_CALL_SIGNAL_ISDN_PAT6 = 48; - + public static final int TONE_CDMA_LOW_SS_2 = 64; /** - * general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON + * CDMA HIGH SLS tone: {3700Hz 25ms, 4000Hz 25ms} 10 times, + * 500ms OFF, {3700Hz 25ms, 4000Hz 25ms} 20 times, 500ms OFF, + * {3700Hz 25ms, 4000Hz 25ms} 10 times, 3000ms OFF, REPEAT * * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_HIGH_SLS = 65; + /** + * CDMA MED SLS tone: {2600Hz 25ms, 2900Hz 25ms} 10 times, + * 500ms OFF, {2600Hz 25ms, 2900Hz 25ms} 20 times, 500ms OFF, + * {2600Hz 25ms, 2900Hz 25ms} 10 times, 3000ms OFF, REPEAT * - * @hide + * + * @see #ToneGenerator(int, int) */ - public static final int TONE_CDMA_CALL_SIGNAL_ISDN_PAT7 = 49; - - // TODO(Moto): Need comments for each one and we need ToneGenerator.cpp/ToneGenerator.h - - /** @hide */ - public static final int TONE_CDMA_HIGH_L = 50; - - /** @hide */ - public static final int TONE_CDMA_LOW_L = 51; - /** @hide */ - public static final int TONE_CDMA_HIGH_SS = 52; - /** @hide */ - public static final int TONE_CDMA_MED_SS = 53; - /** @hide */ - public static final int TONE_CDMA_LOW_SS = 54; - /** @hide */ - public static final int TONE_CDMA_HIGH_SSL = 55; - - - /** @hide */ - public static final int TONE_CDMA_MED_SSL = 56; - /** @hide */ - public static final int TONE_CDMA_LOW_SSL = 57; - /** @hide */ - public static final int TONE_CDMA_HIGH_SS_2 = 58; - /** @hide */ - public static final int TONE_CDMA_MED_SS_2 = 59; - /** @hide */ - public static final int TONE_CDMA_LOW_SS_2 = 60; - /** @hide */ - public static final int TONE_CDMA_HIGH_SLS = 61; - /** @hide */ - public static final int TONE_CDMA_MED_SLS = 62; - /** @hide */ - public static final int TONE_CDMA_LOW_SLS = 63; - /** @hide */ - public static final int TONE_CDMA_HIGH_S_X4 = 64; - /** @hide */ - public static final int TONE_CDMA_MED_S_X4 = 65; - /** @hide */ - public static final int TONE_CDMA_LOW_S_X4 = 66; - /** @hide */ - public static final int TONE_CDMA_HIGH_PBX_L = 67; - /** @hide */ - public static final int TONE_CDMA_MED_PBX_L = 68; - /** @hide */ - public static final int TONE_CDMA_LOW_PBX_L = 69; - /** @hide */ - public static final int TONE_CDMA_HIGH_PBX_SS = 70; - /** @hide */ - public static final int TONE_CDMA_MED_PBX_SS = 71; - /** @hide */ - public static final int TONE_CDMA_LOW_PBX_SS = 72; - /** @hide */ - public static final int TONE_CDMA_HIGH_PBX_SSL = 73; - /** @hide */ - public static final int TONE_CDMA_MED_PBX_SSL = 74; - - /** @hide */ - public static final int TONE_CDMA_LOW_PBX_SSL = 75; - /** @hide */ - public static final int TONE_CDMA_HIGH_PBX_SLS = 76; - /** @hide */ - public static final int TONE_CDMA_MED_PBX_SLS = 77; - /** @hide */ - public static final int TONE_CDMA_LOW_PBX_SLS = 78; - /** @hide */ - public static final int TONE_CDMA_HIGH_PBX_S_X4 = 79; - /** @hide */ - public static final int TONE_CDMA_MED_PBX_S_X4 = 80; - /** @hide */ - public static final int TONE_CDMA_LOW_PBX_S_X4 = 81; - /** @hide */ - public static final int TONE_CDMA_INTERCEPT_ONE_SHOT = TONE_SUP_INTERCEPT_ABBREV; - /** @hide */ - public static final int TONE_CDMA_REORDER_ONE_SHOT = TONE_CDMA_ABBR_REORDER; - /** @hide */ - public static final int TONE_CDMA_NETWORK_BUSY_ONE_SHOT = 82; - /** @hide */ - public static final int TONE_CDMA_ABBR_ALERT = 83; - /** @hide */ - public static final int TONE_CDMA_SIGNAL_OFF = 84; - /** @hide */ - public static final int TONE_CDMA_INVALID = 85; + public static final int TONE_CDMA_MED_SLS = 66; + /** + * CDMA LOW SLS tone: {1300Hz 25ms, 1450Hz 25ms} 10 times, + * 500ms OFF, {1300Hz 25ms, 1450Hz 25ms} 20 times, 500ms OFF, + * {1300Hz 25ms, 1450Hz 25ms} 10 times, 3000ms OFF, REPEAT + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_LOW_SLS = 67; + /** + * CDMA HIGH S X4 tone: {3700Hz 25ms, 4000Hz 25ms} 10 times, + * 500ms OFF, {3700Hz 25ms, 4000Hz 25ms} 10 times, 500ms OFF, + * {3700Hz 25ms, 4000Hz 25ms} 10 times, 500ms OFF, + * {3700Hz 25ms, 4000Hz 25ms} 10 times, 2500ms OFF, REPEAT.... + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_HIGH_S_X4 = 68; + /** + * CDMA MED S X4 tone: {2600Hz 25ms, 2900Hz 25ms} 10 times, + * 500ms OFF, {2600Hz 25ms, 2900Hz 25ms} 10 times, 500ms OFF, + * {2600Hz 25ms, 2900Hz 25ms} 10 times, 500ms OFF, + * {2600Hz 25ms, 2900Hz 25ms} 10 times, 2500ms OFF, REPEAT.... + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_MED_S_X4 = 69; + /** + * CDMA LOW S X4 tone: {2600Hz 25ms, 2900Hz 25ms} 10 times, + * 500ms OFF, {2600Hz 25ms, 2900Hz 25ms} 10 times, 500ms OFF, + * {2600Hz 25ms, 2900Hz 25ms} 10 times, 500ms OFF, + * {2600Hz 25ms, 2900Hz 25ms} 10 times, 2500ms OFF, REPEAT.... + * + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_LOW_S_X4 = 70; + /** + * CDMA HIGH PBX L: {3700Hz 25ms, 4000Hz 25ms}20 times, + * 2000ms OFF, REPEAT.... + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_HIGH_PBX_L = 71; + /** + * CDMA MED PBX L: {2600Hz 25ms, 2900Hz 25ms}20 times, + * 2000ms OFF, REPEAT.... + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_MED_PBX_L = 72; + /** + * CDMA LOW PBX L: {1300Hz 25ms,1450Hz 25ms}20 times, + * 2000ms OFF, REPEAT.... + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_LOW_PBX_L = 73; + /** + * CDMA HIGH PBX SS tone: {3700Hz 25ms, 4000Hz 25ms} 8 times + * 200 ms OFF, {3700Hz 25ms 4000Hz 25ms}8 times, + * 2000ms OFF, REPEAT.... + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_HIGH_PBX_SS = 74; + /** + * CDMA MED PBX SS tone: {2600Hz 25ms, 2900Hz 25ms} 8 times + * 200 ms OFF, {2600Hz 25ms 2900Hz 25ms}8 times, + * 2000ms OFF, REPEAT.... + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_MED_PBX_SS = 75; + /** + * CDMA LOW PBX SS tone: {1300Hz 25ms, 1450Hz 25ms} 8 times + * 200 ms OFF, {1300Hz 25ms 1450Hz 25ms}8 times, + * 2000ms OFF, REPEAT.... + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_LOW_PBX_SS = 76; + /** + * CDMA HIGH PBX SSL tone:{3700Hz 25ms, 4000Hz 25ms} 8 times + * 200ms OFF, {3700Hz 25ms, 4000Hz 25ms} 8 times, 200ms OFF, + * {3700Hz 25ms, 4000Hz 25ms} 16 times, 1000ms OFF, REPEAT.... + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_HIGH_PBX_SSL = 77; + /** + * CDMA MED PBX SSL tone:{2600Hz 25ms, 2900Hz 25ms} 8 times + * 200ms OFF, {2600Hz 25ms, 2900Hz 25ms} 8 times, 200ms OFF, + * {2600Hz 25ms, 2900Hz 25ms} 16 times, 1000ms OFF, REPEAT.... + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_MED_PBX_SSL = 78; + /** + * CDMA LOW PBX SSL tone:{1300Hz 25ms, 1450Hz 25ms} 8 times + * 200ms OFF, {1300Hz 25ms, 1450Hz 25ms} 8 times, 200ms OFF, + * {1300Hz 25ms, 1450Hz 25ms} 16 times, 1000ms OFF, REPEAT.... + * + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_LOW_PBX_SSL = 79; + /** + * CDMA HIGH PBX SSL tone:{3700Hz 25ms, 4000Hz 25ms} 8 times + * 200ms OFF, {3700Hz 25ms, 4000Hz 25ms} 16 times, 200ms OFF, + * {3700Hz 25ms, 4000Hz 25ms} 8 times, 1000ms OFF, REPEAT.... + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_HIGH_PBX_SLS = 80; + /** + * CDMA HIGH PBX SLS tone:{2600Hz 25ms, 2900Hz 25ms} 8 times + * 200ms OFF, {2600Hz 25ms, 2900Hz 25ms} 16 times, 200ms OFF, + * {2600Hz 25ms, 2900Hz 25ms} 8 times, 1000ms OFF, REPEAT.... + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_MED_PBX_SLS = 81; + /** + * CDMA HIGH PBX SLS tone:{1300Hz 25ms, 1450Hz 25ms} 8 times + * 200ms OFF, {1300Hz 25ms, 1450Hz 25ms} 16 times, 200ms OFF, + * {1300Hz 25ms, 1450Hz 25ms} 8 times, 1000ms OFF, REPEAT.... + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_LOW_PBX_SLS = 82; + /** + * CDMA HIGH PBX X S4 tone: {3700Hz 25ms 4000Hz 25ms} 8 times, + * 200ms OFF, {3700Hz 25ms 4000Hz 25ms} 8 times, 200ms OFF, + * {3700Hz 25ms 4000Hz 25ms} 8 times, 200ms OFF, + * {3700Hz 25ms 4000Hz 25ms} 8 times, 800ms OFF, REPEAT... + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_HIGH_PBX_S_X4 = 83; + /** + * CDMA MED PBX X S4 tone: {2600Hz 25ms 2900Hz 25ms} 8 times, + * 200ms OFF, {2600Hz 25ms 2900Hz 25ms} 8 times, 200ms OFF, + * {2600Hz 25ms 2900Hz 25ms} 8 times, 200ms OFF, + * {2600Hz 25ms 2900Hz 25ms} 8 times, 800ms OFF, REPEAT... + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_MED_PBX_S_X4 = 84; + /** + * CDMA LOW PBX X S4 tone: {1300Hz 25ms 1450Hz 25ms} 8 times, + * 200ms OFF, {1300Hz 25ms 1450Hz 25ms} 8 times, 200ms OFF, + * {1300Hz 25ms 1450Hz 25ms} 8 times, 200ms OFF, + * {1300Hz 25ms 1450Hz 25ms} 8 times, 800ms OFF, REPEAT... + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_LOW_PBX_S_X4 = 85; + /** + * CDMA Alert Network Lite tone: 1109Hz 62ms ON, 784Hz 62ms ON, 740Hz 62ms ON + * 622Hz 62ms ON, 1109Hz 62ms ON + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_ALERT_NETWORK_LITE = 86; + /** + * CDMA Alert Auto Redial tone: {1245Hz 62ms ON, 659Hz 62ms ON} 3 times, + * 1245 62ms ON + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_ALERT_AUTOREDIAL_LITE = 87; + /** + * CDMA One Min Beep tone: 1150Hz+770Hz 400ms ON + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_ONE_MIN_BEEP = 88; + /** + * + * CDMA KEYPAD Volume key lite tone: 941Hz+1477Hz 120ms ON + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_KEYPAD_VOLUME_KEY_LITE = 89; + /** + * CDMA PRESSHOLDKEY LITE tone: 587Hz 375ms ON, 1175Hz 125ms ON + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_PRESSHOLDKEY_LITE = 90; + /** + * CDMA ALERT INCALL LITE tone: 587Hz 62ms, 784 62ms, 831Hz 62ms, + * 784Hz 62ms, 1109 62ms, 784Hz 62ms, 831Hz 62ms, 784Hz 62ms + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_ALERT_INCALL_LITE = 91; + /** + * CDMA EMERGENCY RINGBACK tone: {941Hz 125ms ON, 10ms OFF} 3times + * 4990ms OFF, REPEAT... + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_EMERGENCY_RINGBACK = 92; + /** + * CDMA ALERT CALL GUARD tone: {1319Hz 125ms ON, 125ms OFF} 3 times + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_ALERT_CALL_GUARD = 93; + /** + * CDMA SOFT ERROR LITE tone: 1047Hz 125ms ON, 370Hz 125ms + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_SOFT_ERROR_LITE = 94; + /** + * CDMA CALLDROP LITE tone: 1480Hz 125ms, 1397Hz 125ms, 784Hz 125ms + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_CALLDROP_LITE = 95; + /** + * CDMA_NETWORK_BUSY_ONE_SHOT tone: 425Hz 500ms ON, 500ms OFF. + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_NETWORK_BUSY_ONE_SHOT = 96; + /** + * CDMA_ABBR_ALERT tone: 1150Hz+770Hz 400ms ON + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_ABBR_ALERT = 97; + /** + * CDMA_SIGNAL_OFF - silent tone + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_CDMA_SIGNAL_OFF = 98; /** Maximum volume, for use with {@link #ToneGenerator(int,int)} */ public static final int MAX_VOLUME = AudioSystem.MAX_VOLUME; @@ -546,6 +778,71 @@ public class ToneGenerator * <li>{@link #TONE_SUP_CONGESTION_ABBREV} * <li>{@link #TONE_SUP_CONFIRM} * <li>{@link #TONE_SUP_PIP} + * <li>{@link #TONE_CDMA_DIAL_TONE_LITE} + * <li>{@link #TONE_CDMA_NETWORK_USA_RINGBACK} + * <li>{@link #TONE_CDMA_INTERCEPT} + * <li>{@link #TONE_CDMA_ABBR_INTERCEPT} + * <li>{@link #TONE_CDMA_REORDER} + * <li>{@link #TONE_CDMA_ABBR_REORDER} + * <li>{@link #TONE_CDMA_NETWORK_BUSY} + * <li>{@link #TONE_CDMA_CONFIRM} + * <li>{@link #TONE_CDMA_ANSWER} + * <li>{@link #TONE_CDMA_NETWORK_CALLWAITING} + * <li>{@link #TONE_CDMA_PIP} + * <li>{@link #TONE_CDMA_CALL_SIGNAL_ISDN_NORMAL} + * <li>{@link #TONE_CDMA_CALL_SIGNAL_ISDN_INTERGROUP} + * <li>{@link #TONE_CDMA_CALL_SIGNAL_ISDN_SP_PRI} + * <li>{@link #TONE_CDMA_CALL_SIGNAL_ISDN_PAT3} + * <li>{@link #TONE_CDMA_CALL_SIGNAL_ISDN_PING_RING} + * <li>{@link #TONE_CDMA_CALL_SIGNAL_ISDN_PAT5} + * <li>{@link #TONE_CDMA_CALL_SIGNAL_ISDN_PAT6} + * <li>{@link #TONE_CDMA_CALL_SIGNAL_ISDN_PAT7} + * <li>{@link #TONE_CDMA_HIGH_L} + * <li>{@link #TONE_CDMA_MED_L} + * <li>{@link #TONE_CDMA_LOW_L} + * <li>{@link #TONE_CDMA_HIGH_SS} + * <li>{@link #TONE_CDMA_MED_SS} + * <li>{@link #TONE_CDMA_LOW_SS} + * <li>{@link #TONE_CDMA_HIGH_SSL} + * <li>{@link #TONE_CDMA_MED_SSL} + * <li>{@link #TONE_CDMA_LOW_SSL} + * <li>{@link #TONE_CDMA_HIGH_SS_2} + * <li>{@link #TONE_CDMA_MED_SS_2} + * <li>{@link #TONE_CDMA_LOW_SS_2} + * <li>{@link #TONE_CDMA_HIGH_SLS} + * <li>{@link #TONE_CDMA_MED_SLS} + * <li>{@link #TONE_CDMA_LOW_SLS} + * <li>{@link #TONE_CDMA_HIGH_S_X4} + * <li>{@link #TONE_CDMA_MED_S_X4} + * <li>{@link #TONE_CDMA_LOW_S_X4} + * <li>{@link #TONE_CDMA_HIGH_PBX_L} + * <li>{@link #TONE_CDMA_MED_PBX_L} + * <li>{@link #TONE_CDMA_LOW_PBX_L} + * <li>{@link #TONE_CDMA_HIGH_PBX_SS} + * <li>{@link #TONE_CDMA_MED_PBX_SS} + * <li>{@link #TONE_CDMA_LOW_PBX_SS} + * <li>{@link #TONE_CDMA_HIGH_PBX_SSL} + * <li>{@link #TONE_CDMA_MED_PBX_SSL} + * <li>{@link #TONE_CDMA_LOW_PBX_SSL} + * <li>{@link #TONE_CDMA_HIGH_PBX_SLS} + * <li>{@link #TONE_CDMA_MED_PBX_SLS} + * <li>{@link #TONE_CDMA_LOW_PBX_SLS} + * <li>{@link #TONE_CDMA_HIGH_PBX_S_X4} + * <li>{@link #TONE_CDMA_MED_PBX_S_X4} + * <li>{@link #TONE_CDMA_LOW_PBX_S_X4} + * <li>{@link #TONE_CDMA_ALERT_NETWORK_LITE} + * <li>{@link #TONE_CDMA_ALERT_AUTOREDIAL_LITE} + * <li>{@link #TONE_CDMA_ONE_MIN_BEEP} + * <li>{@link #TONE_CDMA_KEYPAD_VOLUME_KEY_LITE} + * <li>{@link #TONE_CDMA_PRESSHOLDKEY_LITE} + * <li>{@link #TONE_CDMA_ALERT_INCALL_LITE} + * <li>{@link #TONE_CDMA_EMERGENCY_RINGBACK} + * <li>{@link #TONE_CDMA_ALERT_CALL_GUARD} + * <li>{@link #TONE_CDMA_SOFT_ERROR_LITE} + * <li>{@link #TONE_CDMA_CALLDROP_LITE} + * <li>{@link #TONE_CDMA_NETWORK_BUSY_ONE_SHOT} + * <li>{@link #TONE_CDMA_ABBR_ALERT} + * <li>{@link #TONE_CDMA_SIGNAL_OFF} * </ul> * @see #ToneGenerator(int, int) */ @@ -566,7 +863,7 @@ public class ToneGenerator private native final void native_setup(int streamType, int volume); private native final void native_finalize(); - + @Override protected void finalize() { native_finalize(); } diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp index 7bfeb83..0273a5a 100644 --- a/media/jni/android_media_MediaRecorder.cpp +++ b/media/jni/android_media_MediaRecorder.cpp @@ -41,7 +41,7 @@ using namespace android; // ---------------------------------------------------------------------------- // helper function to extract a native Camera object from a Camera Java object -extern sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, struct camera_context_t** context); +extern sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, struct JNICameraContext** context); struct fields_t { jfieldID context; diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp index 81ee92c..c22cd53 100644 --- a/media/libmedia/ToneGenerator.cpp +++ b/media/libmedia/ToneGenerator.cpp @@ -33,199 +33,720 @@ namespace android { // Descriptors for all available tones (See ToneGenerator::ToneDescriptor class declaration for details) const ToneGenerator::ToneDescriptor ToneGenerator::sToneDescriptors[] = { - { segments: {{ duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1336, 941, 0 }}, - { duration: 0 , waveFreq: { 0 }}}, + { segments: {{ duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1336, 941, 0 }, 0, 0}, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: ToneGenerator::TONEGEN_INF, repeatSegment: 0 }, // TONE_DTMF_0 - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1209, 697, 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1209, 697, 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: ToneGenerator::TONEGEN_INF, repeatSegment: 0 }, // TONE_DTMF_1 - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1336, 697, 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1336, 697, 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: ToneGenerator::TONEGEN_INF, repeatSegment: 0 }, // TONE_DTMF_2 - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1477, 697, 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1477, 697, 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: ToneGenerator::TONEGEN_INF, repeatSegment: 0 }, // TONE_DTMF_3 - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1209, 770, 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1209, 770, 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: ToneGenerator::TONEGEN_INF, repeatSegment: 0 }, // TONE_DTMF_4 - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1336, 770, 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1336, 770, 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: ToneGenerator::TONEGEN_INF, repeatSegment: 0 }, // TONE_DTMF_5 - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1477, 770, 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1477, 770, 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: ToneGenerator::TONEGEN_INF, repeatSegment: 0 }, // TONE_DTMF_6 - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1209, 852, 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1209, 852, 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: ToneGenerator::TONEGEN_INF, repeatSegment: 0 }, // TONE_DTMF_7 - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1336, 852, 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1336, 852, 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: ToneGenerator::TONEGEN_INF, repeatSegment: 0 }, // TONE_DTMF_8 - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1477, 852, 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1477, 852, 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: ToneGenerator::TONEGEN_INF, repeatSegment: 0 }, // TONE_DTMF_9 - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1209, 941, 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1209, 941, 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: ToneGenerator::TONEGEN_INF, repeatSegment: 0 }, // TONE_DTMF_S - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1477, 941, 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1477, 941, 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: ToneGenerator::TONEGEN_INF, repeatSegment: 0 }, // TONE_DTMF_P - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1633, 697, 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1633, 697, 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: ToneGenerator::TONEGEN_INF, repeatSegment: 0 }, // TONE_DTMF_A - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1633, 770, 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1633, 770, 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: ToneGenerator::TONEGEN_INF, repeatSegment: 0 }, // TONE_DTMF_B - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1633, 852, 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1633, 852, 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: ToneGenerator::TONEGEN_INF, repeatSegment: 0 }, // TONE_DTMF_C - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1633, 941, 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1633, 941, 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: ToneGenerator::TONEGEN_INF, repeatSegment: 0 }, // TONE_DTMF_D - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 425, 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 425, 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: ToneGenerator::TONEGEN_INF, repeatSegment: 0 }, // TONE_SUP_DIAL - { segments: { { duration: 500 , waveFreq: { 425, 0 }}, - { duration: 500, waveFreq: { 0 }}, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: 500 , waveFreq: { 425, 0 }, 0, 0}, + { duration: 500, waveFreq: { 0 }, 0, 0}, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: ToneGenerator::TONEGEN_INF, repeatSegment: 0 }, // TONE_SUP_BUSY - { segments: { { duration: 200, waveFreq: { 425, 0 } }, - { duration: 200, waveFreq: { 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: 200, waveFreq: { 425, 0 }, 0, 0 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: ToneGenerator::TONEGEN_INF, repeatSegment: 0 }, // TONE_SUP_CONGESTION - { segments: { { duration: 200, waveFreq: { 425, 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: 200, waveFreq: { 425, 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: 0, repeatSegment: 0 }, // TONE_SUP_RADIO_ACK - { segments: { { duration: 200, waveFreq: { 425, 0 }}, - { duration: 200, waveFreq: { 0 }}, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: 200, waveFreq: { 425, 0 }, 0, 0}, + { duration: 200, waveFreq: { 0 }, 0, 0}, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: 2, repeatSegment: 0 }, // TONE_SUP_RADIO_NOTAVAIL - { segments: { { duration: 330, waveFreq: { 950, 1400, 1800, 0 }}, - { duration: 1000, waveFreq: { 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: 330, waveFreq: { 950, 1400, 1800, 0 }, 0, 0}, + { duration: 1000, waveFreq: { 0 }, 0, 0}, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: ToneGenerator::TONEGEN_INF, repeatSegment: 0 }, // TONE_SUP_ERROR - { segments: { { duration: 200, waveFreq: { 425, 0 } }, - { duration: 600, waveFreq: { 0 } }, - { duration: 200, waveFreq: { 425, 0 } }, - { duration: 3000, waveFreq: { 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: 200, waveFreq: { 425, 0 }, 0, 0 }, + { duration: 600, waveFreq: { 0 }, 0, 0 }, + { duration: 200, waveFreq: { 425, 0 }, 0, 0 }, + { duration: 3000, waveFreq: { 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: ToneGenerator::TONEGEN_INF, repeatSegment: 0 }, // TONE_SUP_CALL_WAITING - { segments: { { duration: 1000, waveFreq: { 425, 0 } }, - { duration: 4000, waveFreq: { 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: 1000, waveFreq: { 425, 0 }, 0, 0 }, + { duration: 4000, waveFreq: { 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: ToneGenerator::TONEGEN_INF, repeatSegment: 0 }, // TONE_SUP_RINGTONE - { segments: { { duration: 40, waveFreq: { 400, 1200, 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: 40, waveFreq: { 400, 1200, 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: 0, repeatSegment: 0 }, // TONE_PROP_BEEP - { segments: { { duration: 100, waveFreq: { 1200, 0 } }, - { duration: 100, waveFreq: { 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: 100, waveFreq: { 1200, 0 }, 0, 0 }, + { duration: 100, waveFreq: { 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: 1, repeatSegment: 0 }, // TONE_PROP_ACK - { segments: { { duration: 400, waveFreq: { 300, 400, 500, 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: 400, waveFreq: { 300, 400, 500, 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: 0, repeatSegment: 0 }, // TONE_PROP_NACK - { segments: { { duration: 200, waveFreq: { 400, 1200, 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: 200, waveFreq: { 400, 1200, 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: 0, repeatSegment: 0 }, // TONE_PROP_PROMPT - { segments: { { duration: 40, waveFreq: { 400, 1200, 0 } }, - { duration: 200, waveFreq: { 0 } }, - { duration: 40, waveFreq: { 400, 1200, 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: 40, waveFreq: { 400, 1200, 0 }, 0, 0 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 40, waveFreq: { 400, 1200, 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: 0, repeatSegment: 0 }, // TONE_PROP_BEEP2 - { segments: { { duration: 250, waveFreq: { 440, 0 } }, - { duration: 250, waveFreq: { 620, 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: 250, waveFreq: { 440, 0 }, 0, 0 }, + { duration: 250, waveFreq: { 620, 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0 }}, repeatCnt: ToneGenerator::TONEGEN_INF, repeatSegment: 0 }, // TONE_SUP_INTERCEPT - { segments: { { duration: 250, waveFreq: { 440, 0 } }, - { duration: 250, waveFreq: { 620, 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: 250, waveFreq: { 440, 0 }, 0, 0 }, + { duration: 250, waveFreq: { 620, 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: 7, repeatSegment: 0 }, // TONE_SUP_INTERCEPT_ABBREV - { segments: { { duration: 250, waveFreq: { 480, 620, 0 } }, - { duration: 250, waveFreq: { 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: 250, waveFreq: { 480, 620, 0 }, 0, 0 }, + { duration: 250, waveFreq: { 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: 7, repeatSegment: 0 }, // TONE_SUP_CONGESTION_ABBREV - { segments: { { duration: 100, waveFreq: { 350, 440, 0 } }, - { duration: 100, waveFreq: { 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: 100, waveFreq: { 350, 440, 0 }, 0, 0 }, + { duration: 100, waveFreq: { 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: 2, repeatSegment: 0 }, // TONE_SUP_CONFIRM - { segments: { { duration: 100, waveFreq: { 480, 0 } }, - { duration: 100, waveFreq: { 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: 100, waveFreq: { 480, 0 }, 0, 0 }, + { duration: 100, waveFreq: { 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: 3, repeatSegment: 0 }, // TONE_SUP_PIP - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 350, 440, 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: {{ duration: ToneGenerator::TONEGEN_INF, waveFreq: { 425, 0 }, 0, 0}, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_DIAL_TONE_LITE + { segments: { { duration: 2000, waveFreq: { 440, 480, 0 }, 0, 0 }, + { duration: 4000, waveFreq: { 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_NETWORK_USA_RINGBACK + { segments: { { duration: 250, waveFreq: { 440, 0 }, 0, 0 }, + { duration: 250, waveFreq: { 620, 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_INTERCEPT + { segments: { { duration: 250, waveFreq: { 440, 0 }, 0, 0 }, + { duration: 250, waveFreq: { 620, 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, + repeatCnt: 0, + repeatSegment: 0 }, // TONE_CDMA_ABBR_INTERCEPT + { segments: { { duration: 250, waveFreq: { 480, 620, 0 }, 0, 0 }, + { duration: 250, waveFreq: { 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_REORDER + { segments: { { duration: 250, waveFreq: { 480, 620, 0 }, 0, 0 }, + { duration: 250, waveFreq: { 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, + repeatCnt: 7, + repeatSegment: 0 }, // TONE_CDMA_ABBR_REORDER + { segments: { { duration: 500, waveFreq: { 480, 620, 0 }, 0, 0 }, + { duration: 500, waveFreq: { 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_NETWORK_BUSY + { segments: { { duration: 100, waveFreq: { 350, 440, 0 }, 0, 0 }, + { duration: 100, waveFreq: { 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, + repeatCnt: 2, + repeatSegment: 0 }, // TONE_CDMA_CONFIRM + { segments: { { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: 0, + repeatSegment: 0 }, // TONE_CDMA_ANSWER + { segments: { { duration: 300, waveFreq: { 440, 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, + repeatCnt: 0, + repeatSegment: 0 }, // TONE_CDMA_NETWORK_CALLWAITING + { segments: { { duration: 100, waveFreq: { 480, 0 }, 0, 0 }, + { duration: 100, waveFreq: { 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, + repeatCnt: 3, + repeatSegment: 0 }, // TONE_CDMA_PIP + + { segments: { { duration: 32, waveFreq: { 2091, 0}, 0, 0 }, + { duration: 64, waveFreq: { 2556, 0}, 19, 0}, + { duration: 32, waveFreq: { 2091, 0}, 0, 0}, + { duration: 48, waveFreq: { 2556, 0}, 0, 0}, + { duration: 4000, waveFreq: { 0 }, 0, 0}, + { duration: 0, waveFreq: { 0 }, 0, 0}}, + repeatCnt: 0, + repeatSegment: 0 }, // TONE_CDMA_CALL_SIGNAL_ISDN_NORMAL + { segments: { { duration: 32, waveFreq: { 2091, 0}, 0, 0 }, + { duration: 64, waveFreq: { 2556, 0}, 7, 0 }, + { duration: 32, waveFreq: { 2091, 0}, 0, 0 }, + { duration: 400, waveFreq: { 0 }, 0, 0 }, + { duration: 32, waveFreq: { 2091, 0}, 0, 0 }, + { duration: 64, waveFreq: { 2556, 0}, 7, 4 }, + { duration: 32, waveFreq: { 2091, 0}, 0, 0 }, + { duration: 4000, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 } }, + repeatCnt: 0, + repeatSegment: 0 }, // TONE_CDMA_CALL_SIGNAL_ISDN_INTERGROUP + { segments: { { duration: 32, waveFreq: { 2091, 0}, 0, 0 }, + { duration: 64, waveFreq: { 2556, 0}, 3, 0 }, + { duration: 16, waveFreq: { 2091, 0}, 0, 0 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 32, waveFreq: { 2091, 0}, 0, 0 }, + { duration: 64, waveFreq: { 2556, 0}, 3, 4 }, + { duration: 16, waveFreq: { 2091, 0}, 0, 0 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 } }, + repeatCnt: 0, + repeatSegment: 0 }, // TONE_CDMA_CALL_SIGNAL_ISDN_SP_PRI + { segments: { { duration: 0, waveFreq: { 0 }, 0, 0} }, + repeatCnt: 0, + repeatSegment: 0 }, // TONE_CDMA_CALL_SIGNAL_ISDN_PAT3 + { segments: { { duration: 32, waveFreq: { 2091, 0 }, 0, 0 }, + { duration: 64, waveFreq: { 2556, 0 }, 4, 0 }, + { duration: 20, waveFreq: { 2091, 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 } , 0, 0 } }, + repeatCnt: 0, + repeatSegment: 0 }, // TONE_CDMA_CALL_SIGNAL_ISDN_PING_RING + { segments: { { duration: 0, waveFreq: { 0 }, 0, 0} }, + repeatCnt: 0, + repeatSegment: 0 }, // TONE_CDMA_CALL_SIGNAL_ISDN_PAT5 + { segments: { { duration: 0, waveFreq: { 0 }, 0, 0} }, + repeatCnt: 0, + repeatSegment: 0 }, // TONE_CDMA_CALL_SIGNAL_ISDN_PAT6 + { segments: { { duration: 0, waveFreq: { 0 }, 0, 0} }, + repeatCnt: 0, + repeatSegment: 0 }, // TONE_CDMA_CALL_SIGNAL_ISDN_PAT7 + + { segments: { { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 4000, 0 }, 39, 0 }, + { duration: 4000, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 } }, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_HIGH_L + { segments: { { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2900, 0 }, 39, 0 }, + { duration: 4000, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 } }, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_MED_L + { segments: { { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1450, 0 }, 39, 0 }, + { duration: 4000, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 } }, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_LOW_L + { segments: { { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 4000, 0 }, 15, 0 }, + { duration: 400, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 } }, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_HIGH_SS + { segments: { { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2900, 0 }, 15, 0 }, + { duration: 400, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_MED_SS + { segments: { { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1450, 0 }, 15, 0 }, + { duration: 400, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_LOW_SS + { segments: { { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 4000, 0 }, 7, 0 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 4000, 0 }, 7, 3 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 4000, 0 }, 15, 6 }, + { duration: 4000, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_HIGH_SSL + { segments: { { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2900, 0 }, 7, 0 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2900, 0 }, 7, 3 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2900, 0 }, 15, 6 }, + { duration: 4000, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_MED_SSL + { segments: { { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1450, 0 }, 7, 0 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1450, 0 }, 7, 3 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1450, 0 }, 15, 6 }, + { duration: 4000, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_LOW_SSL + { segments: { { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 4000, 0 }, 19, 0 }, + { duration: 1000, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 4000, 0 }, 19, 3 }, + { duration: 3000, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_HIGH_SS_2 + { segments: { { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2900, 0 }, 19, 0 }, + { duration: 1000, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2900, 0 }, 19, 3 }, + { duration: 3000, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_MED_SS_2 + { segments: { { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1450, 0 }, 19, 0 }, + { duration: 1000, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1450, 0 }, 19, 3 }, + { duration: 3000, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_LOW_SS_2 + { segments: { { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 4000, 0 }, 9, 0 }, + { duration: 500, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 4000, 0 }, 19, 3 }, + { duration: 500, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 4000, 0 }, 9, 6 }, + { duration: 3000, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_HIGH_SLS + { segments: { { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2900, 0 }, 9, 0 }, + { duration: 500, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2900, 0 }, 19, 3 }, + { duration: 500, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2900, 0 }, 9, 6 }, + { duration: 3000, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_MED_SLS + { segments: { { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1450, 0 }, 9, 0 }, + { duration: 500, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1450, 0 }, 19, 3 }, + { duration: 500, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1450, 0 }, 9, 6 }, + { duration: 3000, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_LOW_SLS + { segments: { { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 4000, 0 }, 9, 0 }, + { duration: 500, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 4000, 0 }, 9, 3 }, + { duration: 500, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 4000, 0 }, 9, 6 }, + { duration: 500, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 4000, 0 }, 9, 9 }, + { duration: 2500, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_HIGH_S_X4 + { segments: { { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2900, 0 }, 9, 0 }, + { duration: 500, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2900, 0 }, 9, 3 }, + { duration: 500, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2900, 0 }, 9, 6 }, + { duration: 500, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2900, 0 }, 9, 9 }, + { duration: 2500, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_MED_S_X4 + { segments: { { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1450, 0 }, 9, 0 }, + { duration: 500, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1450, 0 }, 9, 3 }, + { duration: 500, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1450, 0 }, 9, 6 }, + { duration: 500, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1450, 0 }, 9, 9 }, + { duration: 2500, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_LOW_S_X4 + { segments: { { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 4000, 0 }, 19, 0 }, + { duration: 2000, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_HIGH_PBX_L + { segments: { { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2900, 0 }, 19, 0 }, + { duration: 2000, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_MED_PBX_L + { segments: { { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1450, 0 }, 19, 0 }, + { duration: 2000, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_LOW_PBX_L + { segments: { { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 4000, 0 }, 7, 0 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 4000, 0 }, 7, 3 }, + { duration: 2000, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_HIGH_PBX_SS + { segments: { { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2900, 0 }, 7, 0 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2900, 0 }, 7, 3 }, + { duration: 2000, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_MED_PBX_SS + { segments: { { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1450, 0 }, 7, 0 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1450, 0 }, 7, 3 }, + { duration: 2000, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_LOW_PBX_SS + { segments: { { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 4000, 0 }, 7, 0 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 4000, 0 }, 7, 3 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 4000, 0 }, 15, 6 }, + { duration: 1000, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_HIGH_PBX_SSL + { segments: { { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2900, 0 }, 7, 0 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2900, 0 }, 7, 3 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2900, 0 }, 15, 6 }, + { duration: 1000, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_MED_PBX_SSL + { segments: { { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1450, 0 }, 7, 0 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1450, 0 }, 7, 3 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1450, 0 }, 15, 6 }, + { duration: 1000, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_LOW_PBX_SSL + { segments: { { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 4000, 0 }, 7, 0 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 4000, 0 }, 15, 3 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 4000, 0 }, 7, 6 }, + { duration: 1000, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_HIGH_PBX_SLS + { segments: { { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2900, 0 }, 7, 0 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2900, 0 }, 15, 3 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2900, 0 }, 7, 6 }, + { duration: 1000, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_MED_PBX_SLS + { segments: { { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1450, 0 }, 7, 0 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1450, 0 }, 15, 3 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1450, 0 }, 7, 6 }, + { duration: 1000, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_LOW_PBX_SLS + { segments: { { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 4000, 0 }, 7, 0 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 4000, 0 }, 7, 3 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 4000, 0 }, 7, 6 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 3700, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 4000, 0 }, 7, 9 }, + { duration: 800, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_HIGH_PBX_S_X4 + { segments: { { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2900, 0 }, 7, 0 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2900, 0 }, 7, 3 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2900, 0 }, 7, 6 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2600, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 2900, 0 }, 7, 9 }, + { duration: 800, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_MED_PBX_S_X4 + { segments: { { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1450, 0 }, 7, 0 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1450, 0 }, 7, 3 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1450, 0 }, 7, 6 }, + { duration: 200, waveFreq: { 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1300, 0 }, 0, 0 }, + { duration: 25, waveFreq: { 1450, 0 }, 7, 9 }, + { duration: 800, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_LOW_PBX_S_X4 + + { segments: { { duration: 62, waveFreq: { 1109, 0 }, 0, 0 }, + { duration: 62, waveFreq: { 784, 0 }, 0, 0 }, + { duration: 62, waveFreq: { 740, 0 }, 0, 0 }, + { duration: 62, waveFreq: { 622, 0 }, 0, 0 }, + { duration: 62, waveFreq: { 1109, 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 } }, + repeatCnt: 0, + repeatSegment: 0 }, // TONE_CDMA_ALERT_NETWORK_LITE + { segments: { { duration: 62, waveFreq: { 1245, 0 }, 0, 0 }, + { duration: 62, waveFreq: { 659, 0 }, 2, 0 }, + { duration: 62, waveFreq: { 1245, 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 } }, + repeatCnt: 0, + repeatSegment: 0 }, // TONE_CDMA_ALERT_AUTOREDIAL_LITE + { segments: { { duration: 400, waveFreq: { 1150, 770, 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 } }, + repeatCnt: 0, + repeatSegment: 0 }, // TONE_CDMA_ONE_MIN_BEEP + { segments: { { duration: 120, waveFreq: { 941, 1477, 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 } }, + repeatCnt: 0, + repeatSegment: 0 }, // TONE_CDMA_KEYPAD_VOLUME_KEY_LITE + { segments: { { duration: 375, waveFreq: { 587, 0 }, 0, 0 }, + { duration: 125, waveFreq: { 1175, 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 } }, + repeatCnt: 0, + repeatSegment: 0 }, // TONE_CDMA_PRESSHOLDKEY_LITE + { segments: { { duration: 62, waveFreq: { 587, 0 }, 0, 0 }, + { duration: 62, waveFreq: { 784, 0 }, 0, 0 }, + { duration: 62, waveFreq: { 831, 0 }, 0, 0 }, + { duration: 62, waveFreq: { 784, 0 }, 0, 0 }, + { duration: 62, waveFreq: { 1109, 0 }, 0, 0 }, + { duration: 62, waveFreq: { 784, 0 }, 0, 0 }, + { duration: 62, waveFreq: { 831, 0 }, 0, 0 }, + { duration: 62, waveFreq: { 784, 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 } }, + repeatCnt: 0, + repeatSegment: 0 }, // TONE_CDMA_ALERT_INCALL_LITE + { segments: { { duration: 125, waveFreq: { 941, 0 }, 0, 0 }, + { duration: 10, waveFreq: { 0 }, 2, 0 }, + { duration: 4990, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 } }, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_CDMA_EMERGENCY_RINGBACK + { segments: { { duration: 125, waveFreq: { 1319, 0 }, 0, 0 }, + { duration: 125, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 } }, + repeatCnt: 2, + repeatSegment: 0 }, // TONE_CDMA_ALERT_CALL_GUARD + { segments: { { duration: 125, waveFreq: { 1047, 0 }, 0, 0 }, + { duration: 125, waveFreq: { 370, 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 } }, + repeatCnt: 0, + repeatSegment: 0 }, // TONE_CDMA_SOFT_ERROR_LITE + { segments: { { duration: 125, waveFreq: { 1480, 0 }, 0, 0 }, + { duration: 125, waveFreq: { 1397, 0 }, 0, 0 }, + { duration: 125, waveFreq: { 784, 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 } }, + repeatCnt: 0, + repeatSegment: 0 }, // TONE_CDMA_CALLDROP_LITE + + { segments: { { duration: 500, waveFreq: { 425, 0 }, 0, 0 }, + { duration: 500, waveFreq: { 0 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: 0, + repeatSegment: 0 }, // TONE_CDMA_NETWORK_BUSY_ONE_SHOT + { segments: { { duration: 400, waveFreq: { 1150, 770 }, 0, 0 }, + { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: 0, + repeatSegment: 0 }, // TONE_CDMA_ABBR_ALERT + { segments: { { duration: 0, waveFreq: { 0 }, 0, 0 }}, + repeatCnt: 0, + repeatSegment: 0 }, // TONE_CDMA_SIGNAL_OFF + + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 350, 440, 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: ToneGenerator::TONEGEN_INF, repeatSegment: 0 }, // TONE_ANSI_DIAL - { segments: { { duration: 500, waveFreq: { 480, 620, 0 } }, - { duration: 500, waveFreq: { 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: 500, waveFreq: { 480, 620, 0 }, 0, 0 }, + { duration: 500, waveFreq: { 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: ToneGenerator::TONEGEN_INF, repeatSegment: 0 }, // TONE_ANSI_BUSY - { segments: { { duration: 250, waveFreq: { 480, 620, 0 } }, - { duration: 250, waveFreq: { 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: 250, waveFreq: { 480, 620, 0 }, 0, 0 }, + { duration: 250, waveFreq: { 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: ToneGenerator::TONEGEN_INF, repeatSegment: 0 }, // TONE_ANSI_CONGESTION - { segments: { { duration: 300, waveFreq: { 440, 0 } }, - { duration: 9700, waveFreq: { 0 } }, - { duration: 100, waveFreq: { 440, 0 } }, - { duration: 100, waveFreq: { 0 } }, - { duration: 100, waveFreq: { 440, 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: 300, waveFreq: { 440, 0 }, 0, 0 }, + { duration: 9700, waveFreq: { 0 }, 0, 0 }, + { duration: 100, waveFreq: { 440, 0 }, 0, 0 }, + { duration: 100, waveFreq: { 0 }, 0, 0 }, + { duration: 100, waveFreq: { 440, 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: ToneGenerator::TONEGEN_INF, repeatSegment: 1 }, // TONE_ANSI_CALL_WAITING - { segments: { { duration: 2000, waveFreq: { 440, 480, 0 } }, - { duration: 4000, waveFreq: { 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: 2000, waveFreq: { 440, 480, 0 }, 0, 0 }, + { duration: 4000, waveFreq: { 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: ToneGenerator::TONEGEN_INF, repeatSegment: 0 }, // TONE_ANSI_RINGTONE - { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 400, 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 400, 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: ToneGenerator::TONEGEN_INF, repeatSegment: 0 }, // TONE_JAPAN_DIAL - { segments: { { duration: 500, waveFreq: { 400, 0 } }, - { duration: 500, waveFreq: { 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: 500, waveFreq: { 400, 0 }, 0, 0 }, + { duration: 500, waveFreq: { 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: ToneGenerator::TONEGEN_INF, repeatSegment: 0 }, // TONE_JAPAN_BUSY - { segments: { { duration: 1000, waveFreq: { 400, 0 } }, - { duration: 2000, waveFreq: { 0 } }, - { duration: 0 , waveFreq: { 0 }}}, + { segments: { { duration: 1000, waveFreq: { 400, 0 }, 0, 0 }, + { duration: 2000, waveFreq: { 0 }, 0, 0 }, + { duration: 0 , waveFreq: { 0 }, 0, 0}}, repeatCnt: ToneGenerator::TONEGEN_INF, repeatSegment: 0 }, // TONE_JAPAN_RADIO_ACK + + + }; // Used by ToneGenerator::getToneForRegion() to convert user specified supervisory tone type @@ -529,9 +1050,9 @@ initAudioTrack_exit: // //////////////////////////////////////////////////////////////////////////////// void ToneGenerator::audioCallback(int event, void* user, void *info) { - + if (event != AudioTrack::EVENT_MORE_DATA) return; - + const AudioTrack::Buffer *buffer = static_cast<const AudioTrack::Buffer *>(info); ToneGenerator *lpToneGen = static_cast<ToneGenerator *>(user); short *lpOut = buffer->i16; @@ -549,12 +1070,12 @@ void ToneGenerator::audioCallback(int event, void* user, void *info) { unsigned int lGenSmp; unsigned int lWaveCmd = WaveGenerator::WAVEGEN_CONT; bool lSignal = false; - + lpToneGen->mLock.lock(); // Update pcm frame count and end time (current time at the end of this process) lpToneGen->mTotalSmp += lReqSmp; - + // Update tone gen state machine and select wave gen command switch (lpToneGen->mState) { case TONE_PLAYING: @@ -562,13 +1083,13 @@ void ToneGenerator::audioCallback(int event, void* user, void *info) { break; case TONE_STARTING: LOGV("Starting Cbk"); - + lWaveCmd = WaveGenerator::WAVEGEN_START; break; case TONE_STOPPING: case TONE_RESTARTING: LOGV("Stop/restart Cbk"); - + lWaveCmd = WaveGenerator::WAVEGEN_STOP; lpToneGen->mNextSegSmp = TONEGEN_INF; // forced to skip state machine management below break; @@ -578,21 +1099,21 @@ void ToneGenerator::audioCallback(int event, void* user, void *info) { lNumSmp = 0; goto audioCallback_EndLoop; } - - + + // Exit if tone sequence is over if (lpToneDesc->segments[lpToneGen->mCurSegment].duration == 0) { if (lpToneGen->mState == TONE_PLAYING) { - lpToneGen->mState = TONE_STOPPING; + lpToneGen->mState = TONE_STOPPING; } goto audioCallback_EndLoop; } - + if (lpToneGen->mTotalSmp > lpToneGen->mNextSegSmp) { // Time to go to next sequence segment - + LOGV("End Segment, time: %d\n", (unsigned int)(systemTime()/1000000)); - + lGenSmp = lReqSmp; // If segment, ON -> OFF transition : ramp volume down @@ -609,25 +1130,49 @@ void ToneGenerator::audioCallback(int event, void* user, void *info) { LOGV("ON->OFF, lGenSmp: %d, lReqSmp: %d\n", lGenSmp, lReqSmp); } - // Go to next segment - lpToneGen->mCurSegment++; + // check if we need to loop and loop for the reqd times + if (lpToneDesc->segments[lpToneGen->mCurSegment].loopCnt) { + if (lpToneGen->mLoopCounter < lpToneDesc->segments[lpToneGen->mCurSegment].loopCnt) { + LOGV ("in if loop loopCnt(%d) loopctr(%d), CurSeg(%d) \n", + lpToneDesc->segments[lpToneGen->mCurSegment].loopCnt, + lpToneGen->mLoopCounter, + lpToneGen->mCurSegment); + lpToneGen->mCurSegment = lpToneDesc->segments[lpToneGen->mCurSegment].loopIndx; + ++lpToneGen->mLoopCounter; + } else { + // completed loop. go to next segment + lpToneGen->mLoopCounter = 0; + lpToneGen->mCurSegment++; + LOGV ("in else loop loopCnt(%d) loopctr(%d), CurSeg(%d) \n", + lpToneDesc->segments[lpToneGen->mCurSegment].loopCnt, + lpToneGen->mLoopCounter, + lpToneGen->mCurSegment); + } + } else { + lpToneGen->mCurSegment++; + LOGV ("Goto next seg loopCnt(%d) loopctr(%d), CurSeg(%d) \n", + lpToneDesc->segments[lpToneGen->mCurSegment].loopCnt, + lpToneGen->mLoopCounter, + lpToneGen->mCurSegment); + + } // Handle loop if last segment reached if (lpToneDesc->segments[lpToneGen->mCurSegment].duration == 0) { LOGV("Last Seg: %d\n", lpToneGen->mCurSegment); - + // Pre increment loop count and restart if total count not reached. Stop sequence otherwise if (++lpToneGen->mCurCount <= lpToneDesc->repeatCnt) { LOGV("Repeating Count: %d\n", lpToneGen->mCurCount); - + lpToneGen->mCurSegment = lpToneDesc->repeatSegment; if (lpToneDesc->segments[lpToneDesc->repeatSegment].waveFreq[0] != 0) { lWaveCmd = WaveGenerator::WAVEGEN_START; } - + LOGV("New segment %d, Next Time: %d\n", lpToneGen->mCurSegment, (lpToneGen->mNextSegSmp*1000)/lpToneGen->mSamplingRate); - + } else { lGenSmp = 0; LOGV("End repeat, time: %d\n", (unsigned int)(systemTime()/1000000)); @@ -644,11 +1189,11 @@ void ToneGenerator::audioCallback(int event, void* user, void *info) { lGenSmp = 0; } } - + // Update next segment transition position. No harm to do it also for last segment as lpToneGen->mNextSegSmp won't be used any more lpToneGen->mNextSegSmp += (lpToneDesc->segments[lpToneGen->mCurSegment].duration * lpToneGen->mSamplingRate) / 1000; - + } else { // Inside a segment keep tone ON or OFF if (lpToneDesc->segments[lpToneGen->mCurSegment].waveFreq[0] == 0) { @@ -657,24 +1202,24 @@ void ToneGenerator::audioCallback(int event, void* user, void *info) { lGenSmp = lReqSmp; // If event segment, tone is currently ON } } - + if (lGenSmp) { // If samples must be generated, call all active wave generators and acumulate waves in lpOut unsigned int lFreqIdx = 0; unsigned short lFrequency = lpToneDesc->segments[lpToneGen->mCurSegment].waveFreq[lFreqIdx]; - + while (lFrequency != 0) { WaveGenerator *lpWaveGen = lpToneGen->mWaveGens.valueFor(lFrequency); lpWaveGen->getSamples(lpOut, lGenSmp, lWaveCmd); lFrequency = lpToneDesc->segments[lpToneGen->mCurSegment].waveFreq[++lFreqIdx]; } } - + lNumSmp -= lReqSmp; lpOut += lReqSmp; - + audioCallback_EndLoop: - + switch (lpToneGen->mState) { case TONE_RESTARTING: LOGV("Cbk restarting track\n"); @@ -694,7 +1239,7 @@ audioCallback_EndLoop: LOGV("Cbk Stopping track\n"); lSignal = true; lpToneGen->mpAudioTrack->stop(); - + // Force loop exit lNumSmp = 0; break; @@ -765,6 +1310,7 @@ bool ToneGenerator::prepareWave() { mTotalSmp = 0; mCurSegment = 0; mCurCount = 0; + mLoopCounter = 0; if (mpToneDesc->segments[0].duration == TONEGEN_INF) { mNextSegSmp = TONEGEN_INF; } else{ diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java index af46082..84058f5 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java @@ -51,8 +51,8 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase<Medi private SQLiteDatabase mDB; private SurfaceHolder mSurfaceHolder = null; - private static final int NUM_STRESS_LOOP = 1; - private static final int NUM_PLAYBACk_IN_EACH_LOOP = 2; + private static final int NUM_STRESS_LOOP = 10; + private static final int NUM_PLAYBACk_IN_EACH_LOOP = 20; private static final long MEDIA_STRESS_WAIT_TIME = 5000; //5 seconds private static final String MEDIA_MEMORY_OUTPUT = "/sdcard/mediaMemOutput.txt"; diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java index dbf937c..69e93a1 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java @@ -18,6 +18,11 @@ package com.android.mediaframeworktest.stress; import com.android.mediaframeworktest.MediaFrameworkTest; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.Writer; + import android.hardware.Camera; import android.media.MediaPlayer; import android.media.MediaRecorder; @@ -47,6 +52,8 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me private static final long WAIT_TIME_PLAYBACK = 60000; // 6 second private static final String OUTPUT_FILE = "/sdcard/temp"; private static final String OUTPUT_FILE_EXT = ".3gp"; + private static final String MEDIA_STRESS_OUTPUT = + "/sdcard/mediaStressOutput.txt"; public MediaRecorderStressTest() { super("com.android.mediaframeworktest", MediaFrameworkTest.class); @@ -62,8 +69,14 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me public void testStressCamera() throws Exception { SurfaceHolder mSurfaceHolder; mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); + File stressOutFile = new File(MEDIA_STRESS_OUTPUT); + Writer output = new BufferedWriter(new FileWriter(stressOutFile, true)); + output.write("Camera start preview stress:\n"); + output.write("Total number of loops:" + + NUMBER_OF_CAMERA_STRESS_LOOPS + "\n"); try { Log.v(TAG, "Start preview"); + output.write("No of loop: "); for (int i = 0; i< NUMBER_OF_CAMERA_STRESS_LOOPS; i++){ mCamera = Camera.open(); mCamera.setPreviewDisplay(mSurfaceHolder); @@ -71,10 +84,13 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me Thread.sleep(WAIT_TIME_CAMERA_TEST); mCamera.stopPreview(); mCamera.release(); + output.write(" ," + i); } } catch (Exception e) { Log.v(TAG, e.toString()); } + output.write("\n\n"); + output.close(); } //Test case for stressing the camera preview. @@ -83,7 +99,13 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me String filename; SurfaceHolder mSurfaceHolder; mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); - try { + File stressOutFile = new File(MEDIA_STRESS_OUTPUT); + Writer output = new BufferedWriter(new FileWriter(stressOutFile, true)); + output.write("H263 video record- reset after prepare Stress test\n"); + output.write("Total number of loops:" + + NUMBER_OF_RECORDER_STRESS_LOOPS + "\n"); + try { + output.write("No of loop: "); Log.v(TAG, "Start preview"); for (int i = 0; i < NUMBER_OF_RECORDER_STRESS_LOOPS; i++){ Log.v(TAG, "counter = " + i); @@ -106,10 +128,13 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me Thread.sleep(WAIT_TIME_RECORDER_TEST); mRecorder.reset(); mRecorder.release(); + output.write(", " + i); } } catch (Exception e) { Log.v(TAG, e.toString()); } + output.write("\n\n"); + output.close(); } @@ -119,8 +144,14 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me String filename; SurfaceHolder mSurfaceHolder; mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); + File stressOutFile = new File(MEDIA_STRESS_OUTPUT); + Writer output = new BufferedWriter(new FileWriter(stressOutFile, true)); + output.write("Camera and video recorder preview switching\n"); + output.write("Total number of loops:" + + NUMBER_OF_SWTICHING_LOOPS_BW_CAMERA_AND_RECORDER + "\n"); try { Log.v(TAG, "Start preview"); + output.write("No of loop: "); for (int i = 0; i < NUMBER_OF_SWTICHING_LOOPS_BW_CAMERA_AND_RECORDER; i++){ mCamera = Camera.open(); mCamera.setPreviewDisplay(mSurfaceHolder); @@ -147,11 +178,14 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me Log.v(TAG, "before release"); Thread.sleep(WAIT_TIME_CAMERA_TEST); mRecorder.release(); - Log.v(TAG, "release video recorder"); + Log.v(TAG, "release video recorder"); + output.write(", " + i); } } catch (Exception e) { Log.v(TAG, e.toString()); } + output.write("\n\n"); + output.close(); } //Stress test case for record a video and play right away. @@ -160,7 +194,13 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me String filename; SurfaceHolder mSurfaceHolder; mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); - try { + File stressOutFile = new File(MEDIA_STRESS_OUTPUT); + Writer output = new BufferedWriter(new FileWriter(stressOutFile, true)); + output.write("Video record and play back stress test:\n"); + output.write("Total number of loops:" + + NUMBER_OF_RECORDERANDPLAY_STRESS_LOOPS + "\n"); + try { + output.write("No of loop: "); for (int i = 0; i < NUMBER_OF_RECORDERANDPLAY_STRESS_LOOPS; i++){ filename = OUTPUT_FILE + i + OUTPUT_FILE_EXT; Log.v(TAG, filename); @@ -189,10 +229,13 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me mp.start(); Thread.sleep(WAIT_TIME_PLAYBACK); mp.release(); + output.write(", " + i); } } catch (Exception e) { Log.v(TAG, e.toString()); } + output.write("\n\n"); + output.close(); } } diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp index 25e31ee..de323b3 100644 --- a/opengl/libs/EGL/egl.cpp +++ b/opengl/libs/EGL/egl.cpp @@ -1052,23 +1052,25 @@ EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw, if (!validate_display_context(dpy, ctx)) return EGL_FALSE; + EGLSurface impl_draw = EGL_NO_SURFACE; + EGLSurface impl_read = EGL_NO_SURFACE; egl_context_t * const c = get_context(ctx); if (draw != EGL_NO_SURFACE) { egl_surface_t const * d = get_surface(draw); if (!d) return setError(EGL_BAD_SURFACE, EGL_FALSE); if (d->impl != c->impl) return setError(EGL_BAD_MATCH, EGL_FALSE); - draw = d->surface; + impl_draw = d->surface; } if (read != EGL_NO_SURFACE) { egl_surface_t const * r = get_surface(read); if (!r) return setError(EGL_BAD_SURFACE, EGL_FALSE); if (r->impl != c->impl) return setError(EGL_BAD_MATCH, EGL_FALSE); - read = r->surface; + impl_read = r->surface; } EGLBoolean result = c->cnx->hooks->egl.eglMakeCurrent( - dp->dpys[c->impl], draw, read, c->context); + dp->dpys[c->impl], impl_draw, impl_read, c->context); if (result == EGL_TRUE) { setGlThreadSpecific(c->cnx->hooks); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index b2f3557..660b469 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -592,6 +592,21 @@ class DatabaseHelper extends SQLiteOpenHelper { loadIntegerSetting(stmt, Settings.System.SCREEN_OFF_TIMEOUT, R.integer.def_screen_off_timeout); + // Set default cdma emergency tone + loadSetting(stmt, Settings.System.EMERGENCY_TONE, 0); + + // Set default cdma call auto retry + loadSetting(stmt, Settings.System.CALL_AUTO_RETRY, 0); + + // Set default cdma DTMF type + loadSetting(stmt, Settings.System.DTMF_TONE_TYPE_WHEN_DIALING, 0); + + // Set default hearing aid + loadSetting(stmt, Settings.System.HEARING_AID, 0); + + // Set default tty mode + loadSetting(stmt, Settings.System.TTY_MODE, 0); + loadBooleanSetting(stmt, Settings.System.AIRPLANE_MODE_ON, R.bool.def_airplane_mode_on); diff --git a/packages/TtsService/jni/android_tts_SynthProxy.cpp b/packages/TtsService/jni/android_tts_SynthProxy.cpp index 54d038a..8537cae 100644 --- a/packages/TtsService/jni/android_tts_SynthProxy.cpp +++ b/packages/TtsService/jni/android_tts_SynthProxy.cpp @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +#define LOG_NDEBUG 0 #include <stdio.h> #include <unistd.h> @@ -168,7 +168,7 @@ void prepAudioTrack(SynthProxyJniStorage* pJniData, static tts_callback_status ttsSynthDoneCB(void *& userdata, uint32_t rate, AudioSystem::audio_format format, int channel, int8_t *&wav, size_t &bufferSize, tts_synth_status status) { - LOGI("ttsSynthDoneCallback: %d bytes", bufferSize); + LOGV("ttsSynthDoneCallback: %d bytes", bufferSize); if (userdata == NULL){ LOGE("userdata == NULL"); @@ -178,7 +178,7 @@ static tts_callback_status ttsSynthDoneCB(void *& userdata, uint32_t rate, SynthProxyJniStorage* pJniData = (SynthProxyJniStorage*)(pForAfter->jniStorage); if (pForAfter->usageMode == USAGEMODE_PLAY_IMMEDIATELY){ - LOGI("Direct speech"); + LOGV("Direct speech"); if (wav == NULL) { delete pForAfter; @@ -189,26 +189,34 @@ static tts_callback_status ttsSynthDoneCB(void *& userdata, uint32_t rate, prepAudioTrack(pJniData, rate, format, channel); if (pJniData->mAudioOut) { pJniData->mAudioOut->write(wav, bufferSize); - LOGI("AudioTrack wrote: %d bytes", bufferSize); + //LOGV("AudioTrack wrote: %d bytes", bufferSize); } else { - LOGI("Can't play, null audiotrack"); + LOGE("Can't play, null audiotrack"); } } } else if (pForAfter->usageMode == USAGEMODE_WRITE_TO_FILE) { - LOGI("Save to file"); + LOGV("Save to file"); if (wav == NULL) { delete pForAfter; - LOGI("Null: speech has completed"); + LOGV("Null: speech has completed"); } if (bufferSize > 0){ fwrite(wav, 1, bufferSize, pForAfter->outputFile); } } - // TODO update to call back into the SynthProxy class through the + // Future update: + // For sync points in the speech, call back into the SynthProxy class through the // javaTTSFields.synthProxyMethodPost methode to notify - // playback has completed if the synthesis is done, i.e. - // if status == TTS_SYNTH_DONE - //delete pForAfter; + // playback has completed if the synthesis is done or if a marker has been reached. + + if (status == TTS_SYNTH_DONE) { + // this struct was allocated in the original android_tts_SynthProxy_speak call, + // all processing matching this call is now done. + LOGV("Speech synthesis done."); + delete pForAfter; + pForAfter = NULL; + return TTS_CALLBACK_HALT; + } // we don't update the wav (output) parameter as we'll let the next callback // write at the same location, we've consumed the data already, but we need @@ -289,14 +297,38 @@ android_tts_SynthProxy_setLanguage(JNIEnv *env, jobject thiz, jint jniData, variantNativeString); } env->ReleaseStringUTFChars(language, langNativeString); - env->ReleaseStringUTFChars(language, countryNativeString); - env->ReleaseStringUTFChars(language, variantNativeString); + env->ReleaseStringUTFChars(country, countryNativeString); + env->ReleaseStringUTFChars(variant, variantNativeString); +} + + +static void +android_tts_SynthProxy_loadLanguage(JNIEnv *env, jobject thiz, jint jniData, + jstring language, jstring country, jstring variant) +{ + if (jniData == 0) { + LOGE("android_tts_SynthProxy_loadLanguage(): invalid JNI data"); + return; + } + + SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; + const char *langNativeString = env->GetStringUTFChars(language, 0); + const char *countryNativeString = env->GetStringUTFChars(country, 0); + const char *variantNativeString = env->GetStringUTFChars(variant, 0); + // TODO check return codes + if (pSynthData->mNativeSynthInterface) { + pSynthData->mNativeSynthInterface->loadLanguage(langNativeString, countryNativeString, + variantNativeString); + } + env->ReleaseStringUTFChars(language, langNativeString); + env->ReleaseStringUTFChars(country, countryNativeString); + env->ReleaseStringUTFChars(variant, variantNativeString); } static void android_tts_SynthProxy_setSpeechRate(JNIEnv *env, jobject thiz, jint jniData, - int speechRate) + jint speechRate) { if (jniData == 0) { LOGE("android_tts_SynthProxy_setSpeechRate(): invalid JNI data"); @@ -316,6 +348,28 @@ android_tts_SynthProxy_setSpeechRate(JNIEnv *env, jobject thiz, jint jniData, } +static void +android_tts_SynthProxy_setPitch(JNIEnv *env, jobject thiz, jint jniData, + jint pitch) +{ + if (jniData == 0) { + LOGE("android_tts_SynthProxy_setPitch(): invalid JNI data"); + return; + } + + int bufSize = 10; + char buffer [bufSize]; + sprintf(buffer, "%d", pitch); + + SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; + LOGI("setting pitch to %d", pitch); + // TODO check return codes + if (pSynthData->mNativeSynthInterface) { + pSynthData->mNativeSynthInterface->setProperty("pitch", buffer, bufSize); + } +} + + // TODO: Refactor this to get rid of any assumptions about sample rate, etc. static void android_tts_SynthProxy_synthesizeToFile(JNIEnv *env, jobject thiz, jint jniData, @@ -537,10 +591,18 @@ static JNINativeMethod gMethods[] = { "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", (void*)android_tts_SynthProxy_setLanguage }, + { "native_loadLanguage", + "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", + (void*)android_tts_SynthProxy_loadLanguage + }, { "native_setSpeechRate", "(II)V", (void*)android_tts_SynthProxy_setSpeechRate }, + { "native_setPitch", + "(II)V", + (void*)android_tts_SynthProxy_setPitch + }, { "native_playAudioBuffer", "(III)V", (void*)android_tts_SynthProxy_playAudioBuffer diff --git a/packages/TtsService/src/android/tts/SynthProxy.java b/packages/TtsService/src/android/tts/SynthProxy.java index 364dc58..a8eaaa4 100755 --- a/packages/TtsService/src/android/tts/SynthProxy.java +++ b/packages/TtsService/src/android/tts/SynthProxy.java @@ -73,6 +73,13 @@ public class SynthProxy { public void setLanguage(String language, String country, String variant) { native_setLanguage(mJniData, language, country, variant); } + + /** + * Loads the language: it's not set, but prepared for use later. + */ + public void loadLanguage(String language, String country, String variant) { + native_loadLanguage(mJniData, language, country, variant); + } /** * Sets the speech rate @@ -81,6 +88,12 @@ public class SynthProxy { native_setSpeechRate(mJniData, speechRate); } + /** + * Sets the pitch of the synthesized voice + */ + public final void setPitch(int pitch) { + native_setPitch(mJniData, pitch); + } /** * Plays the given audio buffer @@ -143,8 +156,13 @@ public class SynthProxy { private native final void native_setLanguage(int jniData, String language, String country, String variant); + + private native final void native_loadLanguage(int jniData, String language, String country, + String variant); private native final void native_setSpeechRate(int jniData, int speechRate); + + private native final void native_setPitch(int jniData, int speechRate); // TODO add buffer format private native final void native_playAudioBuffer(int jniData, int bufferPointer, int bufferSize); diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java index e9c4ab7..421b2ca 100755 --- a/packages/TtsService/src/android/tts/TtsService.java +++ b/packages/TtsService/src/android/tts/TtsService.java @@ -47,12 +47,13 @@ import java.util.concurrent.locks.ReentrantLock; public class TtsService extends Service implements OnCompletionListener { private static class SpeechItem { - public static final int SPEECH = 0; - public static final int EARCON = 1; - public static final int SILENCE = 2; + public static final int TEXT = 0; + public static final int IPA = 1; + public static final int EARCON = 2; + public static final int SILENCE = 3; public String mText = null; public ArrayList<String> mParams = null; - public int mType = SPEECH; + public int mType = TEXT; public long mDuration = 0; public SpeechItem(String text, ArrayList<String> params, int itemType) { @@ -89,6 +90,8 @@ public class TtsService extends Service implements OnCompletionListener { } } + private static final int MAX_SPEECH_ITEM_CHAR_LENGTH = 4000; + private static final String ACTION = "android.intent.action.USE_TTS"; private static final String CATEGORY = "android.intent.category.TTS"; private static final String PKGNAME = "android.tts"; @@ -108,7 +111,6 @@ public class TtsService extends Service implements OnCompletionListener { private final ReentrantLock synthesizerLock = new ReentrantLock(); private SynthProxy nativeSynth; - @Override public void onCreate() { super.onCreate(); @@ -145,14 +147,11 @@ public class TtsService extends Service implements OnCompletionListener { private void setDefaultSettings() { - // TODO handle default language setLanguage("eng", "USA", ""); // speech rate setSpeechRate(getDefaultRate()); - - // TODO handle default pitch } @@ -171,6 +170,39 @@ public class TtsService extends Service implements OnCompletionListener { } + private String getDefaultLanguage() { + String defaultLang = android.provider.Settings.Secure.getString(mResolver, + android.provider.Settings.Secure.TTS_DEFAULT_LANG); + if (defaultLang == null) { + return TextToSpeech.Engine.FALLBACK_TTS_DEFAULT_LANG; + } else { + return defaultLang; + } + } + + + private String getDefaultCountry() { + String defaultCountry = android.provider.Settings.Secure.getString(mResolver, + android.provider.Settings.Secure.TTS_DEFAULT_COUNTRY); + if (defaultCountry == null) { + return TextToSpeech.Engine.FALLBACK_TTS_DEFAULT_COUNTRY; + } else { + return defaultCountry; + } + } + + + private String getDefaultLocVariant() { + String defaultVar = android.provider.Settings.Secure.getString(mResolver, + android.provider.Settings.Secure.TTS_DEFAULT_VARIANT); + if (defaultVar == null) { + return TextToSpeech.Engine.FALLBACK_TTS_DEFAULT_VARIANT; + } else { + return defaultVar; + } + } + + private void setSpeechRate(int rate) { if (isDefaultEnforced()) { nativeSynth.setSpeechRate(getDefaultRate()); @@ -179,16 +211,23 @@ public class TtsService extends Service implements OnCompletionListener { } } + + private void setPitch(int pitch) { + nativeSynth.setPitch(pitch); + } + + private void setLanguage(String lang, String country, String variant) { - Log.v("TTS", "TtsService.setLanguage("+lang+", "+country+", "+variant+")"); + Log.v("TTS", "TtsService.setLanguage(" + lang + ", " + country + ", " + variant + ")"); if (isDefaultEnforced()) { - nativeSynth.setLanguage(lang, country, variant); + nativeSynth.setLanguage(getDefaultLanguage(), getDefaultCountry(), + getDefaultLocVariant()); } else { - // TODO handle default language - nativeSynth.setLanguage("eng", "USA", ""); + nativeSynth.setLanguage(lang, country, variant); } } + /** * Adds a sound resource to the TTS. * @@ -259,7 +298,29 @@ public class TtsService extends Service implements OnCompletionListener { if (queueMode == 0) { stop(); } - mSpeechQueue.add(new SpeechItem(text, params, SpeechItem.SPEECH)); + mSpeechQueue.add(new SpeechItem(text, params, SpeechItem.TEXT)); + if (!mIsSpeaking) { + processSpeechQueue(); + } + } + + /** + * Speaks the given IPA text using the specified queueing mode and parameters. + * + * @param ipaText + * The IPA text that should be spoken + * @param queueMode + * 0 for no queue (interrupts all previous utterances), 1 for + * queued + * @param params + * An ArrayList of parameters. This is not implemented for all + * engines. + */ + private void speakIpa(String ipaText, int queueMode, ArrayList<String> params) { + if (queueMode == 0) { + stop(); + } + mSpeechQueue.add(new SpeechItem(ipaText, params, SpeechItem.IPA)); if (!mIsSpeaking) { processSpeechQueue(); } @@ -363,6 +424,7 @@ public class TtsService extends Service implements OnCompletionListener { if (synthAvailable) { synthesizerLock.unlock(); } + processSpeechQueue(); } } } @@ -384,6 +446,11 @@ public class TtsService extends Service implements OnCompletionListener { return sr; } + private void broadcastTtsQueueProcessingCompleted(){ + Intent i = new Intent(Intent.ACTION_TTS_QUEUE_PROCESSING_COMPLETED); + sendBroadcast(i); + } + private void dispatchSpeechCompletedCallbacks(String mark) { Log.i("TTS callback", "dispatch started"); // Broadcast to all clients the new value. @@ -400,6 +467,33 @@ public class TtsService extends Service implements OnCompletionListener { Log.i("TTS callback", "dispatch completed to " + N); } + private SpeechItem splitCurrentTextIfNeeded(SpeechItem currentSpeechItem){ + if (currentSpeechItem.mText.length() < MAX_SPEECH_ITEM_CHAR_LENGTH){ + return currentSpeechItem; + } else { + ArrayList<SpeechItem> splitItems = new ArrayList<SpeechItem>(); + int start = 0; + int end = start + MAX_SPEECH_ITEM_CHAR_LENGTH - 1; + String splitText; + SpeechItem splitItem; + while (end < currentSpeechItem.mText.length()){ + splitText = currentSpeechItem.mText.substring(start, end); + splitItem = new SpeechItem(splitText, null, SpeechItem.TEXT); + splitItems.add(splitItem); + start = end; + end = start + MAX_SPEECH_ITEM_CHAR_LENGTH - 1; + } + splitText = currentSpeechItem.mText.substring(start); + splitItem = new SpeechItem(splitText, null, SpeechItem.TEXT); + splitItems.add(splitItem); + mSpeechQueue.remove(0); + for (int i = splitItems.size() - 1; i >= 0; i--){ + mSpeechQueue.add(0, splitItems.get(i)); + } + return mSpeechQueue.get(0); + } + } + private void processSpeechQueue() { boolean speechQueueAvailable = false; try { @@ -409,11 +503,7 @@ public class TtsService extends Service implements OnCompletionListener { } if (mSpeechQueue.size() < 1) { mIsSpeaking = false; - // Dispatch a completion here as this is the - // only place where speech completes normally. - // Nothing left to say in the queue is a special case - // that is always a "mark" - associated text is null. - dispatchSpeechCompletedCallbacks(""); + broadcastTtsQueueProcessingCompleted(); return; } @@ -424,11 +514,12 @@ public class TtsService extends Service implements OnCompletionListener { // processSpeechQueue to continue running the queue Log.i("TTS processing: ", currentSpeechItem.mText); if (sr == null) { - if (currentSpeechItem.mType == SpeechItem.SPEECH) { - // TODO: Split text up into smaller chunks before accepting - // them for processing. + if (currentSpeechItem.mType == SpeechItem.TEXT) { + currentSpeechItem = splitCurrentTextIfNeeded(currentSpeechItem); speakInternalOnly(currentSpeechItem.mText, currentSpeechItem.mParams); + } else if (currentSpeechItem.mType == SpeechItem.IPA) { + // TODO Implement IPA support } else { // This is either silence or an earcon that was missing silence(currentSpeechItem.mDuration); @@ -494,8 +585,7 @@ public class TtsService extends Service implements OnCompletionListener { } /** - * Synthesizes the given text using the specified queuing mode and - * parameters. + * Synthesizes the given text to a file using the specified parameters. * * @param text * The String of text that should be synthesized @@ -540,6 +630,52 @@ public class TtsService extends Service implements OnCompletionListener { return true; } + /** + * Synthesizes the given IPA text to a file using the specified parameters. + * + * @param ipaText + * The String of IPA text that should be synthesized + * @param params + * An ArrayList of parameters. The first element of this array + * controls the type of voice to use. + * @param filename + * The string that gives the full output filename; it should be + * something like "/sdcard/myappsounds/mysound.wav". + * @return A boolean that indicates if the synthesis succeeded + */ + private boolean synthesizeIpaToFile(String ipaText, ArrayList<String> params, + String filename, boolean calledFromApi) { + // Only stop everything if this is a call made by an outside app trying + // to + // use the API. Do NOT stop if this is a call from within the service as + // clearing the speech queue here would be a mistake. + if (calledFromApi) { + stop(); + } + Log.i("TTS", "Synthesizing IPA to " + filename); + boolean synthAvailable = false; + try { + synthAvailable = synthesizerLock.tryLock(); + if (!synthAvailable) { + return false; + } + // Don't allow a filename that is too long + // TODO use platform constant + if (filename.length() > 250) { + return false; + } + // TODO: Add nativeSynth.synthesizeIpaToFile(text, filename); + } finally { + // This check is needed because finally will always run; even if the + // method returns somewhere in the try block. + if (synthAvailable) { + synthesizerLock.unlock(); + } + } + Log.i("TTS", "Completed synthesis for " + filename); + return true; + } + @Override public IBinder onBind(Intent intent) { if (ACTION.equals(intent.getAction())) { @@ -586,6 +722,27 @@ public class TtsService extends Service implements OnCompletionListener { } /** + * Speaks the given IPA text using the specified queueing mode and + * parameters. + * + * @param ipaText + * The IPA text that should be spoken + * @param queueMode + * 0 for no queue (interrupts all previous utterances), 1 for + * queued + * @param params + * An ArrayList of parameters. The first element of this + * array controls the type of voice to use. + */ + public void speakIpa(String ipaText, int queueMode, String[] params) { + ArrayList<String> speakingParams = new ArrayList<String>(); + if (params != null) { + speakingParams = new ArrayList<String>(Arrays.asList(params)); + } + mSelf.speakIpa(ipaText, queueMode, speakingParams); + } + + /** * Plays the earcon using the specified queueing mode and parameters. * * @param earcon @@ -706,6 +863,17 @@ public class TtsService extends Service implements OnCompletionListener { } /** + * Sets the pitch for the TTS. Note that this will only have an + * effect on synthesized speech; it will not affect pre-recorded speech. + * + * @param pitch + * The pitch that should be used for the synthesized voice + */ + public void setPitch(int pitch) { + mSelf.setPitch(pitch); + } + + /** * Sets the speech rate for the TTS, which affects the synthesized voice. * * @param lang the three letter ISO language code. @@ -717,7 +885,7 @@ public class TtsService extends Service implements OnCompletionListener { } /** - * Speaks the given text using the specified queueing mode and + * Synthesizes the given text to a file using the specified * parameters. * * @param text @@ -738,6 +906,29 @@ public class TtsService extends Service implements OnCompletionListener { } return mSelf.synthesizeToFile(text, speakingParams, filename, true); } + + /** + * Synthesizes the given IPA text to a file using the specified + * parameters. + * + * @param ipaText + * The String of IPA text that should be synthesized + * @param params + * An ArrayList of parameters. The first element of this + * array controls the type of voice to use. + * @param filename + * The string that gives the full output filename; it should + * be something like "/sdcard/myappsounds/mysound.wav". + * @return A boolean that indicates if the synthesis succeeded + */ + public boolean synthesizeIpaToFile(String ipaText, String[] params, + String filename) { + ArrayList<String> speakingParams = new ArrayList<String>(); + if (params != null) { + speakingParams = new ArrayList<String>(Arrays.asList(params)); + } + return mSelf.synthesizeIpaToFile(ipaText, speakingParams, filename, true); + } }; } diff --git a/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java b/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java index a12db8c..2ad218f 100644 --- a/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java +++ b/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, The Android Open Source Project + * 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. diff --git a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java index 8358c5c..877fa6b 100644 --- a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java +++ b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, The Android Open Source Project + * 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. @@ -25,8 +25,7 @@ import java.io.IOException; * The service that manages the L2TP-over-IPSec VPN connection. */ class L2tpIpsecService extends VpnService<L2tpIpsecProfile> { - private static final String IPSEC_SERVICE = "racoon"; - private static final String L2TP_SERVICE = "mtpd"; + private static final String IPSEC_DAEMON = "racoon"; @Override protected void connect(String serverIp, String username, String password) @@ -34,7 +33,7 @@ class L2tpIpsecService extends VpnService<L2tpIpsecProfile> { String hostIp = getHostIp(); // IPSEC - AndroidServiceProxy ipsecService = startService(IPSEC_SERVICE); + AndroidServiceProxy ipsecService = startService(IPSEC_DAEMON); ipsecService.sendCommand( String.format("SETKEY %s %s", hostIp, serverIp)); ipsecService.sendCommand(String.format("SET_CERTS %s %s %s %s", @@ -42,11 +41,11 @@ class L2tpIpsecService extends VpnService<L2tpIpsecProfile> { getUserkeyPath())); // L2TP - AndroidServiceProxy l2tpService = startService(L2TP_SERVICE); - l2tpService.sendCommand2("l2tp", serverIp, "1701", "", - "file", getPppOptionFilePath(), - "name", username, - "password", password); + L2tpIpsecProfile p = getProfile(); + MtpdHelper.sendCommand(this, L2tpService.L2TP_DAEMON, serverIp, + L2tpService.L2TP_PORT, + (p.isSecretEnabled() ? p.getSecretString() : null), + username, password); } private String getCaCertPath() { diff --git a/packages/VpnServices/src/com/android/server/vpn/L2tpService.java b/packages/VpnServices/src/com/android/server/vpn/L2tpService.java index 9aad7a1..9273f35 100644 --- a/packages/VpnServices/src/com/android/server/vpn/L2tpService.java +++ b/packages/VpnServices/src/com/android/server/vpn/L2tpService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, The Android Open Source Project + * 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. @@ -24,19 +24,15 @@ import java.io.IOException; * The service that manages the L2TP VPN connection. */ class L2tpService extends VpnService<L2tpProfile> { - private static final String L2TP_SERVICE = "mtpd"; + static final String L2TP_DAEMON = "l2tp"; + static final String L2TP_PORT = "1701"; @Override protected void connect(String serverIp, String username, String password) throws IOException { - String hostIp = getHostIp(); - - // L2TP - AndroidServiceProxy l2tpService = startService(L2TP_SERVICE); - l2tpService.sendCommand2("l2tp", serverIp, "1701", "", - "file", getPppOptionFilePath(), - "name", username, - "password", password); + L2tpProfile p = getProfile(); + MtpdHelper.sendCommand(this, L2TP_DAEMON, serverIp, L2TP_PORT, + (p.isSecretEnabled() ? p.getSecretString() : null), + username, password); } - } diff --git a/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java b/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java new file mode 100644 index 0000000..6160900 --- /dev/null +++ b/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vpn; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; + +/** + * A helper class for sending commands to the MTP daemon (mtpd). + */ +class MtpdHelper { + private static final String MTPD_SERVICE = "mtpd"; + private static final String VPN_LINKNAME = "vpn"; + private static final String PPP_ARGS_SEPARATOR = ""; + + static void sendCommand(VpnService<?> vpnService, String protocol, + String serverIp, String port, String secret, String username, + String password) throws IOException { + ArrayList<String> args = new ArrayList<String>(); + args.addAll(Arrays.asList(protocol, serverIp, port)); + if (secret != null) args.add(secret); + args.add(PPP_ARGS_SEPARATOR); + addPppArguments(vpnService, args, serverIp, username, password); + + AndroidServiceProxy mtpd = vpnService.startService(MTPD_SERVICE); + mtpd.sendCommand2(args.toArray(new String[args.size()])); + } + + private static void addPppArguments(VpnService<?> vpnService, + ArrayList<String> args, String serverIp, String username, + String password) throws IOException { + args.addAll(Arrays.asList( + "linkname", VPN_LINKNAME, + "name", username, + "password", password, + "ipparam", serverIp + "@" + vpnService.getGatewayIp(), + "refuse-eap", "nodefaultroute", "usepeerdns", + "idle", "1800", + "mtu", "1400", + "mru", "1400")); + } + + private MtpdHelper() { + } +} diff --git a/packages/VpnServices/src/com/android/server/vpn/NormalProcessProxy.java b/packages/VpnServices/src/com/android/server/vpn/NormalProcessProxy.java index f20d85f..f0bbc34 100644 --- a/packages/VpnServices/src/com/android/server/vpn/NormalProcessProxy.java +++ b/packages/VpnServices/src/com/android/server/vpn/NormalProcessProxy.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, The Android Open Source Project + * 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. diff --git a/packages/VpnServices/src/com/android/server/vpn/PptpService.java b/packages/VpnServices/src/com/android/server/vpn/PptpService.java new file mode 100644 index 0000000..01362a5 --- /dev/null +++ b/packages/VpnServices/src/com/android/server/vpn/PptpService.java @@ -0,0 +1,36 @@ +/* + * 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 android.net.vpn.PptpProfile; + +import java.io.IOException; + +/** + * The service that manages the PPTP VPN connection. + */ +class PptpService extends VpnService<PptpProfile> { + static final String PPTP_DAEMON = "pptp"; + static final String PPTP_PORT = "1723"; + @Override + protected void connect(String serverIp, String username, String password) + throws IOException { + MtpdHelper.sendCommand(this, PPTP_DAEMON, serverIp, PPTP_PORT, null, + username, password); + } + +} diff --git a/packages/VpnServices/src/com/android/server/vpn/ProcessProxy.java b/packages/VpnServices/src/com/android/server/vpn/ProcessProxy.java index 14d7521..50fbf4b 100644 --- a/packages/VpnServices/src/com/android/server/vpn/ProcessProxy.java +++ b/packages/VpnServices/src/com/android/server/vpn/ProcessProxy.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, The Android Open Source Project + * 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. diff --git a/packages/VpnServices/src/com/android/server/vpn/VpnService.java b/packages/VpnServices/src/com/android/server/vpn/VpnService.java index fdefe9c..44127ff 100644 --- a/packages/VpnServices/src/com/android/server/vpn/VpnService.java +++ b/packages/VpnServices/src/com/android/server/vpn/VpnService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, The Android Open Source Project + * 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. @@ -20,6 +20,7 @@ import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; +import android.net.NetworkUtils; import android.net.vpn.VpnManager; import android.net.vpn.VpnProfile; import android.net.vpn.VpnState; @@ -31,8 +32,10 @@ import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.NetworkInterface; import java.net.Socket; import java.util.ArrayList; +import java.util.Enumeration; import java.util.List; /** @@ -153,16 +156,25 @@ abstract class VpnService<E extends VpnProfile> { } /** - * Returns the IP of the specified host name. + * Returns the IP address of the specified host name. */ protected String getIp(String hostName) throws IOException { - InetAddress iaddr = InetAddress.getByName(hostName); - byte[] aa = iaddr.getAddress(); - StringBuilder sb = new StringBuilder().append(byteToInt(aa[0])); - for (int i = 1; i < aa.length; i++) { - sb.append(".").append(byteToInt(aa[i])); + return InetAddress.getByName(hostName).getHostAddress(); + } + + /** + * Returns the IP address of the default gateway. + */ + protected String getGatewayIp() throws IOException { + Enumeration<NetworkInterface> ifces = + NetworkInterface.getNetworkInterfaces(); + for (; ifces.hasMoreElements(); ) { + NetworkInterface ni = ifces.nextElement(); + int gateway = NetworkUtils.getDefaultRoute(ni.getName()); + if (gateway == 0) continue; + return toInetAddress(gateway).getHostAddress(); } - return sb.toString(); + throw new IOException("Default gateway is not available"); } /** @@ -170,7 +182,7 @@ abstract class VpnService<E extends VpnProfile> { * connection is established. */ protected String getConnectMonitorFile() { - return "/etc/ppp/ip-up"; + return "/etc/ppp/ip-up-vpn"; } /** @@ -461,12 +473,18 @@ abstract class VpnService<E extends VpnProfile> { } private String reallyGetHostIp() throws IOException { - Socket s = new Socket(); - s.connect(new InetSocketAddress("www.google.com", 80), DNS_TIMEOUT); - String ipAddress = s.getLocalAddress().getHostAddress(); - Log.d(TAG, "Host IP: " + ipAddress); - s.close(); - return ipAddress; + Enumeration<NetworkInterface> ifces = + NetworkInterface.getNetworkInterfaces(); + for (; ifces.hasMoreElements(); ) { + NetworkInterface ni = ifces.nextElement(); + int gateway = NetworkUtils.getDefaultRoute(ni.getName()); + if (gateway == 0) continue; + Enumeration<InetAddress> addrs = ni.getInetAddresses(); + for (; addrs.hasMoreElements(); ) { + return addrs.nextElement().getHostAddress(); + } + } + throw new IOException("Host IP is not available"); } private String getProfileSubpath(String subpath) throws IOException { @@ -496,8 +514,13 @@ abstract class VpnService<E extends VpnProfile> { return ((message == null) || (message.length() == 0)); } - private static int byteToInt(byte b) { - return ((int) b) & 0x0FF; + private InetAddress toInetAddress(int addr) throws IOException { + byte[] aa = new byte[4]; + for (int i= 0; i < aa.length; i++) { + aa[i] = (byte) (addr & 0x0FF); + addr >>= 8; + } + return InetAddress.getByAddress(aa); } private class ServiceHelper implements ProcessProxy.Callback { diff --git a/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java b/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java index 7195ea6..63fc858 100644 --- a/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java +++ b/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, The Android Open Source Project + * 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. diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index 197404e..bbebba3 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -47,6 +47,7 @@ import android.util.Log; import android.util.SparseArray; import android.backup.IBackupManager; +import android.backup.IRestoreObserver; import android.backup.IRestoreSession; import android.backup.BackupManager; import android.backup.RestoreSet; @@ -76,12 +77,12 @@ class BackupManagerService extends IBackupManager.Stub { private static final boolean DEBUG = true; // Default time to wait after data changes before we back up the data - private static final long COLLECTION_INTERVAL = 1000; - //private static final long COLLECTION_INTERVAL = 3 * 60 * 1000; + private static final long COLLECTION_INTERVAL = 3 * 60 * 1000; private static final int MSG_RUN_BACKUP = 1; private static final int MSG_RUN_FULL_BACKUP = 2; private static final int MSG_RUN_RESTORE = 3; + private static final String RESTORE_OBSERVER_KEY = "_resOb"; // Timeout interval for deciding that a bind or clear-data has taken too long static final long TIMEOUT_INTERVAL = 10 * 1000; @@ -110,8 +111,8 @@ class BackupManagerService extends IBackupManager.Stub { // Backups that we haven't started yet. private HashMap<ApplicationInfo,BackupRequest> mPendingBackups = new HashMap<ApplicationInfo,BackupRequest>(); - // Do we need to back up the package manager metadata on the next pass? - private boolean mDoPackageManager; + + // Pseudoname that we use for the Package Manager metadata "package" private static final String PACKAGE_MANAGER_SENTINEL = "@pm@"; // locking around the pending-backup management @@ -134,7 +135,20 @@ class BackupManagerService extends IBackupManager.Stub { private IBackupTransport mLocalTransport, mGoogleTransport; private RestoreSession mActiveRestoreSession; - private File mStateDir; + private class RestoreParams { + public IBackupTransport transport; + public IRestoreObserver observer; + public long token; + + RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token) { + transport = _transport; + observer = _obs; + token = _token; + } + } + + // Where we keep our journal files and other bookkeeping + private File mBaseStateDir; private File mDataDir; private File mJournalDir; private File mJournal; @@ -146,13 +160,12 @@ class BackupManagerService extends IBackupManager.Stub { mActivityManager = ActivityManagerNative.getDefault(); // Set up our bookkeeping - mStateDir = new File(Environment.getDataDirectory(), "backup"); - mStateDir.mkdirs(); + mBaseStateDir = new File(Environment.getDataDirectory(), "backup"); mDataDir = Environment.getDownloadCacheDirectory(); // Set up the backup-request journaling - mJournalDir = new File(mStateDir, "pending"); - mJournalDir.mkdirs(); + mJournalDir = new File(mBaseStateDir, "pending"); + mJournalDir.mkdirs(); // creates mBaseStateDir along the way makeJournalLocked(); // okay because no other threads are running yet // Build our mapping of uid to backup client services. This implicitly @@ -165,8 +178,7 @@ class BackupManagerService extends IBackupManager.Stub { // Set up our transport options and initialize the default transport // TODO: Have transports register themselves somehow? // TODO: Don't create transports that we don't need to? - mTransportId = BackupManager.TRANSPORT_LOCAL; - //mTransportId = BackupManager.TRANSPORT_GOOGLE; + mTransportId = BackupManager.TRANSPORT_GOOGLE; mLocalTransport = new LocalTransport(context); // This is actually pretty cheap mGoogleTransport = null; @@ -337,9 +349,8 @@ class BackupManagerService extends IBackupManager.Stub { case MSG_RUN_RESTORE: { - int token = msg.arg1; - IBackupTransport transport = (IBackupTransport)msg.obj; - (new PerformRestoreThread(transport, token)).start(); + RestoreParams params = (RestoreParams)msg.obj; + (new PerformRestoreThread(params.transport, params.observer, params.token)).start(); break; } } @@ -351,29 +362,29 @@ class BackupManagerService extends IBackupManager.Stub { void addPackageParticipantsLocked(String packageName) { // Look for apps that define the android:backupAgent attribute if (DEBUG) Log.v(TAG, "addPackageParticipantsLocked: " + packageName); - List<ApplicationInfo> targetApps = allAgentApps(); + List<PackageInfo> targetApps = allAgentPackages(); addPackageParticipantsLockedInner(packageName, targetApps); } private void addPackageParticipantsLockedInner(String packageName, - List<ApplicationInfo> targetApps) { + List<PackageInfo> targetPkgs) { if (DEBUG) { - Log.v(TAG, "Adding " + targetApps.size() + " backup participants:"); - for (ApplicationInfo a : targetApps) { - Log.v(TAG, " " + a + " agent=" + a.backupAgentName); + Log.v(TAG, "Adding " + targetPkgs.size() + " backup participants:"); + for (PackageInfo p : targetPkgs) { + Log.v(TAG, " " + p + " agent=" + p.applicationInfo.backupAgentName + + " uid=" + p.applicationInfo.uid); } } - for (ApplicationInfo app : targetApps) { - if (packageName == null || app.packageName.equals(packageName)) { - int uid = app.uid; + for (PackageInfo pkg : targetPkgs) { + if (packageName == null || pkg.packageName.equals(packageName)) { + int uid = pkg.applicationInfo.uid; HashSet<ApplicationInfo> set = mBackupParticipants.get(uid); if (set == null) { set = new HashSet<ApplicationInfo>(); mBackupParticipants.put(uid, set); } - set.add(app); - backUpPackageManagerData(); + set.add(pkg.applicationInfo); } } } @@ -382,67 +393,66 @@ class BackupManagerService extends IBackupManager.Stub { // 'packageName' is null, *all* participating apps will be removed. void removePackageParticipantsLocked(String packageName) { if (DEBUG) Log.v(TAG, "removePackageParticipantsLocked: " + packageName); - List<ApplicationInfo> allApps = null; + List<PackageInfo> allApps = null; if (packageName != null) { - allApps = new ArrayList<ApplicationInfo>(); + allApps = new ArrayList<PackageInfo>(); try { - ApplicationInfo app = mPackageManager.getApplicationInfo(packageName, 0); - allApps.add(app); + int flags = PackageManager.GET_SIGNATURES; + allApps.add(mPackageManager.getPackageInfo(packageName, flags)); } catch (Exception e) { - // just skip it + // just skip it (???) } } else { // all apps with agents - allApps = allAgentApps(); + allApps = allAgentPackages(); } removePackageParticipantsLockedInner(packageName, allApps); } private void removePackageParticipantsLockedInner(String packageName, - List<ApplicationInfo> agents) { + List<PackageInfo> agents) { if (DEBUG) { Log.v(TAG, "removePackageParticipantsLockedInner (" + packageName + ") removing " + agents.size() + " entries"); - for (ApplicationInfo a : agents) { - Log.v(TAG, " - " + a); + for (PackageInfo p : agents) { + Log.v(TAG, " - " + p); } } - for (ApplicationInfo app : agents) { - if (packageName == null || app.packageName.equals(packageName)) { - int uid = app.uid; + for (PackageInfo pkg : agents) { + if (packageName == null || pkg.packageName.equals(packageName)) { + int uid = pkg.applicationInfo.uid; HashSet<ApplicationInfo> set = mBackupParticipants.get(uid); if (set != null) { // Find the existing entry with the same package name, and remove it. // We can't just remove(app) because the instances are different. for (ApplicationInfo entry: set) { - if (entry.packageName.equals(app.packageName)) { + if (entry.packageName.equals(pkg.packageName)) { set.remove(entry); - backUpPackageManagerData(); break; } } if (set.size() == 0) { - mBackupParticipants.delete(uid); } + mBackupParticipants.delete(uid); + } } } } } // Returns the set of all applications that define an android:backupAgent attribute - private List<ApplicationInfo> allAgentApps() { + private List<PackageInfo> allAgentPackages() { // !!! TODO: cache this and regenerate only when necessary - List<ApplicationInfo> allApps = mPackageManager.getInstalledApplications(0); - int N = allApps.size(); - if (N > 0) { - for (int a = N-1; a >= 0; a--) { - ApplicationInfo app = allApps.get(a); - if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) - || app.backupAgentName == null) { - allApps.remove(a); - } + int flags = PackageManager.GET_SIGNATURES; + List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags); + int N = packages.size(); + for (int a = N-1; a >= 0; a--) { + ApplicationInfo app = packages.get(a).applicationInfo; + if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) + || app.backupAgentName == null) { + packages.remove(a); } } - return allApps; + return packages; } // Reset the given package's known backup participants. Unlike add/remove, the update @@ -455,19 +465,11 @@ class BackupManagerService extends IBackupManager.Stub { if (DEBUG) Log.v(TAG, "updatePackageParticipantsLocked: " + packageName); // brute force but small code size - List<ApplicationInfo> allApps = allAgentApps(); + List<PackageInfo> allApps = allAgentPackages(); removePackageParticipantsLockedInner(packageName, allApps); addPackageParticipantsLockedInner(packageName, allApps); } - private void backUpPackageManagerData() { - // No need to schedule a backup just for the metadata; just piggyback on - // the next actual data backup. - synchronized(this) { - mDoPackageManager = true; - } - } - // The queue lock should be held when scheduling a backup pass private void scheduleBackupPassLocked(long timeFromNowMillis) { mBackupHandler.removeMessages(MSG_RUN_BACKUP); @@ -565,6 +567,7 @@ class BackupManagerService extends IBackupManager.Stub { private static final String TAG = "PerformBackupThread"; IBackupTransport mTransport; ArrayList<BackupRequest> mQueue; + File mStateDir; File mJournal; public PerformBackupThread(IBackupTransport transport, ArrayList<BackupRequest> queue, @@ -572,52 +575,43 @@ class BackupManagerService extends IBackupManager.Stub { mTransport = transport; mQueue = queue; mJournal = journal; + + try { + mStateDir = new File(mBaseStateDir, transport.transportDirName()); + } catch (RemoteException e) { + // can't happen; the transport is local + } + mStateDir.mkdirs(); } @Override public void run() { if (DEBUG) Log.v(TAG, "Beginning backup of " + mQueue.size() + " targets"); - // start up the transport - try { - mTransport.startSession(); - } catch (Exception e) { - Log.e(TAG, "Error session transport"); - e.printStackTrace(); - return; - } - - // The transport is up and running. First, back up the package manager - // metadata if necessary - boolean doPackageManager; - synchronized (BackupManagerService.this) { - doPackageManager = mDoPackageManager; - mDoPackageManager = false; - } - if (doPackageManager) { - // The package manager doesn't have a proper <application> etc, but since - // it's running here in the system process we can just set up its agent - // directly and use a synthetic BackupRequest. - if (DEBUG) Log.i(TAG, "Running PM backup pass as well"); - - PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent( - mPackageManager, allAgentApps()); - BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false); - pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL; - processOneBackup(pmRequest, - IBackupAgent.Stub.asInterface(pmAgent.onBind()), - mTransport); - } + // The package manager doesn't have a proper <application> etc, but since + // it's running here in the system process we can just set up its agent + // directly and use a synthetic BackupRequest. We always run this pass + // because it's cheap and this way we guarantee that we don't get out of + // step even if we're selecting among various transports at run time. + PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent( + mPackageManager, allAgentPackages()); + BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false); + pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL; + processOneBackup(pmRequest, + IBackupAgent.Stub.asInterface(pmAgent.onBind()), + mTransport); // Now run all the backups in our queue doQueuedBackups(mTransport); // Finally, tear down the transport try { - mTransport.endSession(); - } catch (Exception e) { - Log.e(TAG, "Error ending transport"); - e.printStackTrace(); + if (!mTransport.finishBackup()) { + // STOPSHIP TODO: handle errors + Log.e(TAG, "Backup failure in finishBackup()"); + } + } catch (RemoteException e) { + Log.e(TAG, "Error in finishBackup()", e); } if (!mJournal.delete()) { @@ -712,24 +706,18 @@ class BackupManagerService extends IBackupManager.Stub { if (DEBUG) Log.v(TAG, "doBackup() success; calling transport"); backupData = ParcelFileDescriptor.open(backupDataName, ParcelFileDescriptor.MODE_READ_ONLY); - int error = transport.performBackup(packInfo, backupData); + if (!transport.performBackup(packInfo, backupData)) { + // STOPSHIP TODO: handle errors + Log.e(TAG, "Backup failure in performBackup()"); + } // !!! TODO: After successful transport, delete the now-stale data // and juggle the files so that next time the new state is passed //backupDataName.delete(); newStateName.renameTo(savedStateName); } - } catch (NameNotFoundException e) { - Log.e(TAG, "Package not found on backup: " + packageName); - } catch (FileNotFoundException fnf) { - Log.w(TAG, "File not found on backup: "); - fnf.printStackTrace(); - } catch (RemoteException e) { - Log.d(TAG, "Remote target " + request.appInfo.packageName + " threw during backup:"); - e.printStackTrace(); } catch (Exception e) { - Log.w(TAG, "Final exception guard in backup: "); - e.printStackTrace(); + Log.e(TAG, "Error backing up " + packageName, e); } } } @@ -737,25 +725,6 @@ class BackupManagerService extends IBackupManager.Stub { // ----- Restore handling ----- - // Is the given package restorable on this device? Returns the on-device app's - // ApplicationInfo struct if it is; null if not. - // - // !!! TODO: also consider signatures - PackageInfo isRestorable(PackageInfo packageInfo) { - if (packageInfo.packageName != null) { - try { - PackageInfo app = mPackageManager.getPackageInfo(packageInfo.packageName, - PackageManager.GET_SIGNATURES); - if ((app.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) { - return app; - } - } catch (Exception e) { - // doesn't exist on this device, or other error -- just ignore it. - } - } - return null; - } - private boolean signaturesMatch(Signature[] storedSigs, Signature[] deviceSigs) { // Allow unsigned apps, but not signed on one device and unsigned on the other // !!! TODO: is this the right policy? @@ -792,8 +761,10 @@ class BackupManagerService extends IBackupManager.Stub { class PerformRestoreThread extends Thread { private IBackupTransport mTransport; - private int mToken; + private IRestoreObserver mObserver; + private long mToken; private RestoreSet mImage; + private File mStateDir; class RestoreRequest { public PackageInfo app; @@ -805,9 +776,18 @@ class BackupManagerService extends IBackupManager.Stub { } } - PerformRestoreThread(IBackupTransport transport, int restoreSetToken) { + PerformRestoreThread(IBackupTransport transport, IRestoreObserver observer, + long restoreSetToken) { mTransport = transport; + mObserver = observer; mToken = restoreSetToken; + + try { + mStateDir = new File(mBaseStateDir, transport.transportDirName()); + } catch (RemoteException e) { + // can't happen; the transport is local + } + mStateDir.mkdirs(); } @Override @@ -816,130 +796,175 @@ class BackupManagerService extends IBackupManager.Stub { /** * Restore sequence: * - * 1. start up the transport session - * 2. get the restore set description for our identity - * 3. for each app in the restore set: + * 1. get the restore set description for our identity + * 2. for each app in the restore set: * 3.a. if it's restorable on this device, add it to the restore queue - * 4. for each app in the restore queue: - * 4.a. clear the app data - * 4.b. get the restore data for the app from the transport - * 4.c. launch the backup agent for the app - * 4.d. agent.doRestore() with the data from the server - * 4.e. unbind the agent [and kill the app?] - * 5. shut down the transport + * 3. for each app in the restore queue: + * 3.a. clear the app data + * 3.b. get the restore data for the app from the transport + * 3.c. launch the backup agent for the app + * 3.d. agent.doRestore() with the data from the server + * 3.e. unbind the agent [and kill the app?] + * 4. shut down the transport */ - int err = -1; + int error = -1; // assume error + + // build the set of apps to restore try { - err = mTransport.startSession(); - } catch (Exception e) { - Log.e(TAG, "Error starting transport for restore"); - e.printStackTrace(); - } + RestoreSet[] images = mTransport.getAvailableRestoreSets(); + if (images == null) { + // STOPSHIP TODO: Handle the failure somehow? + Log.e(TAG, "Error getting restore sets"); + return; + } - if (err == 0) { - // build the set of apps to restore - try { - RestoreSet[] images = mTransport.getAvailableRestoreSets(); - if (images.length > 0) { - // !!! TODO: pick out the set for this token - mImage = images[0]; - - // Pull the Package Manager metadata from the restore set first - PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent( - mPackageManager, allAgentApps()); - PackageInfo pmApp = new PackageInfo(); - pmApp.packageName = PACKAGE_MANAGER_SENTINEL; - // !!! TODO: version currently ignored when 'restoring' the PM metadata - processOneRestore(pmApp, 0, - IBackupAgent.Stub.asInterface(pmAgent.onBind())); - - // build the set of apps we will attempt to restore - PackageInfo[] packages = mTransport.getAppSet(mImage.token); - HashSet<RestoreRequest> appsToRestore = new HashSet<RestoreRequest>(); - for (PackageInfo pkg: packages) { - // get the real PackageManager idea of the package - PackageInfo app = isRestorable(pkg); - if (app != null) { - // Validate against the backed-up signature block, too - Metadata info = pmAgent.getRestoredMetadata(app.packageName); - if (info != null) { - if (app.versionCode >= info.versionCode) { - if (DEBUG) Log.v(TAG, "Restore version " - + info.versionCode - + " compatible with app version " - + app.versionCode); - if (signaturesMatch(info.signatures, app.signatures)) { - appsToRestore.add( - new RestoreRequest(app, info.versionCode)); - } else { - Log.w(TAG, "Sig mismatch restoring " - + app.packageName); - } - } else { - Log.i(TAG, "Restore set for " + app.packageName - + " is too new [" + info.versionCode - + "] for installed app version " - + app.versionCode); - } - } else { - Log.d(TAG, "Unable to get metadata for " - + app.packageName); - } - } - } + if (images.length == 0) { + Log.i(TAG, "No restore sets available"); + return; + } + + mImage = images[0]; + + // Get the list of all packages which have backup enabled. + // (Include the Package Manager metadata pseudo-package first.) + ArrayList<PackageInfo> restorePackages = new ArrayList<PackageInfo>(); + PackageInfo omPackage = new PackageInfo(); + omPackage.packageName = PACKAGE_MANAGER_SENTINEL; + restorePackages.add(omPackage); - // now run the restore queue - doQueuedRestores(appsToRestore); + List<PackageInfo> agentPackages = allAgentPackages(); + restorePackages.addAll(agentPackages); + + // let the observer know that we're running + if (mObserver != null) { + try { + // !!! TODO: get an actual count from the transport after + // its startRestore() runs? + mObserver.restoreStarting(restorePackages.size()); + } catch (RemoteException e) { + Log.d(TAG, "Restore observer died at restoreStarting"); + mObserver = null; } - } catch (RemoteException e) { - // can't happen; transports run locally } - // done; shut down the transport - try { - mTransport.endSession(); - } catch (Exception e) { - Log.e(TAG, "Error ending transport for restore"); - e.printStackTrace(); + if (!mTransport.startRestore(mToken, restorePackages.toArray(new PackageInfo[0]))) { + // STOPSHIP TODO: Handle the failure somehow? + Log.e(TAG, "Error starting restore operation"); + return; } - } - // even if the initial session startup failed, report that we're done here - } + String packageName = mTransport.nextRestorePackage(); + if (packageName == null) { + // STOPSHIP TODO: Handle the failure somehow? + Log.e(TAG, "Error getting first restore package"); + return; + } else if (packageName.equals("")) { + Log.i(TAG, "No restore data available"); + return; + } else if (!packageName.equals(PACKAGE_MANAGER_SENTINEL)) { + Log.e(TAG, "Expected restore data for \"" + PACKAGE_MANAGER_SENTINEL + + "\", found only \"" + packageName + "\""); + return; + } + + // Pull the Package Manager metadata from the restore set first + PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent( + mPackageManager, agentPackages); + processOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(pmAgent.onBind())); + + int count = 0; + for (;;) { + packageName = mTransport.nextRestorePackage(); + if (packageName == null) { + // STOPSHIP TODO: Handle the failure somehow? + Log.e(TAG, "Error getting next restore package"); + return; + } else if (packageName.equals("")) { + break; + } - // restore each app in the queue - void doQueuedRestores(HashSet<RestoreRequest> appsToRestore) { - for (RestoreRequest req : appsToRestore) { - PackageInfo app = req.app; - Log.d(TAG, "starting agent for restore of " + app); + if (mObserver != null) { + ++count; + try { + mObserver.onUpdate(count); + } catch (RemoteException e) { + Log.d(TAG, "Restore observer died in onUpdate"); + mObserver = null; + } + } - try { - // Remove the app's data first - clearApplicationDataSynchronous(app.packageName); + Metadata metaInfo = pmAgent.getRestoredMetadata(packageName); + if (metaInfo == null) { + Log.e(TAG, "Missing metadata for " + packageName); + continue; + } + + int flags = PackageManager.GET_SIGNATURES; + PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, flags); + if (metaInfo.versionCode > packageInfo.versionCode) { + Log.w(TAG, "Package " + packageName + + " restore version [" + metaInfo.versionCode + + "] is too new for installed version [" + + packageInfo.versionCode + "]"); + continue; + } + + if (!signaturesMatch(metaInfo.signatures, packageInfo.signatures)) { + Log.w(TAG, "Signature mismatch restoring " + packageName); + continue; + } + + if (DEBUG) Log.v(TAG, "Package " + packageName + + " restore version [" + metaInfo.versionCode + + "] is compatible with installed version [" + + packageInfo.versionCode + "]"); - // Now perform the restore into the clean app - IBackupAgent agent = bindToAgentSynchronous(app.applicationInfo, + // Now perform the actual restore + clearApplicationDataSynchronous(packageName); + IBackupAgent agent = bindToAgentSynchronous( + packageInfo.applicationInfo, IApplicationThread.BACKUP_MODE_RESTORE); - if (agent != null) { - processOneRestore(app, req.storedAppVersion, agent); + if (agent == null) { + Log.w(TAG, "Can't find backup agent for " + packageName); + continue; } - // unbind even on timeout, just in case - mActivityManager.unbindBackupAgent(app.applicationInfo); - } catch (SecurityException ex) { - // Try for the next one. - Log.d(TAG, "error in bind", ex); + try { + processOneRestore(packageInfo, metaInfo.versionCode, agent); + } finally { + // unbind even on timeout or failure, just in case + mActivityManager.unbindBackupAgent(packageInfo.applicationInfo); + } + } + + // if we get this far, report success to the observer + error = 0; + } catch (NameNotFoundException e) { + // STOPSHIP TODO: Handle the failure somehow? + Log.e(TAG, "Invalid paackage restoring data", e); + } catch (RemoteException e) { + // STOPSHIP TODO: Handle the failure somehow? + Log.e(TAG, "Error restoring data", e); + } finally { + try { + mTransport.finishRestore(); } catch (RemoteException e) { - // can't happen + Log.e(TAG, "Error finishing restore", e); } + if (mObserver != null) { + try { + mObserver.restoreFinished(error); + } catch (RemoteException e) { + Log.d(TAG, "Restore observer died at restoreFinished"); + } + } } } - // Do the guts of a restore of one application, derived from the 'mImage' - // restore set via the 'mTransport' transport. - void processOneRestore(PackageInfo app, int storedAppVersion, IBackupAgent agent) { + // Do the guts of a restore of one application, using mTransport.getRestoreData(). + void processOneRestore(PackageInfo app, int appVersionCode, IBackupAgent agent) { // !!! TODO: actually run the restore through mTransport final String packageName = app.packageName; @@ -954,11 +979,12 @@ class BackupManagerService extends IBackupManager.Stub { // Run the transport's restore pass // Run the target's backup pass - int err = -1; try { - err = mTransport.getRestoreData(mImage.token, app, backupData); - } catch (RemoteException e) { - // can't happen + if (!mTransport.getRestoreData(backupData)) { + // STOPSHIP TODO: Handle this error somehow? + Log.e(TAG, "Error getting restore data for " + packageName); + return; + } } finally { backupData.close(); } @@ -973,30 +999,18 @@ class BackupManagerService extends IBackupManager.Stub { backupData = ParcelFileDescriptor.open(backupDataName, ParcelFileDescriptor.MODE_READ_ONLY); - boolean success = false; try { - agent.doRestore(backupData, storedAppVersion, newState); - success = true; - } catch (Exception e) { - Log.e(TAG, "Restore failed for " + packageName); - e.printStackTrace(); + agent.doRestore(backupData, appVersionCode, newState); } finally { newState.close(); backupData.close(); } // if everything went okay, remember the recorded state now - if (success) { - File savedStateName = new File(mStateDir, packageName); - newStateName.renameTo(savedStateName); - } - } catch (FileNotFoundException fnfe) { - Log.v(TAG, "Couldn't open file for restore: " + fnfe); - } catch (IOException ioe) { - Log.e(TAG, "Unable to process restore file: " + ioe); + File savedStateName = new File(mStateDir, packageName); + newStateName.renameTo(savedStateName); } catch (Exception e) { - Log.e(TAG, "Final exception guard in restore:"); - e.printStackTrace(); + Log.e(TAG, "Error restoring data for " + packageName, e); } } } @@ -1183,15 +1197,15 @@ class BackupManagerService extends IBackupManager.Stub { } } - public int performRestore(int token) throws android.os.RemoteException { + public int performRestore(long token, IRestoreObserver observer) + throws android.os.RemoteException { mContext.enforceCallingPermission("android.permission.BACKUP", "performRestore"); if (mRestoreSets != null) { for (int i = 0; i < mRestoreSets.length; i++) { if (token == mRestoreSets[i].token) { - Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE, - mRestoreTransport); - msg.arg1 = token; + Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); + msg.obj = new RestoreParams(mRestoreTransport, observer, token); mBackupHandler.sendMessage(msg); return 0; } @@ -1206,7 +1220,7 @@ class BackupManagerService extends IBackupManager.Stub { mContext.enforceCallingPermission("android.permission.BACKUP", "endRestoreSession"); - mRestoreTransport.endSession(); + mRestoreTransport.finishRestore(); mRestoreTransport = null; synchronized(BackupManagerService.this) { if (BackupManagerService.this.mActiveRestoreSession == this) { diff --git a/services/java/com/android/server/PackageManagerBackupAgent.java b/services/java/com/android/server/PackageManagerBackupAgent.java index d620eb1..66fb86d 100644 --- a/services/java/com/android/server/PackageManagerBackupAgent.java +++ b/services/java/com/android/server/PackageManagerBackupAgent.java @@ -57,7 +57,7 @@ public class PackageManagerBackupAgent extends BackupAgent { // is stored using the package name as a key) private static final String GLOBAL_METADATA_KEY = "@meta@"; - private List<ApplicationInfo> mAllApps; + private List<PackageInfo> mAllPackages; private PackageManager mPackageManager; private HashMap<String, Metadata> mRestoredSignatures; @@ -73,9 +73,9 @@ public class PackageManagerBackupAgent extends BackupAgent { // We're constructed with the set of applications that are participating // in backup. This set changes as apps are installed & removed. - PackageManagerBackupAgent(PackageManager packageMgr, List<ApplicationInfo> apps) { + PackageManagerBackupAgent(PackageManager packageMgr, List<PackageInfo> packages) { mPackageManager = packageMgr; - mAllApps = apps; + mAllPackages = packages; mRestoredSignatures = null; } @@ -118,8 +118,8 @@ public class PackageManagerBackupAgent extends BackupAgent { // For each app we have on device, see if we've backed it up yet. If not, // write its signature block to the output, keyed on the package name. - for (ApplicationInfo app : mAllApps) { - String packName = app.packageName; + for (PackageInfo pkg : mAllPackages) { + String packName = pkg.packageName; if (!existing.contains(packName)) { // We haven't stored this app's signatures yet, so we do that now try { @@ -186,7 +186,7 @@ public class PackageManagerBackupAgent extends BackupAgent { } // Finally, write the new state blob -- just the list of all apps we handled - writeStateFile(mAllApps, newState); + writeStateFile(mAllPackages, newState); } // "Restore" here is a misnomer. What we're really doing is reading back the @@ -327,7 +327,7 @@ public class PackageManagerBackupAgent extends BackupAgent { } // Util: write out our new backup state file - private void writeStateFile(List<ApplicationInfo> apps, ParcelFileDescriptor stateFile) { + private void writeStateFile(List<PackageInfo> pkgs, ParcelFileDescriptor stateFile) { FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor()); DataOutputStream out = new DataOutputStream(outstream); @@ -338,8 +338,8 @@ public class PackageManagerBackupAgent extends BackupAgent { out.write(metaNameBuf); // now write all the app names too - for (ApplicationInfo app : apps) { - byte[] pkgNameBuf = app.packageName.getBytes(); + for (PackageInfo pkg : pkgs) { + byte[] pkgNameBuf = pkg.packageName.getBytes(); out.writeInt(pkgNameBuf.length); out.write(pkgNameBuf); } diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index 048669a..972aa98 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -968,35 +968,7 @@ class PackageManagerService extends IPackageManager.Stub { if (Config.LOGV) Log.v(TAG, "getActivityInfo " + component + ": " + a); if (a != null && mSettings.isEnabledLP(a.info, flags)) { - ActivityInfo ainfo = PackageParser.generateActivityInfo(a, flags); - if (ainfo != null) { - ApplicationInfo appInfo = getApplicationInfo(component.getPackageName(), - PackageManager.GET_SUPPORTS_DENSITIES); - if (appInfo != null && - (appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) == 0) { - // Check if the screen size is same as what the application expect. - CompatibilityInfo info = new CompatibilityInfo(appInfo); - DisplayMetrics metrics = new DisplayMetrics(); - metrics.setTo(mMetrics); - int orientation = mMetrics.widthPixels > mMetrics.heightPixels ? - Configuration.ORIENTATION_LANDSCAPE : - Configuration.ORIENTATION_PORTRAIT; - metrics.updateMetrics(info, orientation); - if (!info.mExpandable) { - // Don't allow an app that cannot expand to handle rotation. - ainfo.configChanges &= ~ ActivityInfo.CONFIG_ORIENTATION; - } else { - appInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS; - } - if (DEBUG_SETTINGS) { - Log.d(TAG, "component=" + component + - ", expandable:" + - ((appInfo.flags & - ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0)); - } - } - } - return ainfo; + return PackageParser.generateActivityInfo(a, flags); } if (mResolveComponentName.equals(component)) { return mResolveActivity; diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java index b601ece..9f2856c 100644 --- a/services/java/com/android/server/TelephonyRegistry.java +++ b/services/java/com/android/server/TelephonyRegistry.java @@ -462,6 +462,15 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { // private void broadcastServiceStateChanged(ServiceState state) { + long ident = Binder.clearCallingIdentity(); + try { + mBatteryStats.noteAirplaneMode(state.getState() == ServiceState.STATE_POWER_OFF); + } catch (RemoteException re) { + // Can't do much + } finally { + Binder.restoreCallingIdentity(ident); + } + Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED); Bundle data = new Bundle(); state.fillInNotifierBundle(data); diff --git a/services/java/com/android/server/WallpaperService.java b/services/java/com/android/server/WallpaperService.java index 5532894..d921baf 100644 --- a/services/java/com/android/server/WallpaperService.java +++ b/services/java/com/android/server/WallpaperService.java @@ -18,8 +18,10 @@ package com.android.server; import static android.os.FileObserver.*; import static android.os.ParcelFileDescriptor.*; + import android.app.IWallpaperService; import android.app.IWallpaperServiceCallback; +import android.backup.BackupManager; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; @@ -154,7 +156,16 @@ class WallpaperService extends IWallpaperService.Stub { public ParcelFileDescriptor setWallpaper() { checkPermission(android.Manifest.permission.SET_WALLPAPER); try { - return ParcelFileDescriptor.open(WALLPAPER_FILE, MODE_CREATE|MODE_READ_WRITE); + ParcelFileDescriptor fd = ParcelFileDescriptor.open(WALLPAPER_FILE, + MODE_CREATE|MODE_READ_WRITE); + + // changing the wallpaper means we'll need to back up the new one + long origId = Binder.clearCallingIdentity(); + BackupManager bm = new BackupManager(mContext); + bm.dataChanged(); + Binder.restoreCallingIdentity(origId); + + return fd; } catch (FileNotFoundException e) { if (Config.LOGD) Log.d(TAG, "Error setting wallpaper", e); } diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java index fef3598..68bf4fb 100644 --- a/services/java/com/android/server/Watchdog.java +++ b/services/java/com/android/server/Watchdog.java @@ -504,6 +504,7 @@ public class Watchdog extends Thread { if (mPhoneMemMonitor.checkLocked(curTime, mPhonePid, mPhonePss)) { // Just kill the phone process and let it restart. + Log.i(TAG, "Watchdog is killing the phone process"); Process.killProcess(mPhonePid); } } else { @@ -848,6 +849,7 @@ public class Watchdog extends Thread { // Only kill the process if the debugger is not attached. if (!Debug.isDebuggerConnected()) { + Log.i(TAG, "Watchdog is killing the system process"); Process.killProcess(Process.myPid()); } } diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 3b47ae7..5ea7504 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -77,6 +77,7 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.TokenWatcher; import android.provider.Settings; +import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; import android.util.SparseIntArray; @@ -415,7 +416,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo final Rect mTempRect = new Rect(); final Configuration mTempConfiguration = new Configuration(); - + int screenLayout = Configuration.SCREENLAYOUT_UNDEFINED; + public static WindowManagerService main(Context context, PowerManagerService pm, boolean haveInputMethods) { WMThread thr = new WMThread(context, pm, haveInputMethods); @@ -3724,6 +3726,40 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo orientation = Configuration.ORIENTATION_LANDSCAPE; } config.orientation = orientation; + + if (screenLayout == Configuration.SCREENLAYOUT_UNDEFINED) { + // Note we only do this once because at this point we don't + // expect the screen to change in this way at runtime, and want + // to avoid all of this computation for every config change. + DisplayMetrics dm = new DisplayMetrics(); + mDisplay.getMetrics(dm); + int longSize = dw; + int shortSize = dh; + if (longSize < shortSize) { + int tmp = longSize; + longSize = shortSize; + shortSize = tmp; + } + longSize = (int)(longSize/dm.density); + shortSize = (int)(shortSize/dm.density); + + // These semi-magic numbers define our compatibility modes for + // applications with different screens. Don't change unless you + // make sure to test lots and lots of apps! + if (longSize < 470) { + // This is shorter than an HVGA normal density screen (which + // is 480 pixels on its long side). + screenLayout = Configuration.SCREENLAYOUT_SMALL; + } else if (longSize > 490 && shortSize > 330) { + // This is larger than an HVGA normal density screen (which + // is 480x320 pixels). + screenLayout = Configuration.SCREENLAYOUT_LARGE; + } else { + screenLayout = Configuration.SCREENLAYOUT_NORMAL; + } + } + config.screenLayout = screenLayout; + config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO; config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO; mPolicy.adjustConfigurationLw(config); diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 16bb29d..6d04b6b 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -61,6 +61,7 @@ import android.content.pm.ServiceInfo; import android.content.res.Configuration; import android.graphics.Bitmap; import android.net.Uri; +import android.os.BatteryStats; import android.os.Binder; import android.os.Bundle; import android.os.Environment; @@ -1517,7 +1518,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - synchronized(mBatteryStatsService.getActiveStatistics()) { + final BatteryStatsImpl bstats = mBatteryStatsService.getActiveStatistics(); + synchronized(bstats) { synchronized(mPidsSelfLocked) { if (haveNewCpuStats) { if (mBatteryStatsService.isOnBattery()) { @@ -1529,12 +1531,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (pr != null) { BatteryStatsImpl.Uid.Proc ps = pr.batteryStats; ps.addCpuTimeLocked(st.rel_utime, st.rel_stime); + } else { + BatteryStatsImpl.Uid.Proc ps = + bstats.getProcessStatsLocked(st.name, st.pid); + if (ps != null) { + ps.addCpuTimeLocked(st.rel_utime, st.rel_stime); + } } } } } } - + if (mLastWriteTime < (now-BATTERY_STATS_TIME)) { mLastWriteTime = now; mBatteryStatsService.getActiveStatistics().writeLocked(); @@ -10464,8 +10472,17 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // done with this agent public void unbindBackupAgent(ApplicationInfo appInfo) { if (DEBUG_BACKUP) Log.v(TAG, "unbindBackupAgent: " + appInfo); + if (appInfo == null) { + Log.w(TAG, "unbind backup agent for null app"); + return; + } synchronized(this) { + if (mBackupAppName == null) { + Log.w(TAG, "Unbinding backup agent with no active backup"); + return; + } + if (!mBackupAppName.equals(appInfo.packageName)) { Log.e(TAG, "Unbind of " + appInfo + " but is not the current backup target"); return; @@ -12592,51 +12609,63 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } public boolean profileControl(String process, boolean start, - String path) throws RemoteException { + String path, ParcelFileDescriptor fd) throws RemoteException { - synchronized (this) { - // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to - // its own permission. - if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires permission " - + android.Manifest.permission.SET_ACTIVITY_WATCHER); - } - - ProcessRecord proc = null; - try { - int pid = Integer.parseInt(process); - synchronized (mPidsSelfLocked) { - proc = mPidsSelfLocked.get(pid); + try { + synchronized (this) { + // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to + // its own permission. + if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires permission " + + android.Manifest.permission.SET_ACTIVITY_WATCHER); } - } catch (NumberFormatException e) { - } - - if (proc == null) { - HashMap<String, SparseArray<ProcessRecord>> all - = mProcessNames.getMap(); - SparseArray<ProcessRecord> procs = all.get(process); - if (procs != null && procs.size() > 0) { - proc = procs.valueAt(0); + + if (start && fd == null) { + throw new IllegalArgumentException("null fd"); } - } - - if (proc == null || proc.thread == null) { - throw new IllegalArgumentException("Unknown process: " + process); - } - - boolean isSecure = "1".equals(SystemProperties.get(SYSTEM_SECURE, "0")); - if (isSecure) { - if ((proc.info.flags&ApplicationInfo.FLAG_DEBUGGABLE) == 0) { - throw new SecurityException("Process not debuggable: " + proc); + + ProcessRecord proc = null; + try { + int pid = Integer.parseInt(process); + synchronized (mPidsSelfLocked) { + proc = mPidsSelfLocked.get(pid); + } + } catch (NumberFormatException e) { + } + + if (proc == null) { + HashMap<String, SparseArray<ProcessRecord>> all + = mProcessNames.getMap(); + SparseArray<ProcessRecord> procs = all.get(process); + if (procs != null && procs.size() > 0) { + proc = procs.valueAt(0); + } + } + + if (proc == null || proc.thread == null) { + throw new IllegalArgumentException("Unknown process: " + process); + } + + boolean isSecure = "1".equals(SystemProperties.get(SYSTEM_SECURE, "0")); + if (isSecure) { + if ((proc.info.flags&ApplicationInfo.FLAG_DEBUGGABLE) == 0) { + throw new SecurityException("Process not debuggable: " + proc); + } } - } - try { - proc.thread.profilerControl(start, path); + proc.thread.profilerControl(start, path, fd); + fd = null; return true; - } catch (RemoteException e) { - throw new IllegalStateException("Process disappeared"); + } + } catch (RemoteException e) { + throw new IllegalStateException("Process disappeared"); + } finally { + if (fd != null) { + try { + fd.close(); + } catch (IOException e) { + } } } } diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java index 9a4b642..39a1ee0 100644 --- a/services/java/com/android/server/am/BatteryStatsService.java +++ b/services/java/com/android/server/am/BatteryStatsService.java @@ -16,9 +16,6 @@ package com.android.server.am; -import com.android.internal.app.IBatteryStats; -import com.android.internal.os.BatteryStatsImpl; - import android.content.Context; import android.os.Binder; import android.os.IBinder; @@ -26,8 +23,12 @@ import android.os.Parcel; import android.os.Process; import android.os.ServiceManager; import android.telephony.SignalStrength; +import android.telephony.TelephonyManager; import android.util.Log; +import com.android.internal.app.IBatteryStats; +import com.android.internal.os.BatteryStatsImpl; + import java.io.FileDescriptor; import java.io.PrintWriter; @@ -191,7 +192,14 @@ public final class BatteryStatsService extends IBatteryStats.Stub { mStats.notePhoneDataConnectionStateLocked(dataType, hasData); } } - + + public void noteAirplaneMode(boolean airplaneMode) { + enforceCallingPermission(); + synchronized (mStats) { + mStats.noteAirplaneModeLocked(airplaneMode); + } + } + public void noteWifiOn(int uid) { enforceCallingPermission(); synchronized (mStats) { diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index a79eb3a..bf5df88 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -612,6 +612,21 @@ public class TelephonyManager { } /** + * Returns the voice mail count. + * <p> + * Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * @hide + */ + public int getVoiceMessageCount() { + try { + return getITelephony().getVoiceMessageCount(); + } catch (RemoteException ex) { + } + return 0; + } + + /** * Retrieves the alphabetic identifier associated with the voice * mail number. * <p> diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 6e6f64c..d83b135 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -241,7 +241,7 @@ interface ITelephony { /** * Returns the unread count of voicemails */ - int getCountVoiceMessages(); + int getVoiceMessageCount(); } diff --git a/telephony/java/com/android/internal/telephony/IccRecords.java b/telephony/java/com/android/internal/telephony/IccRecords.java index 114094b..ea24c25 100644 --- a/telephony/java/com/android/internal/telephony/IccRecords.java +++ b/telephony/java/com/android/internal/telephony/IccRecords.java @@ -196,7 +196,7 @@ public abstract class IccRecords extends Handler implements IccConstants { * If not available (eg, on an older CPHS SIM) -1 is returned if * getVoiceMessageWaiting() is true */ - public int getCountVoiceMessages() { + public int getVoiceMessageCount() { return countVoiceMessages; } diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java index cdbfd61..c8d384d 100644 --- a/telephony/java/com/android/internal/telephony/Phone.java +++ b/telephony/java/com/android/internal/telephony/Phone.java @@ -831,7 +831,7 @@ public interface Phone { * Returns unread voicemail count. This count is shown when the voicemail * notification is expanded.<p> */ - int getCountVoiceMessages(); + int getVoiceMessageCount(); /** * Returns the alpha tag associated with the voice mail number. diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java index 0783fe0..a26e729 100644 --- a/telephony/java/com/android/internal/telephony/PhoneBase.java +++ b/telephony/java/com/android/internal/telephony/PhoneBase.java @@ -631,6 +631,11 @@ public abstract class PhoneBase implements Phone { mNotifier.notifyDataActivity(this); } + public void notifyMessageWaitingIndicator() { + // This function is added to send the notification to DefaultPhoneNotifier. + mNotifier.notifyMessageWaitingChanged(this); + } + public void notifyDataConnection(String reason) { mNotifier.notifyDataConnection(this, reason); } @@ -638,7 +643,7 @@ public abstract class PhoneBase implements Phone { public abstract String getPhoneName(); /** @hide */ - public int getCountVoiceMessages(){ + public int getVoiceMessageCount(){ return 0; } diff --git a/telephony/java/com/android/internal/telephony/PhoneProxy.java b/telephony/java/com/android/internal/telephony/PhoneProxy.java index 4bb24dc..5b3c8dd 100644 --- a/telephony/java/com/android/internal/telephony/PhoneProxy.java +++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java @@ -435,8 +435,8 @@ public class PhoneProxy extends Handler implements Phone { } /** @hide */ - public int getCountVoiceMessages(){ - return mActivePhone.getCountVoiceMessages(); + public int getVoiceMessageCount(){ + return mActivePhone.getVoiceMessageCount(); } public String getVoiceMailAlphaTag() { diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java index 9f780c9..4db3e5b 100644 --- a/telephony/java/com/android/internal/telephony/RIL.java +++ b/telephony/java/com/android/internal/telephony/RIL.java @@ -1365,7 +1365,6 @@ public final class RIL extends BaseCommands implements CommandsInterface { RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE, result); - rr.mp.writeInt(2); rr.mp.writeInt(success ? 0 : 1); //RIL_CDMA_SMS_ErrorClass // cause code according to X.S004-550E rr.mp.writeInt(cause); diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java index a3016fa..890ea63 100644 --- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java @@ -78,6 +78,7 @@ public abstract class SMSDispatcher extends Handler { protected static final String[] RAW_PROJECTION = new String[] { "pdu", "sequence", + "destination_port", }; static final int MAIL_SEND_SMS = 1; @@ -289,7 +290,13 @@ public abstract class SMSDispatcher extends Handler { sms = (SmsMessage) ar.result; try { if (mStorageAvailable) { - dispatchMessage(sms.mWrappedSmsMessage); + int result = dispatchMessage(sms.mWrappedSmsMessage); + if (result != Activity.RESULT_OK) { + // RESULT_OK means that message was broadcast for app(s) to handle. + // Any other result, we should ack here. + boolean handled = (result == Intents.RESULT_SMS_HANDLED); + acknowledgeLastIncomingSms(handled, result, null); + } } else { acknowledgeLastIncomingSms(false, Intents.RESULT_SMS_OUT_OF_MEMORY, null); } @@ -469,8 +476,11 @@ public abstract class SMSDispatcher extends Handler { * Dispatches an incoming SMS messages. * * @param sms the incoming message from the phone + * @return a result code from {@link Telephony.Sms.Intents}, or + * {@link Activity#RESULT_OK} if the message has been broadcast + * to applications */ - protected abstract void dispatchMessage(SmsMessageBase sms); + protected abstract int dispatchMessage(SmsMessageBase sms); /** @@ -478,8 +488,11 @@ public abstract class SMSDispatcher extends Handler { * the part is stored for later processing. * * NOTE: concatRef (naturally) needs to be non-null, but portAddrs can be null. + * @return a result code from {@link Telephony.Sms.Intents}, or + * {@link Activity#RESULT_OK} if the message has been broadcast + * to applications */ - protected void processMessagePart(SmsMessageBase sms, + protected int processMessagePart(SmsMessageBase sms, SmsHeader.ConcatRef concatRef, SmsHeader.PortAddrs portAddrs) { // Lookup all other related parts @@ -506,8 +519,7 @@ public abstract class SMSDispatcher extends Handler { values.put("destination_port", portAddrs.destPort); } mResolver.insert(mRawUri, values); - acknowledgeLastIncomingSms(true, Intents.RESULT_SMS_HANDLED, null); - return; + return Intents.RESULT_SMS_HANDLED; } // All the parts are in place, deal with them @@ -529,8 +541,7 @@ public abstract class SMSDispatcher extends Handler { } catch (SQLException e) { Log.e(TAG, "Can't access multipart SMS database", e); // TODO: Would OUT_OF_MEMORY be more appropriate? - acknowledgeLastIncomingSms(false, Intents.RESULT_SMS_GENERIC_ERROR, null); - return; + return Intents.RESULT_SMS_GENERIC_ERROR; } finally { if (cursor != null) cursor.close(); } @@ -555,7 +566,7 @@ public abstract class SMSDispatcher extends Handler { output.write(data, 0, data.length); } // Handle the PUSH - mWapPush.dispatchWapPdu(output.toByteArray()); + return mWapPush.dispatchWapPdu(output.toByteArray()); } else { // The messages were sent to a port, so concoct a URI for it dispatchPortAddressedPdus(pdus, portAddrs.destPort); @@ -564,6 +575,7 @@ public abstract class SMSDispatcher extends Handler { // The messages were not sent to a port dispatchPdus(pdus); } + return Activity.RESULT_OK; } /** diff --git a/telephony/java/com/android/internal/telephony/WapPushOverSms.java b/telephony/java/com/android/internal/telephony/WapPushOverSms.java index a9aacda..9970940 100644 --- a/telephony/java/com/android/internal/telephony/WapPushOverSms.java +++ b/telephony/java/com/android/internal/telephony/WapPushOverSms.java @@ -16,8 +16,10 @@ package com.android.internal.telephony; +import android.app.Activity; import android.content.Context; import android.content.Intent; +import android.provider.Telephony; import android.provider.Telephony.Sms.Intents; import android.util.Config; import android.util.Log; @@ -51,8 +53,11 @@ public class WapPushOverSms { * wap-230-wsp-20010705-a section 8 for details on the WAP PDU format. * * @param pdu The WAP PDU, made up of one or more SMS PDUs + * @return a result code from {@link Telephony.Sms.Intents}, or + * {@link Activity#RESULT_OK} if the message has been broadcast + * to applications */ - public void dispatchWapPdu(byte[] pdu) { + public int dispatchWapPdu(byte[] pdu) { if (Config.LOGD) Log.d(LOG_TAG, "Rx: " + IccUtils.bytesToHexString(pdu)); @@ -64,7 +69,7 @@ public class WapPushOverSms { if ((pduType != WspTypeDecoder.PDU_TYPE_PUSH) && (pduType != WspTypeDecoder.PDU_TYPE_CONFIRMED_PUSH)) { if (Config.LOGD) Log.w(LOG_TAG, "Received non-PUSH WAP PDU. Type = " + pduType); - return; + return Intents.RESULT_SMS_HANDLED; } pduDecoder = new WspTypeDecoder(pdu); @@ -77,7 +82,7 @@ public class WapPushOverSms { */ if (pduDecoder.decodeUintvarInteger(index) == false) { if (Config.LOGD) Log.w(LOG_TAG, "Received PDU. Header Length error."); - return; + return Intents.RESULT_SMS_GENERIC_ERROR; } headerLength = (int)pduDecoder.getValue32(); index += pduDecoder.getDecodedDataLength(); @@ -98,7 +103,7 @@ public class WapPushOverSms { */ if (pduDecoder.decodeContentType(index) == false) { if (Config.LOGD) Log.w(LOG_TAG, "Received PDU. Header Content-Type error."); - return; + return Intents.RESULT_SMS_GENERIC_ERROR; } int binaryContentType; String mimeType = pduDecoder.getValueString(); @@ -128,7 +133,7 @@ public class WapPushOverSms { Log.w(LOG_TAG, "Received PDU. Unsupported Content-Type = " + binaryContentType); } - return; + return Intents.RESULT_SMS_HANDLED; } } else { if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_DRM_RIGHTS_XML)) { @@ -145,7 +150,7 @@ public class WapPushOverSms { binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_MMS; } else { if (Config.LOGD) Log.w(LOG_TAG, "Received PDU. Unknown Content-Type = " + mimeType); - return; + return Intents.RESULT_SMS_HANDLED; } } index += pduDecoder.getDecodedDataLength(); @@ -167,6 +172,7 @@ public class WapPushOverSms { if (dispatchedByApplication == false) { dispatchWapPdu_default(pdu, transactionId, pduType, mimeType, dataIndex); } + return Activity.RESULT_OK; } private void dispatchWapPdu_default( diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java index 9aa7eab..3362de8 100755 --- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java +++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java @@ -19,6 +19,7 @@ package com.android.internal.telephony.cdma; import android.app.ActivityManagerNative; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.os.AsyncResult; import android.os.Handler; import android.os.Looper; @@ -26,6 +27,7 @@ import android.os.Message; import android.os.Registrant; import android.os.RegistrantList; import android.os.SystemProperties; +import android.preference.PreferenceManager; import android.provider.Settings; import android.telephony.CellLocation; import android.telephony.PhoneNumberUtils; @@ -40,6 +42,7 @@ import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.Connection; import com.android.internal.telephony.DataConnection; import com.android.internal.telephony.IccCard; +import com.android.internal.telephony.IccException; import com.android.internal.telephony.IccFileHandler; import com.android.internal.telephony.IccPhoneBookInterfaceManager; import com.android.internal.telephony.IccSmsInterfaceManager; @@ -65,6 +68,9 @@ public class CDMAPhone extends PhoneBase { // Default Emergency Callback Mode exit timer private static final int DEFAULT_ECM_EXIT_TIMER_VALUE = 30000; + static final String VM_COUNT_CDMA = "vm_count_key_cdma"; + private static final String VM_NUMBER_CDMA = "vm_number_key_cdma"; + private String mVmNumber = null; //***** Instance Variables CdmaCallTracker mCT; @@ -147,6 +153,9 @@ public class CDMAPhone extends PhoneBase { // This is needed to handle phone process crashes String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false"); mIsPhoneInECMState = inEcm.equals("true"); + + // Notify voicemails. + notifier.notifyMessageWaitingChanged(this); } public void dispose() { @@ -300,7 +309,7 @@ public class CDMAPhone extends PhoneBase { public boolean getMessageWaitingIndicator() { - return mRuimRecords.getVoiceMessageWaiting(); + return (getVoiceMessageCount() > 0); } public List<? extends MmiCode> @@ -678,22 +687,33 @@ public class CDMAPhone extends PhoneBase { public void setVoiceMailNumber(String alphaTag, String voiceMailNumber, Message onComplete) { - //mSIMRecords.setVoiceMailNumber(alphaTag, voiceMailNumber, onComplete); - //TODO: Where do we have to store this value has to be clarified with QC + Message resp; + mVmNumber = voiceMailNumber; + resp = h.obtainMessage(EVENT_SET_VM_NUMBER_DONE, 0, 0, onComplete); + mRuimRecords.setVoiceMailNumber(alphaTag, mVmNumber, resp); } public String getVoiceMailNumber() { - //TODO: Where can we get this value has to be clarified with QC - //return mSIMRecords.getVoiceMailNumber(); -// throw new RuntimeException(); - return "*86"; + String number = null; + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); + // TODO(Moto): The default value of voicemail number should be read from a system property + number = sp.getString(VM_NUMBER_CDMA, "*86"); + return number; } /* Returns Number of Voicemails * @hide */ - public int getCountVoiceMessages() { - return mRuimRecords.getCountVoiceMessages(); + public int getVoiceMessageCount() { + int voicemailCount = mRuimRecords.getVoiceMessageCount(); + // If mRuimRecords.getVoiceMessageCount returns zero, then there is possibility + // that phone was power cycled and would have lost the voicemail count. + // So get the count from preferences. + if (voicemailCount == 0) { + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); + voicemailCount = sp.getInt(VM_COUNT_CDMA, 0); + } + return voicemailCount; } public String getVoiceMailAlphaTag() { @@ -820,9 +840,10 @@ public class CDMAPhone extends PhoneBase { mRuimRecords.setVoiceMessageWaiting(1, mwi ? -1 : 0); } - public void - notifyMessageWaitingIndicator() { - mNotifier.notifyMessageWaitingChanged(this); + /* This function is overloaded to send number of voicemails instead of sending true/false */ + /*package*/ void + updateMessageWaitingIndicator(int mwi) { + mRuimRecords.setVoiceMessageWaiting(1, mwi); } /** @@ -984,6 +1005,19 @@ public class CDMAPhone extends PhoneBase { } break; + case EVENT_SET_VM_NUMBER_DONE:{ + ar = (AsyncResult)msg.obj; + if (IccException.class.isInstance(ar.exception)) { + storeVoiceMailNumber(mVmNumber); + ar.exception = null; + } + onComplete = (Message) ar.userObj; + if (onComplete != null) { + AsyncResult.forMessage(onComplete, ar.result, ar.exception); + onComplete.sendToTarget(); + } + } + default:{ throw new RuntimeException("unexpected event not handled"); } @@ -1198,4 +1232,16 @@ public class CDMAPhone extends PhoneBase { int defRoamInd = getServiceState().getCdmaDefaultRoamingIndicator(); return mEriManager.getCdmaEriText(roamInd, defRoamInd); } + + /** + * Store the voicemail number in preferences + */ + private void storeVoiceMailNumber(String number) { + // Update the preference value of voicemail number + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext()); + SharedPreferences.Editor editor = sp.edit(); + editor.putString(VM_NUMBER_CDMA, number); + editor.commit(); + } + } diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java index da9fd0c..ed2ea90 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java @@ -253,11 +253,7 @@ public final class CdmaCallTracker extends CallTracker { // Always unmute when answering a new call setMute(false); cm.acceptCall(obtainCompleteMessage()); - } else if ((foregroundCall.connections.size() > 0) && - (ringingCall.getState() == CdmaCall.State.WAITING)) { - // TODO(Moto): jsh asks, "Is this check necessary? I don't think it should be - // possible to have no fg connection and a WAITING call, but if we should hit - // this situation, is a CallStateExcetion appropriate?" + } else if (ringingCall.getState() == CdmaCall.State.WAITING) { CdmaConnection cwConn = (CdmaConnection)(ringingCall.getLatestConnection()); // Since there is no network response for supplimentary @@ -530,10 +526,6 @@ public final class CdmaCallTracker extends CallTracker { CdmaConnection cn = (CdmaConnection)foregroundCall.connections.get(n); droppedDuringPoll.add(cn); } - // TODO(Moto): jsh asks, "Are we sure we don't need to do this for - // ringingCall as well? What if there's a WAITING call (ie, UI timer - // hasn't expired, moving it to DISCONNECTED)? How/when will it - // transition to DISCONNECTED?" } foregroundCall.setGeneric(false); // Dropped connections are removed from the CallTracker @@ -681,8 +673,12 @@ public final class CdmaCallTracker extends CallTracker { // set the ringing call state to IDLE here to avoid a race condition // where a new call waiting could get a hang up from an old call // waiting ringingCall. - // TODO(Moto): jsh asks, "Should we call conn.ondisconnect() here or Somewhere?" - ringingCall.detach(conn); + // + // PhoneApp does the call log itself since only PhoneApp knows + // the hangup reason is user ignoring or timing out. So conn.onDisconnect() + // is not called here. Instead, conn.onLocalDisconnect() is called. + conn.onLocalDisconnect(); + phone.notifyCallStateChanged(); return; } else { try { diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java b/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java index 588bdf0..025382d 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java @@ -452,12 +452,7 @@ public class CdmaConnection extends Connection { this.cause = cause; if (!disconnected) { - index = -1; - - disconnectTime = System.currentTimeMillis(); - duration = SystemClock.elapsedRealtime() - connectTimeReal; - disconnected = true; - + doDisconnect(); if (Config.LOGD) Log.d(LOG_TAG, "[CDMAConn] onDisconnect: cause=" + cause); @@ -470,6 +465,21 @@ public class CdmaConnection extends Connection { releaseWakeLock(); } + /** Called when the call waiting connection has been hung up */ + /*package*/ void + onLocalDisconnect() { + if (!disconnected) { + doDisconnect(); + if (Config.LOGD) Log.d(LOG_TAG, + "[CDMAConn] onLoalDisconnect" ); + + if (parent != null) { + parent.detach(this); + } + } + releaseWakeLock(); + } + // Returns true if state has changed, false if nothing changed /*package*/ boolean update (DriverCall dc) { @@ -587,6 +597,14 @@ public class CdmaConnection extends Connection { } private void + doDisconnect() { + index = -1; + disconnectTime = System.currentTimeMillis(); + duration = SystemClock.elapsedRealtime() - connectTimeReal; + disconnected = true; + } + + private void onStartedHolding() { holdingStartTime = SystemClock.elapsedRealtime(); } diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java index 2b4a700..ecdc8f6 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java @@ -20,11 +20,14 @@ package com.android.internal.telephony.cdma; import android.app.Activity; import android.app.PendingIntent; import android.content.ContentValues; +import android.content.SharedPreferences; import android.database.Cursor; import android.database.SQLException; import android.os.AsyncResult; import android.os.Message; +import android.provider.Telephony; import android.provider.Telephony.Sms.Intents; +import android.preference.PreferenceManager; import android.util.Config; import android.util.Log; @@ -64,17 +67,12 @@ final class CdmaSMSDispatcher extends SMSDispatcher { Log.d(TAG, "handleStatusReport is a special GSM function, should never be called in CDMA!"); } - /** - * Dispatches an incoming SMS messages. - * - * @param smsb the incoming message from the phone - */ - protected void dispatchMessage(SmsMessageBase smsb) { + /** {@inheritDoc} */ + protected int dispatchMessage(SmsMessageBase smsb) { // If sms is null, means there was a parsing error. - // TODO: Should NAK this. if (smsb == null) { - return; + return Intents.RESULT_SMS_GENERIC_ERROR; } // Decode BD stream and set sms variables. @@ -83,27 +81,6 @@ final class CdmaSMSDispatcher extends SMSDispatcher { int teleService = sms.getTeleService(); boolean handled = false; - // Teleservices W(E)MT and VMN are handled together: - if ((teleService == SmsEnvelope.TELESERVICE_WMT) - || (teleService == SmsEnvelope.TELESERVICE_WEMT) - || (teleService == SmsEnvelope.TELESERVICE_VMN)) { - // From here on we need decoded BD. - // Special case the message waiting indicator messages - if (sms.isMWISetMessage()) { - mCdmaPhone.updateMessageWaitingIndicator(true); - handled |= sms.isMwiDontStore(); - if (Config.LOGD) { - Log.d(TAG, "Received voice mail indicator set SMS shouldStore=" + !handled); - } - } else if (sms.isMWIClearMessage()) { - mCdmaPhone.updateMessageWaitingIndicator(false); - handled |= sms.isMwiDontStore(); - if (Config.LOGD) { - Log.d(TAG, "Received voice mail indicator clear SMS shouldStore=" + !handled); - } - } - } - if (sms.getUserData() == null) { if (Config.LOGD) { Log.d(TAG, "Received SMS without user data"); @@ -111,11 +88,25 @@ final class CdmaSMSDispatcher extends SMSDispatcher { handled = true; } - if (handled) return; + if (handled) { + return Intents.RESULT_SMS_HANDLED; + } if (SmsEnvelope.TELESERVICE_WAP == teleService){ - processCdmaWapPdu(sms.getUserData(), sms.messageRef, sms.getOriginatingAddress()); - return; + return processCdmaWapPdu(sms.getUserData(), sms.messageRef, + sms.getOriginatingAddress()); + } else if (SmsEnvelope.TELESERVICE_VMN == teleService) { + // handling Voicemail + int voicemailCount = sms.getNumOfVoicemails(); + Log.d(TAG, "Voicemail count=" + voicemailCount); + // Store the voicemail count in preferences. + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences( + ((CDMAPhone) mPhone).getContext()); + SharedPreferences.Editor editor = sp.edit(); + editor.putInt(CDMAPhone.VM_COUNT_CDMA, voicemailCount); + editor.commit(); + ((CDMAPhone) mPhone).updateMessageWaitingIndicator(voicemailCount); + return Intents.RESULT_SMS_HANDLED; } /** @@ -145,17 +136,19 @@ final class CdmaSMSDispatcher extends SMSDispatcher { if (smsHeader != null && smsHeader.portAddrs != null) { if (smsHeader.portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) { // GSM-style WAP indication - mWapPush.dispatchWapPdu(sms.getUserData()); + return mWapPush.dispatchWapPdu(sms.getUserData()); + } else { + // The message was sent to a port, so concoct a URI for it. + dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort); } - // The message was sent to a port, so concoct a URI for it. - dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort); } else { // Normal short and non-port-addressed message, dispatch it. dispatchPdus(pdus); } + return Activity.RESULT_OK; } else { // Process the message part. - processMessagePart(sms, smsHeader.concatRef, smsHeader.portAddrs); + return processMessagePart(sms, smsHeader.concatRef, smsHeader.portAddrs); } } @@ -165,29 +158,35 @@ final class CdmaSMSDispatcher extends SMSDispatcher { * WDP segments are gathered until a datagram completes and gets dispatched. * * @param pdu The WAP-WDP PDU segment + * @return a result code from {@link Telephony.Sms.Intents}, or + * {@link Activity#RESULT_OK} if the message has been broadcast + * to applications */ - protected void processCdmaWapPdu(byte[] pdu, int referenceNumber, String address) { + protected int processCdmaWapPdu(byte[] pdu, int referenceNumber, String address) { int segment; int totalSegments; int index = 0; int msgType; - int sourcePort; - int destinationPort; + int sourcePort = 0; + int destinationPort = 0; msgType = pdu[index++]; if (msgType != 0){ Log.w(TAG, "Received a WAP SMS which is not WDP. Discard."); - return; + return Intents.RESULT_SMS_HANDLED; } totalSegments = pdu[index++]; // >=1 segment = pdu[index++]; // >=0 - //process WDP segment - sourcePort = (0xFF & pdu[index++]) << 8; - sourcePort |= 0xFF & pdu[index++]; - destinationPort = (0xFF & pdu[index++]) << 8; - destinationPort |= 0xFF & pdu[index++]; + // Only the first segment contains sourcePort and destination Port + if (segment == 0) { + //process WDP segment + sourcePort = (0xFF & pdu[index++]) << 8; + sourcePort |= 0xFF & pdu[index++]; + destinationPort = (0xFF & pdu[index++]) << 8; + destinationPort |= 0xFF & pdu[index++]; + } // Lookup all other related parts StringBuilder where = new StringBuilder("reference_number ="); @@ -217,7 +216,7 @@ final class CdmaSMSDispatcher extends SMSDispatcher { mResolver.insert(mRawUri, values); - return; + return Intents.RESULT_SMS_HANDLED; } // All the parts are in place, deal with them @@ -228,6 +227,11 @@ final class CdmaSMSDispatcher extends SMSDispatcher { for (int i = 0; i < cursorCount; i++) { cursor.moveToNext(); int cursorSequence = (int)cursor.getLong(sequenceColumn); + // Read the destination port from the first segment + if (cursorSequence == 0) { + int destinationPortColumn = cursor.getColumnIndex("destination_port"); + destinationPort = (int)cursor.getLong(destinationPortColumn); + } pdus[cursorSequence] = HexDump.hexStringToByteArray( cursor.getString(pduColumn)); } @@ -237,7 +241,7 @@ final class CdmaSMSDispatcher extends SMSDispatcher { mResolver.delete(mRawUri, where.toString(), whereArgs); } catch (SQLException e) { Log.e(TAG, "Can't access multipart SMS database", e); - return; // TODO: NACK the message or something, don't just discard. + return Intents.RESULT_SMS_GENERIC_ERROR; } finally { if (cursor != null) cursor.close(); } @@ -257,15 +261,14 @@ final class CdmaSMSDispatcher extends SMSDispatcher { switch (destinationPort) { case SmsHeader.PORT_WAP_PUSH: // Handle the PUSH - mWapPush.dispatchWapPdu(datagram); - break; + return mWapPush.dispatchWapPdu(datagram); default:{ pdus = new byte[1][]; pdus[0] = datagram; // The messages were sent to any other WAP port dispatchPortAddressedPdus(pdus, destinationPort); - break; + return Activity.RESULT_OK; } } } diff --git a/telephony/java/com/android/internal/telephony/cdma/SignalToneUtil.java b/telephony/java/com/android/internal/telephony/cdma/SignalToneUtil.java index 925a755..44958e9 100644 --- a/telephony/java/com/android/internal/telephony/cdma/SignalToneUtil.java +++ b/telephony/java/com/android/internal/telephony/cdma/SignalToneUtil.java @@ -22,6 +22,9 @@ import android.util.Log; import android.media.ToneGenerator; public class SignalToneUtil { + /** A marker that isn't a valid TONE */ + public static final int CDMA_INVALID_TONE = -1; + // public final int int IS95_CONST_IR_SIGNAL_TYPE_TYPE; static public final int IS95_CONST_IR_SIGNAL_TONE = 0; static public final int IS95_CONST_IR_SIGNAL_ISDN = 1; @@ -76,7 +79,7 @@ public class SignalToneUtil { private static Integer signalParamHash(int signalType, int alertPitch, int signal) { if ((signalType < 0) || (signalType > 256) || (alertPitch > 256) || (alertPitch < 0) || (signal > 256) || (signal < 0)) { - return new Integer(ToneGenerator.TONE_CDMA_INVALID); + return new Integer(CDMA_INVALID_TONE); } return new Integer(signalType * 256 * 256 + alertPitch * 256 + signal); } @@ -84,7 +87,7 @@ public class SignalToneUtil { public static int getAudioToneFromSignalInfo(int signalType, int alertPitch, int signal) { Integer result = hm.get(signalParamHash(signalType, alertPitch, signal)); if (result == null) { - return ToneGenerator.TONE_CDMA_INVALID; + return CDMA_INVALID_TONE; } return result; } @@ -100,13 +103,13 @@ public class SignalToneUtil { ToneGenerator.TONE_CDMA_CALL_SIGNAL_ISDN_INTERGROUP); hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_ISDN, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, - IS95_CONST_IR_SIG_ISDN_SP_PRI), ToneGenerator.TONE_CDMA_CALL_SIGNAL_SP_PRI); + IS95_CONST_IR_SIG_ISDN_SP_PRI), ToneGenerator.TONE_CDMA_CALL_SIGNAL_ISDN_SP_PRI); hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_ISDN, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, IS95_CONST_IR_SIG_ISDN_PAT_3), ToneGenerator.TONE_CDMA_CALL_SIGNAL_ISDN_PAT3); hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_ISDN, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, - IS95_CONST_IR_SIG_ISDN_PING), ToneGenerator.TONE_CDMA_CALL_SIGNAL_ISDN_RING_RING); + IS95_CONST_IR_SIG_ISDN_PING), ToneGenerator.TONE_CDMA_CALL_SIGNAL_ISDN_PING_RING); hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_ISDN, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, IS95_CONST_IR_SIG_ISDN_PAT_5), ToneGenerator.TONE_CDMA_CALL_SIGNAL_ISDN_PAT5); @@ -163,7 +166,7 @@ public class SignalToneUtil { IS95_CONST_IR_SIG_IS54B_L), ToneGenerator.TONE_CDMA_HIGH_L); hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_MED, - IS95_CONST_IR_SIG_IS54B_L), ToneGenerator.TONE_CDMA_INVALID); + IS95_CONST_IR_SIG_IS54B_L), ToneGenerator.TONE_CDMA_MED_L); hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_LOW, IS95_CONST_IR_SIG_IS54B_L), ToneGenerator.TONE_CDMA_LOW_L); diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java index 63d2c47..3a92064 100644 --- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java @@ -773,6 +773,12 @@ public class SmsMessage extends SmsMessageBase { return asciiDigit; } + /** This function shall be called to get the number of voicemails. + * @hide + */ + /*package*/ int getNumOfVoicemails() { + return mBearerData.numberOfMessages; + } } diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java index 70d71fc..d1e4b4f 100755 --- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java +++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java @@ -448,11 +448,6 @@ public class GSMPhone extends PhoneBase { } public void - notifyMessageWaitingIndicator() { - mNotifier.notifyMessageWaitingChanged(this); - } - - public void notifyCallForwardingIndicator() { mNotifier.notifyCallForwardingChanged(this); } diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java index f87392a..2770ddc 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java @@ -78,24 +78,16 @@ final class GsmSMSDispatcher extends SMSDispatcher { } } } - - if (mCm != null) { - mCm.acknowledgeLastIncomingGsmSms(true, Intents.RESULT_SMS_HANDLED, null); - } + acknowledgeLastIncomingSms(true, Intents.RESULT_SMS_HANDLED, null); } - /** - * Dispatches an incoming SMS messages. - * - * @param sms the incoming message from the phone - */ - protected void dispatchMessage(SmsMessageBase smsb) { + /** {@inheritDoc} */ + protected int dispatchMessage(SmsMessageBase smsb) { // If sms is null, means there was a parsing error. - // TODO: Should NAK this. if (smsb == null) { - return; + return Intents.RESULT_SMS_GENERIC_ERROR; } SmsMessage sms = (SmsMessage) smsb; boolean handled = false; @@ -115,7 +107,9 @@ final class GsmSMSDispatcher extends SMSDispatcher { } } - if (handled) return; + if (handled) { + return Intents.RESULT_SMS_HANDLED; + } SmsHeader smsHeader = sms.getUserDataHeader(); // See if message is partial or port addressed. @@ -126,17 +120,19 @@ final class GsmSMSDispatcher extends SMSDispatcher { if (smsHeader != null && smsHeader.portAddrs != null) { if (smsHeader.portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) { - mWapPush.dispatchWapPdu(sms.getUserData()); + return mWapPush.dispatchWapPdu(sms.getUserData()); + } else { + // The message was sent to a port, so concoct a URI for it. + dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort); } - // The message was sent to a port, so concoct a URI for it. - dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort); } else { // Normal short and non-port-addressed message, dispatch it. dispatchPdus(pdus); } + return Activity.RESULT_OK; } else { // Process the message part. - processMessagePart(sms, smsHeader.concatRef, smsHeader.portAddrs); + return processMessagePart(sms, smsHeader.concatRef, smsHeader.portAddrs); } } diff --git a/tests/DpiTest/AndroidManifest.xml b/tests/DpiTest/AndroidManifest.xml index f71cff2..64ad7be 100644 --- a/tests/DpiTest/AndroidManifest.xml +++ b/tests/DpiTest/AndroidManifest.xml @@ -16,6 +16,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.google.android.test.dpi"> + <uses-sdk android:minSdkVersion="3" android:targetSdkVersion="3" /> + <supports-screens android:smallScreens="true" /> <application android:label="DpiTest"> <activity android:name="DpiTestActivity" android:label="DpiTest"> <intent-filter> diff --git a/tests/DpiTest/res/values-largeScreen/strings.xml b/tests/DpiTest/res/values-largeScreen/strings.xml new file mode 100644 index 0000000..f4dd543 --- /dev/null +++ b/tests/DpiTest/res/values-largeScreen/strings.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<resources> + <string name="act_title">DpiTest: Large Screen</string> +</resources> diff --git a/tests/DpiTest/res/values-normalScreen/strings.xml b/tests/DpiTest/res/values-normalScreen/strings.xml new file mode 100644 index 0000000..256d696 --- /dev/null +++ b/tests/DpiTest/res/values-normalScreen/strings.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<resources> + <string name="act_title">DpiTest: Normal Screen</string> +</resources> diff --git a/tests/DpiTest/res/values-smallScreen/strings.xml b/tests/DpiTest/res/values-smallScreen/strings.xml new file mode 100644 index 0000000..cdb4ac9 --- /dev/null +++ b/tests/DpiTest/res/values-smallScreen/strings.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<resources> + <string name="act_title">DpiTest: Small Screen</string> +</resources> diff --git a/tests/DpiTest/res/values/strings.xml b/tests/DpiTest/res/values/strings.xml new file mode 100644 index 0000000..ef924ac --- /dev/null +++ b/tests/DpiTest/res/values/strings.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<resources> + <string name="act_title">DpiTest: Unknown Screen</string> +</resources> diff --git a/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java b/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java index 3759622..5a9f3f5 100644 --- a/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java +++ b/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java @@ -34,6 +34,7 @@ public class DpiTestActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + this.setTitle(R.string.act_title); LinearLayout root = new LinearLayout(this); root.setOrientation(LinearLayout.VERTICAL); diff --git a/tests/backup/src/com/android/backuptest/BackupTestAgent.java b/tests/backup/src/com/android/backuptest/BackupTestAgent.java index c6acc66..8e4fd39 100644 --- a/tests/backup/src/com/android/backuptest/BackupTestAgent.java +++ b/tests/backup/src/com/android/backuptest/BackupTestAgent.java @@ -18,11 +18,15 @@ package com.android.backuptest; import android.backup.BackupHelperAgent; import android.backup.FileBackupHelper; +import android.backup.SharedPreferencesBackupHelper; public class BackupTestAgent extends BackupHelperAgent { public void onCreate() { addHelper("data_files", new FileBackupHelper(this, BackupTestActivity.FILE_NAME)); + addHelper("more_data_files", new FileBackupHelper(this, "another_file.txt", "3.txt", + "empty.txt")); + addHelper("shared_prefs", new SharedPreferencesBackupHelper(this, "settings", "raw")); } } diff --git a/tests/backup/test_backup.sh b/tests/backup/test_backup.sh new file mode 100755 index 0000000..dbf9ed2 --- /dev/null +++ b/tests/backup/test_backup.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +#adb kill-server + +# set the transport +adb shell bmgr transport 1 + +# load up the three files +adb shell "rm /data/data/com.android.backuptest/files/* ; \ + mkdir /data/data/com.android.backuptest ; \ + mkdir /data/data/com.android.backuptest/files ; \ + mkdir /data/data/com.android.backuptest/shared_prefs ; \ + echo -n \"<map><int name=\\\"pref\\\" value=\\\"1\\\" /></map>\" > /data/data/com.android.backuptest/shared_prefs/raw.xml ; \ + echo -n first file > /data/data/com.android.backuptest/files/file.txt ; \ + echo -n asdf > /data/data/com.android.backuptest/files/another_file.txt ; \ + echo -n 3 > /data/data/com.android.backuptest/files/3.txt ; \ + echo -n "" > /data/data/com.android.backuptest/files/empty.txt ; \ +" + +# say that the data has changed +adb shell bmgr backup com.android.backuptest + +# run the backup +adb shell bmgr run + + + diff --git a/tests/backup/test_restore.sh b/tests/backup/test_restore.sh new file mode 100755 index 0000000..ccf29cf --- /dev/null +++ b/tests/backup/test_restore.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +function check_file +{ + data=$(adb shell cat /data/data/com.android.backuptest/$1) + if [ "$data" = "$2" ] ; then + echo "$1 has correct value [$2]" + else + echo $1 is INCORRECT + echo " value: [$data]" + echo " expected: [$2]" + fi +} + +# delete the old data +echo --- Previous files +adb shell "ls -l /data/data/com.android.backuptest/files" +adb shell "rm /data/data/com.android.backuptest/files/*" +echo --- Previous shared_prefs +adb shell "ls -l /data/data/com.android.backuptest/shared_prefs" +adb shell "rm /data/data/com.android.backuptest/shared_prefs/*" +echo --- Erased files and shared_prefs +adb shell "ls -l /data/data/com.android.backuptest/files" +adb shell "ls -l /data/data/com.android.backuptest/shared_prefs" +echo --- + +echo +echo +echo + +# run the restore +adb shell bmgr restore 0 + +echo +echo +echo + +# check the results +check_file files/file.txt "first file" +check_file files/another_file.txt "asdf" +check_file files/3.txt "3" +check_file files/empty.txt "" +check_file shared_prefs/raw.xml '<map><int name="pref" value="1" /></map>' + +echo +echo +echo +echo --- Restored files +adb shell "ls -l /data/data/com.android.backuptest/files" +echo --- Restored shared_prefs +adb shell "ls -l /data/data/com.android.backuptest/shared_prefs" +echo --- +echo diff --git a/tests/permission/src/com/android/framework/permission/tests/HardwareServicePermissionTest.java b/tests/permission/src/com/android/framework/permission/tests/HardwareServicePermissionTest.java index dc6860a..719e758 100644 --- a/tests/permission/src/com/android/framework/permission/tests/HardwareServicePermissionTest.java +++ b/tests/permission/src/com/android/framework/permission/tests/HardwareServicePermissionTest.java @@ -22,10 +22,12 @@ import android.os.Binder; import android.os.IHardwareService; import android.os.RemoteException; import android.os.ServiceManager; +import android.test.suitebuilder.annotation.SmallTest; /** * Verify that Hardware apis cannot be called without required permissions. */ +@SmallTest public class HardwareServicePermissionTest extends TestCase { private IHardwareService mHardwareService; diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp index 6bc1ee6..67af116 100644 --- a/tools/aapt/AaptAssets.cpp +++ b/tools/aapt/AaptAssets.cpp @@ -187,6 +187,13 @@ AaptGroupEntry::parseNamePart(const String8& part, int* axis, uint32_t* value) return 0; } + // screen layout + if (getScreenLayoutName(part.string(), &config)) { + *axis = AXIS_SCREENLAYOUT; + *value = config.screenLayout; + return 0; + } + // version if (getVersionName(part.string(), &config)) { *axis = AXIS_VERSION; @@ -202,7 +209,7 @@ AaptGroupEntry::initFromDirName(const char* dir, String8* resType) { Vector<String8> parts; - String8 mcc, mnc, loc, orient, den, touch, key, keysHidden, nav, size, vers; + String8 mcc, mnc, loc, orient, den, touch, key, keysHidden, nav, size, layout, vers; const char *p = dir; const char *q; @@ -378,6 +385,18 @@ AaptGroupEntry::initFromDirName(const char* dir, String8* resType) //printf("not screen size: %s\n", part.string()); } + if (getScreenLayoutName(part.string())) { + layout = part; + + index++; + if (index == N) { + goto success; + } + part = parts[index]; + } else { + //printf("not screen layout: %s\n", part.string()); + } + if (getVersionName(part.string())) { vers = part; @@ -404,6 +423,7 @@ success: this->keyboard = key; this->navigation = nav; this->screenSize = size; + this->screenLayout = layout; this->version = vers; // what is this anyway? @@ -435,6 +455,8 @@ AaptGroupEntry::toString() const s += ","; s += screenSize; s += ","; + s += screenLayout; + s += ","; s += version; return s; } @@ -483,6 +505,10 @@ AaptGroupEntry::toDirName(const String8& resType) const s += "-"; s += screenSize; } + if (this->screenLayout != "") { + s += "-"; + s += screenLayout; + } if (this->version != "") { s += "-"; s += version; @@ -786,6 +812,26 @@ bool AaptGroupEntry::getScreenSizeName(const char* name, return true; } +bool AaptGroupEntry::getScreenLayoutName(const char* name, + ResTable_config* out) +{ + if (strcmp(name, kWildcardName) == 0) { + if (out) out->screenLayout = out->SCREENLAYOUT_ANY; + return true; + } else if (strcmp(name, "smallscreen") == 0) { + if (out) out->screenLayout = out->SCREENLAYOUT_SMALL; + return true; + } else if (strcmp(name, "normalscreen") == 0) { + if (out) out->screenLayout = out->SCREENLAYOUT_NORMAL; + return true; + } else if (strcmp(name, "largescreen") == 0) { + if (out) out->screenLayout = out->SCREENLAYOUT_LARGE; + return true; + } + + return false; +} + bool AaptGroupEntry::getVersionName(const char* name, ResTable_config* out) { @@ -828,6 +874,7 @@ int AaptGroupEntry::compare(const AaptGroupEntry& o) const if (v == 0) v = keyboard.compare(o.keyboard); if (v == 0) v = navigation.compare(o.navigation); if (v == 0) v = screenSize.compare(o.screenSize); + if (v == 0) v = screenLayout.compare(o.screenLayout); if (v == 0) v = version.compare(o.version); return v; } @@ -846,6 +893,7 @@ ResTable_config AaptGroupEntry::toParams() const getKeyboardName(keyboard.string(), ¶ms); getNavigationName(navigation.string(), ¶ms); getScreenSizeName(screenSize.string(), ¶ms); + getScreenLayoutName(screenLayout.string(), ¶ms); getVersionName(version.string(), ¶ms); return params; } diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h index 01c8140..3b96412 100644 --- a/tools/aapt/AaptAssets.h +++ b/tools/aapt/AaptAssets.h @@ -37,6 +37,7 @@ enum { AXIS_KEYBOARD, AXIS_NAVIGATION, AXIS_SCREENSIZE, + AXIS_SCREENLAYOUT, AXIS_VERSION }; @@ -62,6 +63,7 @@ public: String8 keyboard; String8 navigation; String8 screenSize; + String8 screenLayout; String8 version; bool initFromDirName(const char* dir, String8* resType); @@ -78,6 +80,7 @@ public: static bool getKeyboardName(const char* name, ResTable_config* out = NULL); static bool getNavigationName(const char* name, ResTable_config* out = NULL); static bool getScreenSizeName(const char* name, ResTable_config* out = NULL); + static bool getScreenLayoutName(const char* name, ResTable_config* out = NULL); static bool getVersionName(const char* name, ResTable_config* out = NULL); int compare(const AaptGroupEntry& o) const; diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index 503f661..e04491d 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -329,6 +329,9 @@ enum { TARGET_SDK_VERSION_ATTR = 0x01010270, TEST_ONLY_ATTR = 0x01010272, DENSITY_ATTR = 0x0101026c, + SMALL_SCREEN_ATTR = 0x01010284, + NORMAL_SCREEN_ATTR = 0x01010285, + LARGE_SCREEN_ATTR = 0x01010286, }; const char *getComponentName(String8 &pkgName, String8 &componentName) { @@ -499,6 +502,10 @@ int doDump(Bundle* bundle) bool isLauncherActivity = false; bool withinApplication = false; bool withinReceiver = false; + int targetSdk = 0; + int smallScreen = 1; + int normalScreen = 1; + int largeScreen = 1; String8 pkg; String8 activityName; String8 activityLabel; @@ -572,8 +579,10 @@ int doDump(Bundle* bundle) error.string()); goto bail; } + if (name == "Donut") targetSdk = 4; printf("sdkVersion:'%s'\n", name.string()); } else if (code != -1) { + targetSdk = code; printf("sdkVersion:'%d'\n", code); } code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error); @@ -585,8 +594,12 @@ int doDump(Bundle* bundle) error.string()); goto bail; } + if (name == "Donut" && targetSdk < 4) targetSdk = 4; printf("targetSdkVersion:'%s'\n", name.string()); } else if (code != -1) { + if (targetSdk < code) { + targetSdk = code; + } printf("targetSdkVersion:'%d'\n", code); } } else if (tag == "uses-configuration") { @@ -625,6 +638,13 @@ int doDump(Bundle* bundle) goto bail; } printf("supports-density:'%d'\n", dens); + } else if (tag == "supports-screens") { + smallScreen = getIntegerAttribute(tree, + SMALL_SCREEN_ATTR, NULL, 1); + normalScreen = getIntegerAttribute(tree, + NORMAL_SCREEN_ATTR, NULL, 1); + largeScreen = getIntegerAttribute(tree, + LARGE_SCREEN_ATTR, NULL, 1); } } else if (depth == 3 && withinApplication) { withinActivity = false; @@ -733,6 +753,25 @@ int doDump(Bundle* bundle) } } + // Determine default values for any unspecified screen sizes, + // based on the target SDK of the package. As of 4 (donut) + // the screen size support was introduced, so all default to + // enabled. + if (smallScreen > 0) { + smallScreen = targetSdk >= 4 ? -1 : 0; + } + if (normalScreen > 0) { + normalScreen = -1; + } + if (largeScreen > 0) { + largeScreen = targetSdk >= 4 ? -1 : 0; + } + printf("supports-screens:"); + if (smallScreen != 0) printf(" 'small'"); + if (normalScreen != 0) printf(" 'normal'"); + if (largeScreen != 0) printf(" 'large'"); + printf("\n"); + printf("locales:"); Vector<String8> locales; res.getLocales(&locales); diff --git a/tools/aidl/AST.h b/tools/aidl/AST.h index 1dedd04..aec2164 100755 --- a/tools/aidl/AST.h +++ b/tools/aidl/AST.h @@ -5,6 +5,7 @@ #include <vector> #include <set> #include <stdarg.h> +#include <stdio.h> using namespace std; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java index 1fa11af..06dd96f 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java @@ -59,7 +59,7 @@ public class BridgeAssetManager extends AssetManager { public void setConfiguration(int mcc, int mnc, String locale, int orientation, int touchscreen, int density, int keyboard, int keyboardHidden, int navigation, int screenWidth, int screenHeight, - int version) { + int screenLayout, int version) { Configuration c = new Configuration(); c.mcc = mcc; @@ -70,5 +70,6 @@ public class BridgeAssetManager extends AssetManager { c.keyboardHidden = keyboardHidden; c.navigation = navigation; c.orientation = orientation; + c.screenLayout = screenLayout; } } diff --git a/tools/localize/Perforce.cpp b/tools/localize/Perforce.cpp index 3425668..1c644ed 100644 --- a/tools/localize/Perforce.cpp +++ b/tools/localize/Perforce.cpp @@ -1,6 +1,7 @@ #include "Perforce.h" #include "log.h" #include <string.h> +#include <cstdio> #include <stdlib.h> #include <sstream> #include <sys/types.h> diff --git a/tools/localize/SourcePos.cpp b/tools/localize/SourcePos.cpp index 9d7c5c6..2533f0a 100644 --- a/tools/localize/SourcePos.cpp +++ b/tools/localize/SourcePos.cpp @@ -1,6 +1,7 @@ #include "SourcePos.h" #include <stdarg.h> +#include <cstdio> #include <set> using namespace std; diff --git a/tools/localize/Values.cpp b/tools/localize/Values.cpp index e396f8b..8623b97 100644 --- a/tools/localize/Values.cpp +++ b/tools/localize/Values.cpp @@ -1,5 +1,6 @@ #include "Values.h" #include <stdlib.h> +#include <cstdio> // ===================================================================================== diff --git a/tools/localize/XLIFFFile.cpp b/tools/localize/XLIFFFile.cpp index 51f81de..4e217d9 100644 --- a/tools/localize/XLIFFFile.cpp +++ b/tools/localize/XLIFFFile.cpp @@ -3,6 +3,7 @@ #include <algorithm> #include <sys/time.h> #include <time.h> +#include <cstdio> const char* const XLIFF_XMLNS = "urn:oasis:names:tc:xliff:document:1.2"; diff --git a/tools/localize/file_utils.cpp b/tools/localize/file_utils.cpp index 8792b9e..293e50e 100644 --- a/tools/localize/file_utils.cpp +++ b/tools/localize/file_utils.cpp @@ -1,4 +1,5 @@ #include <string.h> +#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include "file_utils.h" diff --git a/tools/localize/localize_test.cpp b/tools/localize/localize_test.cpp index 63d904c..678cad8 100644 --- a/tools/localize/localize_test.cpp +++ b/tools/localize/localize_test.cpp @@ -1,6 +1,7 @@ #include "XLIFFFile.h" #include "ValuesFile.h" #include "localize.h" +#include <stdio.h> int pseudolocalize_xliff(XLIFFFile* xliff, bool expand); diff --git a/tools/localize/merge_res_and_xliff.cpp b/tools/localize/merge_res_and_xliff.cpp index 58a6554..1fdaa0e 100644 --- a/tools/localize/merge_res_and_xliff.cpp +++ b/tools/localize/merge_res_and_xliff.cpp @@ -3,6 +3,7 @@ #include "file_utils.h" #include "Perforce.h" #include "log.h" +#include <stdio.h> static set<StringResource>::const_iterator find_id(const set<StringResource>& s, const string& id, int index) diff --git a/tools/localize/merge_res_and_xliff_test.cpp b/tools/localize/merge_res_and_xliff_test.cpp index 5a2b0f4..e4ab562 100644 --- a/tools/localize/merge_res_and_xliff_test.cpp +++ b/tools/localize/merge_res_and_xliff_test.cpp @@ -1,5 +1,5 @@ #include "merge_res_and_xliff.h" - +#include <stdio.h> int merge_test() diff --git a/tools/localize/xmb.cpp b/tools/localize/xmb.cpp index 236705f..d8f6ff0 100644 --- a/tools/localize/xmb.cpp +++ b/tools/localize/xmb.cpp @@ -7,6 +7,7 @@ #include "XLIFFFile.h" #include <map> +#include <cstdio> using namespace std; diff --git a/vpn/java/android/net/vpn/IVpnService.aidl b/vpn/java/android/net/vpn/IVpnService.aidl index 0e658df..fedccb0 100644 --- a/vpn/java/android/net/vpn/IVpnService.aidl +++ b/vpn/java/android/net/vpn/IVpnService.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, The Android Open Source Project + * 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. diff --git a/vpn/java/android/net/vpn/L2tpIpsecProfile.java b/vpn/java/android/net/vpn/L2tpIpsecProfile.java index 181619d..4ae2dec 100644 --- a/vpn/java/android/net/vpn/L2tpIpsecProfile.java +++ b/vpn/java/android/net/vpn/L2tpIpsecProfile.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, The Android Open Source Project + * 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. @@ -19,15 +19,14 @@ package android.net.vpn; import android.os.Parcel; /** - * The profile for L2TP-over-IPSec type of VPN. + * The profile for certificate-based L2TP-over-IPSec type of VPN. * {@hide} */ -public class L2tpIpsecProfile extends VpnProfile { +public class L2tpIpsecProfile extends L2tpProfile { private static final long serialVersionUID = 1L; private String mUserCertificate; private String mCaCertificate; - private String mUserkey; @Override public VpnType getType() { @@ -50,20 +49,11 @@ public class L2tpIpsecProfile extends VpnProfile { return mUserCertificate; } - public void setUserkey(String name) { - mUserkey = name; - } - - public String getUserkey() { - return mUserkey; - } - @Override protected void readFromParcel(Parcel in) { super.readFromParcel(in); mCaCertificate = in.readString(); mUserCertificate = in.readString(); - mUserkey = in.readString(); } @Override @@ -71,6 +61,5 @@ public class L2tpIpsecProfile extends VpnProfile { super.writeToParcel(parcel, flags); parcel.writeString(mCaCertificate); parcel.writeString(mUserCertificate); - parcel.writeString(mUserkey); } } diff --git a/vpn/java/android/net/vpn/L2tpIpsecPskProfile.java b/vpn/java/android/net/vpn/L2tpIpsecPskProfile.java new file mode 100644 index 0000000..7a03018 --- /dev/null +++ b/vpn/java/android/net/vpn/L2tpIpsecPskProfile.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.vpn; + +import android.os.Parcel; + +/** + * The profile for pre-shared-key-based L2TP-over-IPSec type of VPN. + * {@hide} + */ +public class L2tpIpsecPskProfile extends L2tpProfile { + private static final long serialVersionUID = 1L; + + private String mPresharedKey; + + @Override + public VpnType getType() { + return VpnType.L2TP_IPSEC_PSK; + } + + public void setPresharedKey(String key) { + mPresharedKey = key; + } + + public String getPresharedKey() { + return mPresharedKey; + } + + @Override + protected void readFromParcel(Parcel in) { + super.readFromParcel(in); + mPresharedKey = in.readString(); + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + super.writeToParcel(parcel, flags); + parcel.writeString(mPresharedKey); + } +} diff --git a/vpn/java/android/net/vpn/L2tpProfile.java b/vpn/java/android/net/vpn/L2tpProfile.java index 59d4981..dbba0c5 100644 --- a/vpn/java/android/net/vpn/L2tpProfile.java +++ b/vpn/java/android/net/vpn/L2tpProfile.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, The Android Open Source Project + * 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. @@ -16,6 +16,8 @@ package android.net.vpn; +import android.os.Parcel; + /** * The profile for L2TP type of VPN. * {@hide} @@ -23,8 +25,44 @@ package android.net.vpn; public class L2tpProfile extends VpnProfile { private static final long serialVersionUID = 1L; + private boolean mSecret; + private String mSecretString; + @Override public VpnType getType() { return VpnType.L2TP; } + + /** + * Enables/disables the secret for authenticating tunnel connection. + */ + public void setSecretEnabled(boolean enabled) { + mSecret = enabled; + } + + public boolean isSecretEnabled() { + return mSecret; + } + + public void setSecretString(String secret) { + mSecretString = secret; + } + + public String getSecretString() { + return mSecretString; + } + + @Override + protected void readFromParcel(Parcel in) { + super.readFromParcel(in); + mSecret = in.readInt() > 0; + mSecretString = in.readString(); + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + super.writeToParcel(parcel, flags); + parcel.writeInt(mSecret ? 1 : 0); + parcel.writeString(mSecretString); + } } diff --git a/vpn/java/android/net/vpn/PptpProfile.java b/vpn/java/android/net/vpn/PptpProfile.java new file mode 100644 index 0000000..c68bb71 --- /dev/null +++ b/vpn/java/android/net/vpn/PptpProfile.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.vpn; + +/** + * The profile for PPTP type of VPN. + * {@hide} + */ +public class PptpProfile extends VpnProfile { + private static final long serialVersionUID = 1L; + + @Override + public VpnType getType() { + return VpnType.PPTP; + } +} diff --git a/vpn/java/android/net/vpn/VpnManager.java b/vpn/java/android/net/vpn/VpnManager.java index 98795bd..dc70b26 100644 --- a/vpn/java/android/net/vpn/VpnManager.java +++ b/vpn/java/android/net/vpn/VpnManager.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, The Android Open Source Project + * 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. diff --git a/vpn/java/android/net/vpn/VpnProfile.aidl b/vpn/java/android/net/vpn/VpnProfile.aidl index ad34bfc..edeaef0 100644 --- a/vpn/java/android/net/vpn/VpnProfile.aidl +++ b/vpn/java/android/net/vpn/VpnProfile.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, The Android Open Source Project + * 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. diff --git a/vpn/java/android/net/vpn/VpnProfile.java b/vpn/java/android/net/vpn/VpnProfile.java index 9e24da4..bd6c809 100644 --- a/vpn/java/android/net/vpn/VpnProfile.java +++ b/vpn/java/android/net/vpn/VpnProfile.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, The Android Open Source Project + * 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. diff --git a/vpn/java/android/net/vpn/VpnState.java b/vpn/java/android/net/vpn/VpnState.java index 977d938..ebd9364 100644 --- a/vpn/java/android/net/vpn/VpnState.java +++ b/vpn/java/android/net/vpn/VpnState.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, The Android Open Source Project + * 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. diff --git a/vpn/java/android/net/vpn/VpnType.java b/vpn/java/android/net/vpn/VpnType.java index 91b0ea2..c7df943 100644 --- a/vpn/java/android/net/vpn/VpnType.java +++ b/vpn/java/android/net/vpn/VpnType.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, The Android Open Source Project + * 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. @@ -21,14 +21,21 @@ package android.net.vpn; * {@hide} */ public enum VpnType { - L2TP_IPSEC("L2TP/IPSec", L2tpIpsecProfile.class), - L2TP("L2TP", L2tpProfile.class); + PPTP("PPTP", "", PptpProfile.class), + L2TP("L2TP", "", L2tpProfile.class), + L2TP_IPSEC_PSK("L2TP/IPSec PSK", "Pre-shared key based L2TP/IPSec VPN", + L2tpIpsecPskProfile.class), + L2TP_IPSEC("L2TP/IPSec CRT", "Certificate based L2TP/IPSec VPN", + L2tpIpsecProfile.class); private String mDisplayName; + private String mDescription; private Class<? extends VpnProfile> mClass; - VpnType(String displayName, Class<? extends VpnProfile> klass) { + VpnType(String displayName, String description, + Class<? extends VpnProfile> klass) { mDisplayName = displayName; + mDescription = description; mClass = klass; } @@ -36,6 +43,10 @@ public enum VpnType { return mDisplayName; } + public String getDescription() { + return mDescription; + } + public Class<? extends VpnProfile> getProfileClass() { return mClass; } |
